diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..539ba93 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..f2e61df --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,24 @@ +# .github/release.yml +--- + +changelog: + exclude: + labels: + - ignore-for-release + categories: + - title: 🚀 NouveautĂ©s + labels: + - feature + - enhancement + - title: 🐛 Correctifs + labels: + - bug + - fix + - title: 🔧 Infrastructure & DĂ©pendances + labels: + - ops + - chore + - dependencies + - title: 📚 Documentation + labels: + - documentation \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..d7935e4 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,66 @@ +name: 🐋 Dockline + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 #v7 + with: + node-version: '26' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build static site + run: npm run build + + - name: Log in to the Container registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f #v4.6.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 #v6.2.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + # Tag semver complet : v1.2.3 + type=semver,pattern={{version}} + # Majeur.mineur : v1.2 (reçoit automatiquement les patchs) + type=semver,pattern={{major}}.{{minor}} + # Majeur seul : v1 (point d'entrĂ©e stable pour FluxCD) + type=semver,pattern={{major}} + # Latest : toujours sur le dernier tag + type=raw,value=latest + + - name: Build and push Docker image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a #v7.3.0 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6240da8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c5ff429 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,22 @@ +## Development + +When starting the dev server, use background mode: + +``` +astro dev --background +``` + +Manage the background server with `astro dev stop`, `astro dev status`, and `astro dev logs`. + +## Documentation + +Full documentation: https://docs.astro.build + +Consult these guides before working on related tasks: + +- [Adding pages, dynamic routes, or middleware](https://docs.astro.build/en/guides/routing/) +- [Working with Astro components](https://docs.astro.build/en/basics/astro-components/) +- [Using React, Vue, Svelte, or other framework components](https://docs.astro.build/en/guides/framework-components/) +- [Adding or managing content](https://docs.astro.build/en/guides/content-collections/) +- [Adding styles or using Tailwind](https://docs.astro.build/en/guides/styling/) +- [Supporting multiple languages](https://docs.astro.build/en/guides/internationalization/) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e8ac748 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM nginx:1.30.4-alpine-slim + +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY dist/ /usr/share/nginx/html/ + +RUN touch /var/run/nginx.pid && \ + chown -R 101:101 /var/cache/nginx /var/log/nginx /var/run/nginx.pid /usr/share/nginx/html + +USER 101 + +EXPOSE 8080 \ No newline at end of file diff --git a/README-astro.md b/README-astro.md new file mode 100644 index 0000000..1b7f5c3 --- /dev/null +++ b/README-astro.md @@ -0,0 +1,49 @@ +# Starlight Starter Kit: Basics + +[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) + +``` +npm create astro@latest -- --template starlight +``` + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro + Starlight project, you'll see the following folders and files: + +``` +. +├── public/ +├── src/ +│ ├── assets/ +│ ├── content/ +│ │ └── docs/ +│ └── content.config.ts +├── astro.config.mjs +├── package.json +└── tsconfig.json +``` + +Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. + +Images can be added to `src/assets/` and embedded in Markdown with a relative link. + +Static assets, like favicons, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). diff --git a/README.md b/README.md index bfd9991..27507a7 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,12 @@ -# Template `README.md` pour les dĂ©pĂŽts d’infrastructure +# DĂ©veloppement - Loutik Docs -## Prompt IA - -Tu es un ingĂ©nieur SRE senior garant du respect des bonnes pratiques de l’industrie. Ta mission est de rĂ©diger le fichier README.md du projet en restant concis, clair et professionnel dans tes explications pour un dĂ©pĂŽt de dĂ©veloppement (application, bot, API...). - -Ci-dessous se trouve un template Markdown du README.md. Les commentaires entre crochets [] sont des instructions destinĂ©es Ă  ton persona et ne doivent jamais apparaĂźtre dans le rĂ©sultat final. - -Ta rĂ©ponse doit contenir uniquement le rĂ©sultat final, sans texte supplĂ©mentaire ni explication. - -Informations Ă  prendre en compte : - -* [INSÉRER LES INFORMATIONS] - -````markdown -# DĂ©veloppement - - -![BanniĂšre Loutik](https://raw.githubusercontent.com/loutik/design-assets/main/banniere_loutik.png) +![BanniĂšre Loutik](https://raw.githubusercontent.com/loutik/design-assets/main/loutikdocs/banniere_loutikdocs.png) ## Contexte -[PrĂ©senter le contexte du dĂ©pĂŽt, les objectifs du projet ainsi que l'objectif de l'outil dĂ©velopper.] +Ce dĂ©pĂŽt contient le site de documentation Loutik, conçu pour partager mon infrastructure homelab, les composants techniques mis en place, les notions apprises au fil de mes expĂ©riences, ainsi que les projets rĂ©alisĂ©s dans le cadre du BTS SIO. + +L’objectif est de centraliser la documentation technique sous une interface claire et facilement navigable, afin de prĂ©senter les architectures, les choix de conception, les bonnes pratiques et les retours d’expĂ©rience liĂ©s au systĂšme d’information et Ă  l’automatisation. --- @@ -28,44 +15,99 @@ Informations Ă  prendre en compte : L’organisation du dĂ©pĂŽt suit la logique suivante : ```text -[GĂ©nĂ©ration de l’arborescence du projet avec les dossiers et fichiers importants] +. +├── public/ # Fichiers statiques servis par le site +├── src/ +│ ├── assets/ # Ressources visuelles et mĂ©dias +│ ├── components/ # Composants Astro rĂ©utilisables +│ ├── content/ +│ │ ├── docs/ # Contenu documentaire du site +│ │ └── config.ts # Configuration des collections de contenu +│ └── ... +├── templates/ # ModĂšles de rĂ©daction pour les contenus +├── .github/ # Configuration GitHub et workflows +├── astro.config.mjs # Configuration principal du projet Astro +├── docker-compose.yml # Services Docker du projet +├── Dockerfile # Image de conteneur du site +├── nginx.conf # Configuration NGINX pour la publication +├── package.json # DĂ©pendances et scripts npm +├── package-lock.json # Verrouillage des dĂ©pendances +├── tsconfig.json # Configuration TypeScript +├── README.md # Documentation du dĂ©pĂŽt +├── README-astro.md # Documentation Astro de rĂ©fĂ©rence +├── LICENSE.md # Licence du projet +└── public/ # Fichiers publics du site ``` -- **`[/]`** : [Description de l’utilitĂ© du dossier] -- **`[/]`** : [Description de l’utilitĂ© du fichier] +- **`src/content/docs/`** : Contient la documentation, les articles, les pages et les ressources associĂ©es au site. +- **`src/components/`** : Regroupe les composants Astro utilisĂ©s pour la structure et le design du site. +- **`public/`** : Contient les assets statiques exposĂ©s directement par le serveur web. +- **`templates/`** : Fournit des modĂšles pour la rĂ©daction de nouveaux contenus et de pages structurĂ©es. +- **`astro.config.mjs`** : ParamĂštre le moteur Astro ainsi que ses intĂ©grations et plugins. +- **`package.json`** : DĂ©clare les scripts et les dĂ©pendances du projet. --- -## Utilisation de [nom] +## Utilisation du projet ### 1. Cloner le dĂ©pĂŽt localement ```bash -git clone [URL du dĂ©pĂŽt] -cd [Nom du dĂ©pĂŽt] +git clone https://github.com/loutik/website-docs.git +cd loutik_website-docs +``` + +### 2. Installer les dĂ©pendances + +```bash +npm install ``` -### 2. [Action Ă  rĂ©aliser] +### 3. Lancer le site en mode dĂ©veloppement -[Description de l’action] +```bash +npm run dev +``` + +Le site est ensuite accessible par dĂ©faut sur l’URL suivante : + +```text +http://localhost:4321 +``` + +### 4. VĂ©rifier le rendu du projet + +Pour gĂ©nĂ©rer la version de production et valider le site avant publication : ```bash -[Exemple de commande] +npm run build +npm run preview ``` -### 3. [Action suivante] +Cette commande permet de vĂ©rifier le rendu final produit par Astro avant un dĂ©ploiement ou une publication. + +### 5. Publier des modifications via une branche et une pull request + +```bash +git checkout -b feature/mon-correctif +git add . +git commit -m "Ajout de la documentation ou correction" +git push -u origin feature/mon-correctif +``` -[Ajouter autant d’étapes que nĂ©cessaire] +Ensuite, ouvrez une pull request sur GitHub pour valider les changements, demander une revue et fusionner la branche dans la branche principale lorsqu’elle est prĂȘte. --- ## Bonnes pratiques -1. **[Nom de la bonne pratique]** : [Description] -2. **[Nom de la bonne pratique]** : [Description] +1. **Valider le build avant merge** : ExĂ©cuter `npm run build` pour s’assurer que le site compile correctement et que les pages sont gĂ©nĂ©rĂ©es sans erreur. +2. **Conserver une structure documentaire claire** : Organiser les pages par thĂ©matiques de maniĂšre cohĂ©rente (homelab, notions, projets, blog, etc.). +3. **SĂ©parer contenu et composants** : Conserver les fichiers de contenu dans `src/content/docs/` et les Ă©lĂ©ments rĂ©utilisables dans `src/components/`. +4. **Optimiser les assets** : Utiliser les ressources statiques et les images dans des dossiers dĂ©diĂ©s pour Ă©viter la confusion et faciliter la maintenance. ```bash -[Commande Ă  exĂ©cuter si nĂ©cessaire] +npm run build ``` --- @@ -78,6 +120,5 @@ cd [Nom du dĂ©pĂŽt]

-DerniĂšre mise Ă  jour : [jour mois annĂ©e — Exemple : 15 avril 2026] -
-```` \ No newline at end of file +DerniĂšre mise Ă  jour : 16 aoĂ»t 2026 + \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..cc43bee --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,68 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import starlight from '@astrojs/starlight'; +import starlightBlog from 'starlight-blog' +import sitemap from '@astrojs/sitemap'; +import mermaid from 'astro-mermaid'; + +// https://astro.build/config +export default defineConfig({ + site: 'https://docs.loutik.fr', + integrations: [ + mermaid({ + autoTheme: true, // Aligne automatiquement la couleur du graphique sur le mode clair/sombre de Starlight + }), + starlight({ + title: 'LoutikDOCS ', + lastUpdated: true, + components: { + Footer: './src/components/Footer.astro', + SiteTitle: './src/components/SiteTitle.astro', + }, + head: [ + { + tag: 'meta', + attrs: { property: 'og:image', content: 'https://docs.loutik.fr/loutikdocs-social-card.png' } + }, + ], + social: [ + { icon: 'linkedin', label: 'LinkInd', href: 'https://www.linkedin.com/in/louismedo/'}, + { icon: 'discord', label: 'Discord', href: 'https://discord.loutik.fr'}, + { icon: 'youtube', label: 'YouTube', href: 'https://www.youtube.com/@FireToak'}, + { icon: 'github', label: 'GitHub', href: 'https://github.com/FireToak' }, + ], + sidebar: [ + { + label: 'Bienvenue 👋', + slug: 'introduction/bienvenue' + }, + { + label: '🏠 Homelab', + collapsed: true, + items: [{ autogenerate: { directory: 'homelab', collapsed: true } }], + }, + { + label: '📚 Notions', + collapsed: true, + items: [{ autogenerate: { directory: 'notions', collapsed: true } }], + }, + { + label: '📖 Projets BTS SIO', + collapsed: true, + items: [{ autogenerate: { directory: 'projets-bts-sio', collapsed: true } }], + }, + ], + plugins: [ + starlightBlog({ + title: 'Blog', + authors: { + louismedo: { + name: 'Louis MEDO', + title: 'Étudiant BTS SIO / Administration systĂšme', + picture: 'https://github.com/FireToak.png', + }, + } + }), + ], + }), sitemap()], +}); \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c37a8d6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +--- +# Local development environment for a PHP web application +services: + # ================================= + # Live view + # ================================= + # nginx: + # image: nginx:1.31.3-alpine-slim + # container_name: dev-loutikdocs + # ports: + # - 8080:80 + # volumes: + # - ./dist:/usr/share/nginx/html + + # ================================== + # Dev view + # ================================== + # nginx: + # image: dev-loutikdocs:1.0.3 + # container_name: dev-loutikdocs + # ports: + # - 8080:8080 + + # =================================== + # Prod view + # =================================== + nginx: + image: ghcr.io/loutik/website-docs:latest + container_name: prd-loutikdocs + ports: + - 8080:8080 \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..9ce214a --- /dev/null +++ b/nginx.conf @@ -0,0 +1,39 @@ +server { + listen 8080; + listen [::]:8080; + server_name _; + + # RĂ©pertoire des fichiers html + root /usr/share/nginx/html; + index index.html; + + # Masque de la version de NGINX + server_tokens off; + + # Redirection favicon + location = /favicon.ico { + try_files /favicon.ico =404; + log_not_found off; + access_log off; + } + + # Redirection index + if ($request_uri ~ "^/index(\.html)?$") { + return 301 /; + } + + # Masque des extensions .html + location / { + try_files $uri $uri.html $uri/ =404; + } + + # Erreur 404 + error_page 404 /404.html; + + location = /404.html { + internal; + } + + # Trailing Slash - DĂ©sactivĂ© + #rewrite ^/(.*)/$ /$1 permanent; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7f0106c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7847 @@ +{ + "name": "docs", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "docs", + "version": "0.0.1", + "dependencies": { + "@astrojs/sitemap": "^3.7.3", + "@astrojs/starlight": "^0.41.7", + "astro": "^7.1.6", + "astro-mermaid": "^2.1.0", + "mermaid": "^11.16.1", + "sharp": "^0.35.3", + "starlight-blog": "^0.28.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.2.tgz", + "integrity": "sha512-8w/9CWmYrAJJ8N0SY3O43ws2BgxoW6u3QsD8u2mE140lMYAlwh+tlNoUeSBq22wVheFuiBbR212l6ixZ2IIgCQ==", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.2", + "@astrojs/compiler-binding-darwin-x64": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.2", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.2", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.2", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.2", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-MM8tn8CSimcfytaOla4b6acN8mKWiL/rlAA1fpT3/Wl7dNGSE4y8FjTN/zJVNnb63CsLWG5zZwCt01TXtDKh9g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.2.tgz", + "integrity": "sha512-2lXOlzf8xb7jLomRsf/aswh61/NnGusynB2OwFkK6k4pmOtpfXMYnG0PLfXrEvxXYj69NdCnmUYXtHDd+JOOag==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-BmU3kWj7qnLrd4vzm49zFEPJ5oFnn1tCT4Vt9hZbqdU5Cmb8GZl7fn6VFsnNfe7B18a2gIFtVzbLINtYl5kBjQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-f0heT9ZZEseSu5bHCeb80eL2DH07ArE6U9xi1WT/PEusNjzPmEr3GJsjG1tRLo5VYUUYX7h3ScaqGmGrMOVGmw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-M8fOUt0itRpqiGyoEA/ij184s8O+hqbCz3+YozRusOOM3osgGljpDThhbKAJjqh82wOo6FioQ4w8PBvU1XMD5Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-/Kebk8sO6HnLeSd691JkaAPfN7CqR9/KEXmWvyNPkaKNGmj8rTZ/lf2uXtnPu92Lan84UrKdIKVPy1fSo2encQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.2.tgz", + "integrity": "sha512-pUA6xbcOSB7DhfzIArB8BCAkFfAIqriiR7zl5zOStd6oU2G0kIKj+GUdGnyXyhfiv881Hffyk5tC0mR18sDjDw==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-ESruf+6Qkl1trHUFxI6GSf6t52j8yN2kCNSzMWdzt7V/T09tFHrYzrVaJQohb2C9bJUH76pNvX6Zb51+xCQc9Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-wzzVrEbOwbsLWOdEbocskjMRx2aZPxJ7ZbmL+jnpBamFwmigm+2M/wzuM6JWncocgYwLic1csSpalBh96kQKXA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.2.tgz", + "integrity": "sha512-xlx/T7JovIKduu4ucbTQUxQ5+Q8wxkHxhLjnZk3VlJbhbQ9RLvvuDk1p2YYFYFQ5y14dVm3FGGO4isQXa4F+Tg==", + "dependencies": { + "@astrojs/compiler-binding": "0.3.2" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.2.tgz", + "integrity": "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ==", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.3.0", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } + }, + "node_modules/@astrojs/markdown-remark": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.2.2.tgz", + "integrity": "sha512-FGfmK84zSNcrsBd0dl1gXE9JvZYElp8EXQa2jpHVAxG4deGKAp43wspxFupjADJX7MSsMRHwYCnfT6EyVmgeFQ==", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.1.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.5.tgz", + "integrity": "sha512-CvWVEFAbay7YO+i9SaqDJubipA5ckiVB89QWoMJ5XC0m5CtFg8JwZ7Kau6X9sYY7FZURH0w2l03ISH2jOS/RDQ==", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "satteri": "^0.9.1" + } + }, + "node_modules/@astrojs/mdx": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-7.0.5.tgz", + "integrity": "sha512-wEM/HH1RiEntyPVagdiF+yArzfcYLKBB0C1RZspVidKZ97rRMbaqP1Nbl/GR0sJs8zwaceqxRymw8aOKKJRdYw==", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/markdown-remark": "7.2.2", + "@mdx-js/mdx": "^3.1.1", + "acorn": "^8.16.0", + "es-module-lexer": "^2.0.0", + "estree-util-visit": "^2.0.0", + "hast-util-to-html": "^9.0.5", + "piccolore": "^0.1.3", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-smartypants": "^3.0.2", + "source-map": "^0.7.6", + "unist-util-visit": "^5.1.0", + "vfile": "^6.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/markdown-satteri": "^0.3.1", + "astro": "^7.0.0" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-satteri": { + "optional": true + } + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/rss": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.19.tgz", + "integrity": "sha512-e+z5wYeYtffQdHQO8c2tkSd2JEBdAuRXJV4ZEU5IxkYeE6e39woDd7nw1PH1Kk2tEYNCYuKdylnnbhGmt61awA==", + "dependencies": { + "fast-xml-parser": "^5.5.7", + "piccolore": "^0.1.3", + "zod": "^4.3.6" + } + }, + "node_modules/@astrojs/sitemap": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.3.tgz", + "integrity": "sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==", + "dependencies": { + "sitemap": "^9.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^4.3.6" + } + }, + "node_modules/@astrojs/starlight": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.41.7.tgz", + "integrity": "sha512-579VJuZgo20UpNQPm9EIez5W3DFSrD16uiV2YX6rUlpLtjgKSdnc69TxVTZXn4AtI2B731TI2qhW1O3K+vwtrQ==", + "dependencies": { + "@astrojs/markdown-satteri": "^0.3.5", + "@astrojs/mdx": "^7.0.5", + "@astrojs/sitemap": "^3.7.3", + "@pagefind/default-ui": "^1.3.0", + "@types/hast": "^3.0.4", + "@types/js-yaml": "^4.0.9", + "@types/mdast": "^4.0.4", + "astro-expressive-code": "^0.44.0", + "bcp-47": "^2.1.0", + "hast-util-from-html": "^2.0.3", + "hast-util-select": "^6.0.4", + "hast-util-to-string": "^3.0.1", + "hastscript": "^9.0.1", + "i18next": "^26.0.7", + "js-yaml": "^4.1.1", + "klona": "^2.0.6", + "magic-string": "^0.30.21", + "mdast-util-directive": "^3.1.0", + "mdast-util-to-markdown": "^2.1.2", + "mdast-util-to-string": "^4.0.0", + "pagefind": "^1.5.2", + "rehype": "^13.0.2", + "rehype-format": "^5.0.1", + "remark-directive": "^4.0.0", + "satteri": "^0.9.1", + "ultrahtml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-visit": "^5.1.0", + "vfile": "^6.0.3" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "^7.2.0", + "astro": "^7.0.2" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", + "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==" + }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", + "integrity": "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", + "integrity": "sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", + "integrity": "sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", + "integrity": "sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", + "integrity": "sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", + "integrity": "sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", + "integrity": "sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", + "integrity": "sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", + "integrity": "sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==" + }, + "node_modules/@clack/core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", + "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", + "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", + "dependencies": { + "@clack/core": "1.4.3", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@expressive-code/core": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.44.1.tgz", + "integrity": "sha512-3dDo9N8D7hYrLNNMMWFovg3+aDUtnQm7c7z0GZc1c0LEFVBc0Q6lKG+tVT28gDadOvsgOANfCn35fgpe97Pmgg==", + "dependencies": { + "@ctrl/tinycolor": "^4.0.4", + "hast-util-select": "^6.0.2", + "hast-util-to-html": "^9.0.1", + "hast-util-to-text": "^4.0.1", + "hastscript": "^9.0.0", + "postcss": "^8.4.38", + "postcss-nested": "^6.0.1", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1" + } + }, + "node_modules/@expressive-code/plugin-frames": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.44.1.tgz", + "integrity": "sha512-HC/bdRao9225ApcgO/e3jn8ZOhldKO7ob1O/Tcipvtv7Vb5nMphZhMtD9uuywpvxkPYBHJi3504WhrKg05Dwqg==", + "dependencies": { + "@expressive-code/core": "^0.44.1" + } + }, + "node_modules/@expressive-code/plugin-shiki": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.44.1.tgz", + "integrity": "sha512-YApiZt3buUzBwL5tqj8G+sYC5NjMjRCHgQwr9bmGl69rtcHy6fE9dooWUeKYB978fJT2BuxT5FeHcF47rA3SEg==", + "dependencies": { + "@expressive-code/core": "^0.44.1", + "shiki": "^4.0.2" + } + }, + "node_modules/@expressive-code/plugin-text-markers": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.44.1.tgz", + "integrity": "sha512-B3BsJoJ8CFMlcIX9f+X9tcI3C4zPDO601+YuLi9GheSTNro7ZfqSjLptMQKBHOWZvxnAtY5zvIX7iO/qtBhNBg==", + "dependencies": { + "@expressive-code/core": "^0.44.1" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, + "node_modules/@iconify/utils": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.0.tgz", + "integrity": "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==", + "dependencies": { + "@chevrotain/types": "~11.1.2" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@nodable/entities": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", + "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ] + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==" + }, + "node_modules/@oxc-project/types": { + "version": "0.144.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz", + "integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pagefind/darwin-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz", + "integrity": "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/darwin-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz", + "integrity": "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/default-ui": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.5.2.tgz", + "integrity": "sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==" + }, + "node_modules/@pagefind/freebsd-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz", + "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@pagefind/linux-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz", + "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/linux-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz", + "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/windows-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz", + "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pagefind/windows-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz", + "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", + "integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz", + "integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz", + "integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz", + "integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz", + "integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz", + "integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz", + "integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz", + "integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz", + "integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz", + "integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz", + "integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz", + "integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz", + "integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==" + }, + "node_modules/@shikijs/core": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.4.3.tgz", + "integrity": "sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==", + "dependencies": { + "@shikijs/primitive": "4.4.3", + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.4.3.tgz", + "integrity": "sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==", + "dependencies": { + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.3.tgz", + "integrity": "sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==", + "dependencies": { + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.4.3.tgz", + "integrity": "sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==", + "dependencies": { + "@shikijs/types": "4.4.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.4.3.tgz", + "integrity": "sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==", + "dependencies": { + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.4.3.tgz", + "integrity": "sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==", + "dependencies": { + "@shikijs/types": "4.4.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.4.3.tgz", + "integrity": "sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz", + "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==" + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz", + "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==" + }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/astro": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.2.1.tgz", + "integrity": "sha512-ynyMTiyF//GLcd8+gRNbvcKgS1ht+qTgp/fkkHIpGYNILLseQqQRuPZ9PqXYKfsact4J1emZaXRDpg2bl35Pag==", + "dependencies": { + "@astrojs/compiler-rs": "^0.3.2", + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/markdown-satteri": "0.3.5", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "am-i-vibing": "^0.4.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^2.0.1", + "devalue": "^5.8.1", + "diff": "^8.0.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.3.0", + "jsonc-parser": "^3.3.1", + "magic-string": "^1.0.0", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^1.0.1", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "semver": "^7.7.4", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unstorage": "^1.17.5", + "vite": "^8.0.13", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0 || ^0.35.0" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "7.2.2" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/astro-expressive-code": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.44.1.tgz", + "integrity": "sha512-DT1LnCqbHasBKlvzJ3m6LR4VI94wwx3W9EV/YbP1te4rqjOHsvsezHYuqb5MeLWLftXms/1FA9QBbwCo43DnJQ==", + "dependencies": { + "rehype-expressive-code": "^0.44.1", + "url-extras": "^0.1.0" + }, + "peerDependencies": { + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta || ^7.0.0" + } + }, + "node_modules/astro-mermaid": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/astro-mermaid/-/astro-mermaid-2.1.0.tgz", + "integrity": "sha512-fFRUN0BTZh+DZhDiLyblXoO26XqJ1Rr+qK3JGgSu7OBspKHDm59jkztg/aHsrdo1vO/tIq/+xhP/vgT8Mp92XA==", + "dependencies": { + "import-meta-resolve": "^4.2.0", + "mdast-util-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "peerDependencies": { + "@mermaid-js/layout-elk": "^0.2.0", + "astro": ">=4", + "mermaid": "^10.0.0 || ^11.0.0" + }, + "peerDependenciesMeta": { + "@mermaid-js/layout-elk": { + "optional": true + } + } + }, + "node_modules/astro/node_modules/magic-string": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.0.tgz", + "integrity": "sha512-ptco+HFxTLgjafSLim2LojBSwfg5feBjd+SqyiwdGkzC38UPdZy3zgrHMI2CoTf5fJL38tbHMYWVzIH8BxGqJw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.1.tgz", + "integrity": "sha512-KLw+H/gd2p4zly1X7Yh/qziuyae5/w/QFnvTng9eZL5fvszL7Whl3MBoWF8yxL7ksUjBfOD+OxkytiqbBpG+Fw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-selector-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz", + "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + }, + "node_modules/cytoscape": { + "version": "3.34.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.1.tgz", + "integrity": "sha512-Lr0RvH9H75y9ar8h9Toy6u4lxRSCcxUq+hHcQ26sVWo6BnaQp1gwEZOYqwuYTZhyW7npyKnNLP8oJ2p1/3OZ7g==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==" + }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.0.tgz", + "integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz", + "integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==" + }, + "node_modules/es-toolkit": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.50.0.tgz", + "integrity": "sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==" + }, + "node_modules/expressive-code": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.44.1.tgz", + "integrity": "sha512-GakidxhapWDzpKLqEaFQ8wGk6gAqEtPQibu8+yPBfnDLgev5Vdsh1pasTxnrXL/mzIknyqeTwhMHTghdaiUrTg==", + "dependencies": { + "@expressive-code/core": "^0.44.1", + "@expressive-code/plugin-frames": "^0.44.1", + "@expressive-code/plugin-shiki": "^0.44.1", + "@expressive-code/plugin-text-markers": "^0.44.1" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fast-xml-builder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz", + "integrity": "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", + "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "@nodable/entities": "^3.0.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.1", + "xml-naming": "^0.3.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==" + }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", + "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "html-whitespace-sensitive-tag-names": "^3.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-select": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", + "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "nth-check": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-whitespace-sensitive-tag-names": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", + "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==" + }, + "node_modules/i18next": { + "version": "26.3.6", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.6.tgz", + "integrity": "sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "peerDependencies": { + "typescript": "^5 || ^6 || ^7" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unsafe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz", + "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" + }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", + "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", + "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==" + }, + "node_modules/mermaid": { + "version": "11.16.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.16.1.tgz", + "integrity": "sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g==", + "dependencies": { + "@braintree/sanitize-url": "^7.1.2", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.2.0", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.20", + "dompurify": "^3.3.3", + "es-toolkit": "^1.45.1", + "katex": "^0.16.45", + "khroma": "^2.1.0", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz", + "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==" + }, + "node_modules/node-mock-http": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", + "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz", + "integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", + "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==" + }, + "node_modules/pagefind": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.2.tgz", + "integrity": "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==", + "bin": { + "pagefind": "lib/runner/bin.cjs" + }, + "optionalDependencies": { + "@pagefind/darwin-arm64": "1.5.2", + "@pagefind/darwin-x64": "1.5.2", + "@pagefind/freebsd-x64": "1.5.2", + "@pagefind/linux-arm64": "1.5.2", + "@pagefind/linux-x64": "1.5.2", + "@pagefind/windows-arm64": "1.5.2", + "@pagefind/windows-x64": "1.5.2" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==" + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==" + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==" + }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-expressive-code": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.44.1.tgz", + "integrity": "sha512-+VZgs7Evw4LXRN3owpoBNSTpYuW6GeOdjqcUT1TuY8o/4MGPtbd0EU7Bgrju7X8KrQ6SslOBAuGWJ5fV5TriJQ==", + "dependencies": { + "expressive-code": "^0.44.1" + } + }, + "node_modules/rehype-format": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", + "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-format": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-4.0.0.tgz", + "integrity": "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.3.tgz", + "integrity": "sha512-gCaK+ndZ0hYezlqFegHFCVh2CQemsi0Npdh1qVM9bxlUFknjkbP6VmojWhddOCrbK0PbbacmYLWfTULRiT1eWA==", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==" + }, + "node_modules/rolldown": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz", + "integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==", + "dependencies": { + "@oxc-project/types": "=0.144.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.4", + "@rolldown/binding-darwin-arm64": "1.2.4", + "@rolldown/binding-darwin-x64": "1.2.4", + "@rolldown/binding-freebsd-x64": "1.2.4", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", + "@rolldown/binding-linux-arm64-gnu": "1.2.4", + "@rolldown/binding-linux-arm64-musl": "1.2.4", + "@rolldown/binding-linux-ppc64-gnu": "1.2.4", + "@rolldown/binding-linux-s390x-gnu": "1.2.4", + "@rolldown/binding-linux-x64-gnu": "1.2.4", + "@rolldown/binding-linux-x64-musl": "1.2.4", + "@rolldown/binding-openharmony-arm64": "1.2.4", + "@rolldown/binding-win32-arm64-msvc": "1.2.4", + "@rolldown/binding-win32-x64-msvc": "1.2.4" + } + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/satteri": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.5.tgz", + "integrity": "sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.5", + "@bruits/satteri-darwin-x64": "0.9.5", + "@bruits/satteri-linux-arm64-gnu": "0.9.5", + "@bruits/satteri-linux-arm64-musl": "0.9.5", + "@bruits/satteri-linux-x64-gnu": "0.9.5", + "@bruits/satteri-linux-x64-musl": "0.9.5", + "@bruits/satteri-wasm32-wasi": "0.9.5", + "@bruits/satteri-win32-arm64-msvc": "0.9.5", + "@bruits/satteri-win32-x64-msvc": "0.9.5" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/schema-dts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-2.0.0.tgz", + "integrity": "sha512-t7NoCy3Rn5GHGx6p7s1qIYK/AeIb8ZxJNR9WUNFkwMv2CiiGZBmqqYWc2FlZVm5ZbiHMY4OvBWhj7QtyrFO2Jw==", + "dependencies": { + "schema-dts-lib": "^1.0.0" + } + }, + "node_modules/schema-dts-lib": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-dts-lib/-/schema-dts-lib-1.0.0.tgz", + "integrity": "sha512-9MEO5vpQH9JdBioUupqluzxSYxPLjhmqRUudk15adUl/ypnRsM2/M1kN3AmVJQeG7nZqcL68H8JlGqQQT6vy9A==", + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/shiki": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.4.3.tgz", + "integrity": "sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==", + "dependencies": { + "@shikijs/core": "4.4.3", + "@shikijs/engine-javascript": "4.4.3", + "@shikijs/engine-oniguruma": "4.4.3", + "@shikijs/langs": "4.4.3", + "@shikijs/themes": "4.4.3", + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/smol-toml": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz", + "integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/starlight-blog": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/starlight-blog/-/starlight-blog-0.28.0.tgz", + "integrity": "sha512-tKddmNbbUd20lV/m4Giih2RtDEegpnzPbrNdiwgI29XOCzVKXjtt9T9ckxTJU+cxwG1f0ii++J8duN9iBzw7Iw==", + "dependencies": { + "@astrojs/markdown-remark": "^7.2.0", + "@astrojs/mdx": "^7.0.0", + "@astrojs/rss": "^4.0.18", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-html": "^9.0.5", + "hast-util-to-string": "^3.0.1", + "mdast-util-mdx-expression": "^2.0.1", + "satteri": "^0.9.3", + "schema-dts": "^2.0.0", + "unist-util-is": "^6.0.1", + "unist-util-remove": "^4.0.0", + "unist-util-visit": "^5.1.0" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.41.0" + } + }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==" + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strnum": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==" + }, + "node_modules/svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, + "node_modules/tinyclip": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", + "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true + }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "peer": true, + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==" + }, + "node_modules/undici": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz", + "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.5.tgz", + "integrity": "sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==", + "dependencies": { + "css-tree": "^3.1.0", + "ohash": "^2.0.11", + "undici": "^8.0.0" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", + "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/url-extras": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/url-extras/-/url-extras-0.1.0.tgz", + "integrity": "sha512-8tzwTeXFPuX/5PHuCDQE5Dd9Ts4rwoq2t9aIT+HS4iAVpmj5l4Ao7Q+BuuFjvWRqrLswBhQDk8O96ZicgCqQqw==", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==" + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b3606d5 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "docs", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/sitemap": "^3.7.3", + "@astrojs/starlight": "^0.41.7", + "astro": "^7.1.6", + "astro-mermaid": "^2.1.0", + "mermaid": "^11.16.1", + "sharp": "^0.35.3", + "starlight-blog": "^0.28.0" + } +} diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..9cae0d1 Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon-96x96.png b/public/favicon-96x96.png new file mode 100644 index 0000000..72f298a Binary files /dev/null and b/public/favicon-96x96.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..06d1e1f Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..0735f2e --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/logo_loutik.png b/public/logo_loutik.png new file mode 100644 index 0000000..6308fe0 Binary files /dev/null and b/public/logo_loutik.png differ diff --git a/public/loutikdocs-social-card.png b/public/loutikdocs-social-card.png new file mode 100644 index 0000000..298ac95 Binary files /dev/null and b/public/loutikdocs-social-card.png differ diff --git a/public/mentions-legales-loutik.pdf b/public/mentions-legales-loutik.pdf new file mode 100644 index 0000000..d663dd1 Binary files /dev/null and b/public/mentions-legales-loutik.pdf differ diff --git a/public/photo_louismedo.jpg b/public/photo_louismedo.jpg new file mode 100644 index 0000000..9b69aa7 Binary files /dev/null and b/public/photo_louismedo.jpg differ diff --git a/public/politique-confidentialite-loutik.pdf b/public/politique-confidentialite-loutik.pdf new file mode 100644 index 0000000..9c91404 Binary files /dev/null and b/public/politique-confidentialite-loutik.pdf differ diff --git a/public/web-app-manifest-192x192.png b/public/web-app-manifest-192x192.png new file mode 100644 index 0000000..f128d65 Binary files /dev/null and b/public/web-app-manifest-192x192.png differ diff --git a/public/web-app-manifest-512x512.png b/public/web-app-manifest-512x512.png new file mode 100644 index 0000000..20170b4 Binary files /dev/null and b/public/web-app-manifest-512x512.png differ diff --git a/src/assets/houston.webp b/src/assets/houston.webp new file mode 100644 index 0000000..930c164 Binary files /dev/null and b/src/assets/houston.webp differ diff --git a/src/components/AccueilLoutik.astro b/src/components/AccueilLoutik.astro new file mode 100644 index 0000000..37cfe16 --- /dev/null +++ b/src/components/AccueilLoutik.astro @@ -0,0 +1,100 @@ +--- +// test +--- + +
+
+

+ LoutikDOCS +

+

DevOps, Administration systÚme & Administration Réseau

+ Lire la documentation 🚀 +
+ +
+
+

đŸ—ïž Homelab

+

Documentation de mon homelab (décisions d'architecture, topologies réseau, stratégies de conception etc...)

+ AccĂ©der ➜ +
+ +
+

📚 Notions

+

Base de connaissances théorique et pratique (LLM, conteneurisation, HTTP etc...)

+ AccĂ©der ➜ +
+ +
+

📖 Projets BTS SIO

+

Documentations des projets académiques (Millenuits, Imdeo).

+ AccĂ©der ➜ +
+
+
+ + \ No newline at end of file diff --git a/src/components/Footer.astro b/src/components/Footer.astro new file mode 100644 index 0000000..4477429 --- /dev/null +++ b/src/components/Footer.astro @@ -0,0 +1,85 @@ +--- +import Default from '@astrojs/starlight/components/Footer.astro'; +--- + + + + + + + + + + \ No newline at end of file diff --git a/src/components/SiteTitle.astro b/src/components/SiteTitle.astro new file mode 100644 index 0000000..0003cfb --- /dev/null +++ b/src/components/SiteTitle.astro @@ -0,0 +1,36 @@ +--- +// src/components/SiteTitle.astro +import type { Props } from '@astrojs/starlight/props'; +--- + + + + + + LoutikDOCS + + + + \ No newline at end of file diff --git a/src/content.config.ts b/src/content.config.ts new file mode 100644 index 0000000..8f13c5f --- /dev/null +++ b/src/content.config.ts @@ -0,0 +1,13 @@ +import { defineCollection } from 'astro:content'; +import { docsLoader } from '@astrojs/starlight/loaders'; +import { docsSchema } from '@astrojs/starlight/schema'; +import { blogSchema } from 'starlight-blog/schema' + +export const collections = { + docs: defineCollection({ + loader: docsLoader(), + schema: docsSchema({ + extend: (context) => blogSchema(context) + }) + }), +}; diff --git a/src/content/docs/404.md b/src/content/docs/404.md new file mode 100644 index 0000000..0fba2e9 --- /dev/null +++ b/src/content/docs/404.md @@ -0,0 +1,14 @@ +--- +title: Not found +template: splash +editUrl: false +lastUpdated: false +hero: + title: '404' + tagline: Houston, nous avons un problÚme. Nous n'avons pas pu trouver la page demandée.
VĂ©rifier l'URL ou rechercher depuis la barre de recherche. + actions: + - text: Aller Ă  l'accueil + icon: right-arrow + link: / + variant: primary +--- \ No newline at end of file diff --git a/src/content/docs/blog/2026-02-14-mon-portfolio-devops-projet-bts-sio/index.md b/src/content/docs/blog/2026-02-14-mon-portfolio-devops-projet-bts-sio/index.md new file mode 100644 index 0000000..17b266c --- /dev/null +++ b/src/content/docs/blog/2026-02-14-mon-portfolio-devops-projet-bts-sio/index.md @@ -0,0 +1,129 @@ +--- +title: "Mon portfolio DevOps : Projet BTS SIO" +date: 2026-02-14 +authors: [louismedo] +tags: [bts-sio, portfolio, gitops, devops] +excerpt: Durant mes Ă©tudes en BTS SIO (Services Informatiques aux Organisations) au lycĂ©e Paul-Louis Courier de Tours, il m'a Ă©tĂ© demandĂ© de rĂ©aliser un portfolio. Ce dernier a pour but de prĂ©senter les diffĂ©rentes certifications et projets que j'ai rĂ©alisĂ©s en cours ou sur mon homelab. +--- + +Durant mes Ă©tudes en BTS SIO (Services Informatiques aux Organisations) au lycĂ©e Paul-Louis Courier de Tours, il m'a Ă©tĂ© demandĂ© de rĂ©aliser un portfolio. Ce dernier a pour but de prĂ©senter les diffĂ©rentes certifications et projets que j'ai rĂ©alisĂ©s en cours ou sur mon homelab. + +L'objectif de ce portfolio est de crĂ©er une vitrine de mes compĂ©tences informatiques, facilement modifiable dans le temps, sans nĂ©cessiter de connexion manuelle Ă  mon serveur ni de retouches directes du code HTML. J'ai donc pensĂ© le site avec les contraintes suivantes : +- **FacilitĂ© d'Ă©dition :** Utilisation de fichiers JSON pour stocker les donnĂ©es liĂ©es Ă  mes diffĂ©rents Ă©lĂ©ments. +- **DĂ©ploiement automatisĂ© :** M'Ă©viter de me connecter Ă  mes serveurs Ă  chaque modification. +- **TraçabilitĂ© :** Suivi des modifications (versioning) pour revenir en arriĂšre en cas d'erreur dans le code. + +--- +## 📖 Expression des besoins + +Il convient d'abord de dĂ©finir les besoins de mon portfolio, qui doit respecter le rĂ©fĂ©rentiel du BTS SIO ainsi que mes objectifs citĂ©s ci-dessus. + +### 1. Besoins fonctionnels : + +- **Sections :** Le portfolio doit contenir une prĂ©sentation de mon profil, du BTS SIO, de mes expĂ©riences, compĂ©tences, formations, projets, certifications, sources de veille, et des documents des Ă©preuves E5 et E6. +- **Navigation :** La barre de navigation doit ĂȘtre positionnĂ©e Ă  gauche et mettre en surbrillance la section active. +- **Mentions lĂ©gales :** Pour respecter le RGPD, le site doit inclure les mentions lĂ©gales obligatoires (responsable, hĂ©bergeur...). +- **Modification simple :** Mettre Ă  jour le contenu facilement via des fichiers JSON sans Ă©diter le code HTML. + +### 2. Besoins techniques : + +- **Automatisation :** Le site doit se mettre Ă  jour seul, sans intervention manuelle sur le serveur. +- **TraçabilitĂ© des modifications :** Utilisation du versioning pour conserver un historique et annuler des changements en cas de bug. +- **Conteneurisation :** Le portfolio doit ĂȘtre packagĂ© sous forme d'image Docker pour ĂȘtre hĂ©bergĂ© sur mon infrastructure Kubernetes, garantissant ainsi sa scalabilitĂ©. + +--- +## 🔧 Technologies retenues + +Plusieurs technologies se dĂ©marquent pour la rĂ©alisation de ce projet. Pour la partie web et infrastructure, j'ai sĂ©lectionnĂ© les solutions suivantes : + +| Technologie | Justification | +| :--- | :--- | +| **PHP** *Affichage web* | PHP permet d'afficher dynamiquement les informations contenues dans les fichiers JSON. Il facilite Ă©galement l'utilisation de snippets (rĂ©utilisation de composants de code). | +| **JSON** *Stockage des donnĂ©es* | Format lĂ©ger et structurĂ© (parents/enfants). Contrairement Ă  une base de donnĂ©es, il ne nĂ©cessite pas d'infrastructure lourde ; un simple fichier permet Ă  PHP de rĂ©cupĂ©rer les informations. | +| **Tailwind CSS** *Mise en forme* | Ce framework utilitaire offre un gain de temps considĂ©rable en Ă©vitant l'Ă©criture de fichiers CSS complexes. Il optimise Ă©galement le code CSS final, rendant le site plus performant. | +| **Git** *Versioning* | Permet de conserver un historique complet de toutes les modifications effectuĂ©es sur le code source. | +| **GitHub Actions** *CI/CD* | Permet d'automatiser la crĂ©ation de l'image Docker du portfolio Ă  chaque modification, facilitant ainsi son dĂ©ploiement sur Kubernetes. | +| **Kubernetes (K3s)** *Orchestration* | Solution dĂ©jĂ  opĂ©rationnelle sur mon infrastructure permettant d'hĂ©berger l'image Docker. Elle assure la haute disponibilitĂ© du site. | + +--- +## đŸŠș RĂ©alisation du site (Dev) + +### 1. Le prototypage + +Avant de dĂ©buter le dĂ©veloppement, il est essentiel de dĂ©finir la direction du projet. La premiĂšre Ă©tape a consistĂ© Ă  rĂ©aliser une maquette sur Figma. Cela permet de visualiser le site et de s'assurer qu'il rĂ©pond aux attentes. Une fois la maquette validĂ©e par les professeurs, le dĂ©veloppement peut commencer sur des bases solides. + +### 2. Brique par brique + +Aujourd'hui, l'approche moderne consiste Ă  dĂ©velopper des composants (ou briques) plutĂŽt que de vastes monolithes. L'objectif est de dĂ©terminer quelles briques mettre en place et dans quel ordre. + +J'ai commencĂ© par dĂ©finir l'arborescence du projet pour faciliter sa maintenance. J'ai ensuite configurĂ© les outils nĂ©cessaires : Tailwind CSS et un conteneur local avec Apache2 pour prĂ©visualiser le portfolio durant le dĂ©veloppement. + +Le dĂ©veloppement a dĂ©butĂ© par les composants rĂ©utilisables (header, navbar, footer). Ensuite, j'ai construit le corps des pages et intĂ©grĂ© PHP pour la lecture dynamique des fichiers `.json`. Enfin, j'ai prĂ©parĂ© les fondations du dĂ©ploiement en rĂ©digeant le fichier permettant de conteneuriser l'application. + +### 3. PrĂ©production et sĂ©curitĂ© + +Avant toute mise en production, une revue de code s'impose. Bien que les technologies choisies prĂ©sentent une surface d'attaque rĂ©duite, il est crucial de s'assurer de la sĂ©curitĂ© de l'application. Par exemple, je vĂ©rifie rigoureusement que les requĂȘtes PHP accĂšdent uniquement aux fichiers JSON prĂ©vus, Ă©vitant ainsi les vulnĂ©rabilitĂ©s de type *Path Traversal*. + +--- +## ✹ Mise en place du site (Ops) + +### CrĂ©ation du Dockerfile + +Pour conteneuriser mon application, j'ai créé un `Dockerfile` basĂ© sur une image officielle PHP incluant Apache. + +```dockerfile +FROM php:8.2-apache + +# DĂ©finition de l'argument (disponible uniquement pendant le build) +ARG APP_VERSION=v0.0.0 + +# Transformation en variable d'environnement (disponible pour PHP pendant l'exĂ©cution) +ENV APP_VERSION=${APP_VERSION} + +COPY . /var/www/html/ +COPY apache-config.conf /etc/apache2/sites-available/000-default.conf +RUN a2enmod rewrite +EXPOSE 80 +``` + +### Pipeline CI/CD avec GitHub Actions + +J'ai ensuite configurĂ© un workflow GitHub Actions. À chaque "push" sur la branche principale, un script s'exĂ©cute automatiquement pour construire l'image Docker Ă  partir du `Dockerfile`. Une fois construite, cette image est poussĂ©e et stockĂ©e de maniĂšre sĂ©curisĂ©e dans le GitHub Container Registry (GHCR), prĂȘte Ă  ĂȘtre dĂ©ployĂ©e. + +### DĂ©ploiement Kubernetes et Keel + +Pour l'hĂ©bergement, j'ai rĂ©digĂ© les manifests Kubernetes (Deployment et Service) appliquĂ©s sur mon cluster K3s. Pour automatiser la mise Ă  jour des pods, j'utilise **Keel**. Keel surveille le registre GitHub ; dĂšs qu'une nouvelle version de mon image Docker y est publiĂ©e, il dĂ©clenche automatiquement le tĂ©lĂ©chargement de l'image et la mise Ă  jour du pod sur mon cluster sans aucune intervention de ma part. + +### Configuration du Reverse Proxy + +Pour rendre mon site accessible depuis l'extĂ©rieur de maniĂšre sĂ©curisĂ©e, le trafic passe d'abord par mon VPS cloud Ă©quipĂ© d'un reverse proxy **NGINX** et sĂ©curisĂ© par le WAF **Crowdsec**. NGINX rĂ©ceptionne la requĂȘte HTTP/HTTPS, puis la redirige au travers d'un tunnel VPN directement vers mon infrastructure on-premise hĂ©bergeant mon cluster K3s, qui sert la page web. + +![SchĂ©ma - client vers portfolio](./schema-client-vers-portfolio-loutik.png) + +### Enregistrement DNS + +Enfin, j'ai configurĂ© la zone DNS de mon nom de domaine en crĂ©ant un enregistrement de type "A" pointant vers l'adresse IP publique de mon VPS. Ainsi, lorsqu'un utilisateur tape l'URL de mon portfolio, la rĂ©solution DNS l'oriente correctement vers mon Reverse Proxy. + +--- +## đŸŽžïž Workflow final + +Maintenant que l'infrastructure et l'automatisation sont en place, la mise Ă  jour de mon portfolio est d'une grande simplicitĂ©. Une simple modification du code suivie d'un `git push` dĂ©clenche l'intĂ©gralitĂ© de la chaĂźne CI/CD jusqu'Ă  la mise en production. + +``` ++----------+ +---------------+ +----------------+ +----------+ +-------------------+ +| DĂ©veloppeur| | GitHub Actions| | GitHub Registry| | Keel (K3s)| | Pod Kubernetes | +| (Git Push) | ----> | (Build Image) | ----> | (Push Image) | ----> | (Watch) | ----> | (Update & Deploy) | ++----------+ +---------------+ +----------------+ +----------+ +-------------------+ +``` + +--- +## 💡 Une dĂ©monstration pertinente de la mĂ©thodologie DevOps + +Pour conclure, avec ce projet de Portfolio, je dĂ©montre qu'il est aujourd'hui indispensable de savoir conjuguer le dĂ©veloppement (Dev) et la gestion d'infrastructure (Ops). Cette approche permet de construire des applications fiables et rĂ©silientes tout en automatisant les tĂąches rĂ©pĂ©titives pour gagner en efficacitĂ©. + +--- +## 🔗 Ressources + +- **DĂ©pĂŽt GitHub du portfolio :** https://github.com/FireToak/portfolio-bts-sio +- **DĂ©pĂŽt GitHub du manifest kubernetes :** https://github.com/FireToak/k3s-deployment-portfolio +- **Lien vers le portfolio :** https://louis.loutik.fr/ \ No newline at end of file diff --git a/src/content/docs/blog/2026-02-14-mon-portfolio-devops-projet-bts-sio/schema-client-vers-portfolio-loutik.png b/src/content/docs/blog/2026-02-14-mon-portfolio-devops-projet-bts-sio/schema-client-vers-portfolio-loutik.png new file mode 100644 index 0000000..44487dd Binary files /dev/null and b/src/content/docs/blog/2026-02-14-mon-portfolio-devops-projet-bts-sio/schema-client-vers-portfolio-loutik.png differ diff --git a/src/content/docs/blog/2026-03-07-etude-comparative-grafana-prometheus-zabbix/index.md b/src/content/docs/blog/2026-03-07-etude-comparative-grafana-prometheus-zabbix/index.md new file mode 100644 index 0000000..3e53695 --- /dev/null +++ b/src/content/docs/blog/2026-03-07-etude-comparative-grafana-prometheus-zabbix/index.md @@ -0,0 +1,61 @@ +--- +title: "Étude comparative - Stack Prometheus/Grafana vs Zabbix" +date: 2026-02-14 +authors: [louismedo] +tags: [devops, supervision, infrastructure] +excerpt: Dans l'Ă©tat de l'art de la gestion d'infrastructure, disposer d'une visibilitĂ© globale en temps rĂ©el est indispensable. La supervision permet de passer d'une gestion subie Ă  une approche proactive. Par exemple, si le tableau de bord indique que le stockage de la machine virtuelle _bd01_ (hĂ©bergeant une base MySQL) atteint 70 %, l'Ă©quipe peut anticiper et allouer de l'espace supplĂ©mentaire bien avant la saturation, Ă©vitant ainsi une interruption de service. +--- + +Dans l'Ă©tat de l'art de la gestion d'infrastructure, disposer d'une visibilitĂ© globale en temps rĂ©el est indispensable. La supervision permet de passer d'une gestion subie Ă  une approche proactive. Par exemple, si le tableau de bord indique que le stockage de la machine virtuelle _bd01_ (hĂ©bergeant une base MySQL) atteint 70 %, l'Ă©quipe peut anticiper et allouer de l'espace supplĂ©mentaire bien avant la saturation, Ă©vitant ainsi une interruption de service. + +## A. PrĂ©sentation des outils + +### A.1. Prometheus/Grafana + +**Prometheus** est un moteur de supervision moderne spĂ©cialisĂ© dans la rĂ©colte de sĂ©ries temporelles. Il fonctionne en mode "Pull", interrogeant activement des _exporters_ HTTP pour rĂ©cupĂ©rer les mĂ©triques des systĂšmes. + +**Grafana** est l'outil de visualisation qui s'y couple. Il transforme les donnĂ©es brutes de Prometheus en tableaux de bord interactifs et esthĂ©tiques. + +_Note :_ Grafana Ă©tant agnostique, il est tout Ă  fait possible de l'utiliser pour requĂȘter l'API de Zabbix. Cela permet de conserver le moteur de collecte de Zabbix tout en bĂ©nĂ©ficiant de l'interface de rendu trĂšs supĂ©rieure de Grafana. + +### A.2. Zabbix + +**Zabbix** est une solution de supervision monolithique et tout-en-un (collecte, base de donnĂ©es relationnelle, alertes, IHM). Il s'appuie principalement sur des agents installĂ©s sur les Ă©quipements (serveurs, VM) ou sur des protocoles standards comme le SNMP pour interroger le matĂ©riel rĂ©seau. + +--- +## B. Les philosophies de chaque outil + +### B.1. Prometheus/Grafana + +La philosophie de cette stack est orientĂ©e **Cloud Native** et **dĂ©centralisation**. Elle est conçue pour des environnements extrĂȘmement dynamiques (comme les microservices ou Docker/Kubernetes) oĂč les ressources apparaissent et disparaissent en permanence. Les donnĂ©es sont organisĂ©es via un systĂšme de _labels_ (Ă©tiquettes) ultra-flexible, adaptĂ© Ă  l'automatisation et au requĂȘtage complexe (PromQL). + +### B.2. Zabbix + +La philosophie de Zabbix repose sur la **centralisation** et la **stabilitĂ©**. L'outil excelle dans la modĂ©lisation hiĂ©rarchique d'un systĂšme d'information classique (HĂŽte > ÉlĂ©ment > DĂ©clencheur). Il est pensĂ© pour surveiller des composants Ă  longue durĂ©e de vie, de la couche matĂ©rielle physique jusqu'Ă  l'OS, via une gestion fine des droits et des utilisateurs. + +--- +## C. Tableau comparatif des fonctionnalitĂ©s + +|**FonctionnalitĂ©**|**Prometheus + Grafana**|**Zabbix**| +|---|---|---| +|**Architecture**|Microservices, Base de donnĂ©es temporelle (TSDB)|Monolithique, Base de donnĂ©es relationnelle| +|**Collecte**|Pull (requĂȘtes HTTP sur endpoints)|Hybride (Agents, Push/Pull, SNMP)| +|**DĂ©couverte**|Dynamique, orientĂ©e services (Service Discovery)|Classique, orientĂ©e rĂ©seau (Scans IP)| +|**Visualisation**|Interface externe avancĂ©e (Grafana)|Interface web intĂ©grĂ©e (basique)| +|**Alerting**|Externe (Alertmanager), basĂ© sur requĂȘtes|IntĂ©grĂ©, basĂ© sur expressions mathĂ©matiques| +|**Configuration**|Fichiers texte YAML (IdĂ©al pour l'Infra-as-Code)|Interface Web cliquable (IHM)| + +--- +## D. Les cas d'utilisation + +**Quand choisir Prometheus/Grafana ?** +Cette stack est le standard de facto si votre projet implique des conteneurs, de l'orchestration dynamique, ou du dĂ©ploiement continu (CI/CD). Elle est idĂ©ale si nous avons besoin de suivre les mĂ©triques internes d'applications dĂ©veloppĂ©es sur mesure et que nous souhaitons gĂ©rer toute notre infrastructure par du code (fichiers YAML). + +**Quand choisir Zabbix ?** +Zabbix est la rĂ©fĂ©rence si le pĂ©rimĂštre du projet englobe une infrastructure d'entreprise classique. Il est le meilleur choix pour monitorer en profondeur des Ă©quipements rĂ©seau (switchs, routeurs Cisco/Aruba), des hyperviseurs, des serveurs physiques, et si l'Ă©quipe prĂ©fĂšre paramĂ©trer la supervision via une interface graphique unique de bout en bout. + +--- +## Ressources +- https://prometheus.io/ +- https://grafana.com/ +- https://www.zabbix.com/ \ No newline at end of file diff --git a/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/index.md b/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/index.md new file mode 100644 index 0000000..5f0fb2e --- /dev/null +++ b/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/index.md @@ -0,0 +1,129 @@ +--- +title: "De l'Homelab Ă  l'Industrie : DĂ©ploiement d'une Architecture Kubernetes GitOps avec FluxCD" +date: 2026-03-22 +authors: [louismedo] +tags: [devops, git, kubernetes, homelab, fluxcd] +excerpt: "Dans le cadre de mon projet personnel LoutikCLOUD, j'ai dĂ» faire face Ă  deux dĂ©fis majeurs : comment gĂ©rer une infrastructure conteneurisĂ©e croissante sans tomber dans le piĂšge de la \"configuration manuelle\" (le fameux ClickOps) ? Et surtout, comment restaurer rapidement une infrastructure complexe comme Kubernetes en cas de sinistre ?" +--- + +## A. La problĂ©matique de la gestion d'un cluster + +Dans le cadre de mon projet personnel **LoutikCLOUD**, j'ai dĂ» faire face Ă  deux dĂ©fis majeurs : comment gĂ©rer une infrastructure conteneurisĂ©e croissante sans tomber dans le piĂšge de la "configuration manuelle" (le fameux *ClickOps*) ? Et surtout, comment restaurer rapidement une infrastructure complexe comme Kubernetes en cas de sinistre ? + +Avec la multiplication des services (site vitrine, bot Discord, outils de surveillance), la mĂ©thode traditionnelle consistant Ă  appliquer des commandes `kubectl` manuellement prĂ©sentait trois risques critiques : + +1. **La dĂ©rive de configuration** : L'Ă©tat rĂ©el du cluster finit par diffĂ©rer de ce que l'on pense avoir dĂ©ployĂ©. +2. **Un RTO (Recovery Time Objective) trop long** : En cas de panne, reconstruire l'infrastructure Ă  la main prendrait un temps incompatible avec une exigence de service. +3. **L'absence de traçabilitĂ©** : Il devenait difficile de dĂ©terminer qui a modifiĂ© quoi, et quand. + +Mon objectif Ă©tait clair : migrer vers une architecture **GitOps** capable de garantir un RTO infĂ©rieur Ă  30 minutes grĂące au redĂ©ploiement automatique, tout en sĂ©parant strictement la configuration de l'outil d'orchestration (FluxCD) de celle des applications mĂ©tiers. + +## B. Les choix techniques : Pourquoi K3s et FluxCD ? + +Pour rĂ©pondre Ă  ces besoins, j'ai arrĂȘtĂ© une stack technique prĂ©cise, privilĂ©giant la lĂ©gĂšretĂ© et l'automatisation native. + +### 1. L'Orchestrateur : K3s + +PlutĂŽt qu'un cluster Kubernetes standard, souvent lourd en ressources, j'ai choisi **K3s**. + +* **LĂ©gĂšretĂ©** : IdĂ©al pour un environnement homelab ou *edge*. +* **Simplification** : Remplacement de la base de donnĂ©es `etcd` par **SQLite** pour le Plan de ContrĂŽle. C'est suffisant pour mon Ă©chelle et bien moins complexe Ă  maintenir. +* **Runtime** : Utilisation de **Containerd** directement intĂ©grĂ©, sans la surcouche Docker devenue inutile pour Kubernetes. + +### 2. L'approche GitOps : FluxCD + +J'ai sĂ©lectionnĂ© FluxCD comme moteur de rĂ©conciliation, le prĂ©fĂ©rant Ă  des solutions comme ArgoCD pour plusieurs raisons stratĂ©giques : + +* **LĂ©gĂšretĂ© et simplicitĂ© :** FluxCD ne possĂšde pas d'interface graphique (UI). Bien que cela puisse sembler ĂȘtre un frein, c'est un atout majeur pour un homelab ou des environnements edge : l'outil est extrĂȘmement gourmand en ressources, contrairement Ă  ArgoCD qui nĂ©cessite plus de mĂ©moire et de CPU pour faire tourner son serveur web et son interface. +- **SĂ©curitĂ© rĂ©seau (Approche Pull) :** FluxCD fonctionne exclusivement en mode Pull. C'est l'outil qui va chercher les changements sur GitHub, et non l'inverse. + - **ConsĂ©quence directe :** Je n'ai aucun besoin de configurer des clĂ©s SSH ou des webhooks entrants depuis GitHub vers mon infrastructure. + - **Avantage :** Mon cluster reste totalement isolĂ© derriĂšre mon pare-feu. Aucune connexion entrante depuis Internet n'est nĂ©cessaire pour le dĂ©ploiement, ce qui rĂ©duit considĂ©rablement la surface d'attaque. +- **Source de vĂ©ritĂ© unique :** Tout rĂ©side dans Git. Si le cluster dĂ©rive, FluxCD le corrige automatiquement. + +### 3. L'Architecture Multi-DĂ©pĂŽts + +Une dĂ©cision structurante a Ă©tĂ© la sĂ©paration en deux dĂ©pĂŽts Git distincts : + +* `loutik-cloud_k3s-flux-system` : Contient la configuration de FluxCD lui-mĂȘme (les contrĂŽleurs, les sources, les politiques). C'est le "chef d'orchestre". +* `loutik-cloud_k3s-manifests` : Contient les applications mĂ©tiers (Site vitrine, Bot Discord) et l'infrastructure rĂ©seau (Traefik). C'est la "partition musicale". + +Cette sĂ©paration permet de modifier la logique d'automatisation sans risquer de corrompre les dĂ©ploiements applicatifs, et vice-versa. + +## C. DifficultĂ©s rencontrĂ©es et solutions apportĂ©es + +Le chemin vers une infrastructure 100 % dĂ©clarative n'a pas Ă©tĂ© sans embĂ»ches. Voici les principaux obstacles techniques que j'ai dĂ» surmonter. + +### 1. La gestion des secrets + +* **ProblĂšme** : Le principe du GitOps impose que tout soit dans Git, mais il est hors de question de versionner des mots de passe ou des tokens en clair. +* **Solution** : J'ai adoptĂ© une approche hybride sĂ©curisĂ©e. Les secrets sensibles (tokens API, mots de passe BDD) sont stockĂ©s dans un coffre-fort **Bitwarden**. Ils sont injectĂ©s manuellement dans le cluster via `kubectl create secret` avant le dĂ©ploiement. FluxCD fait ensuite le lien via des `secretRef` dans ses manifests, sans jamais voir la donnĂ©e sensible dans Git. +* **Axe d'amĂ©lioration envisagĂ©** : IntĂ©grer une solution comme **HashiCorp Vault** pour centraliser et faire tourner les clĂ©s API et autres secrets de maniĂšre dynamique. + +### 2. L'automatisation des mises Ă  jour d'images (Image Update Automation) + +* **ProblĂšme** : Comment mettre Ă  jour un conteneur quand une nouvelle image Docker est poussĂ©e sur le registre (GHCR) sans intervention humaine ? +* **Solution** : J'ai dĂ©ployĂ© les modules **Image Reflector** et **Image Automation Controller** de FluxCD sur mon infrastructure. + +Cependant, la configuration a Ă©tĂ© dĂ©licate. Il a fallu : + +1. Ajouter un marqueur spĂ©cifique (`{"$imagepolicy": "..."}`) dans les fichiers YAML de dĂ©ploiement. +2. CrĂ©er des politiques de versionnement sĂ©mantique (ex : accepter uniquement les versions `>=1.0.0`) pour Ă©viter les ruptures de compatibilitĂ©. +3. Appliquer ces politiques de versionnement sur toutes les chaĂźnes CI/CD de l'infrastructure. +4. Configurer un token Git avec droits d'Ă©criture pour permettre Ă  FluxCD de commiter automatiquement le nouveau tag de version dans le dĂ©pĂŽt. + +### 3. La sĂ©curisation du Plan de ContrĂŽle + +> **Note technique** : Par dĂ©faut sur **K3s**, le *Control Plane* est Ă©galement un nƓud *worker* capable d'exĂ©cuter des pods clients. + +* **ProblĂšme** : Éviter que des applications utilisateur ne s'exĂ©cutent sur le nƓud **Control Plane**, ce qui pourrait compromettre la stabilitĂ© du cƓur du cluster. +* **Solution** : Application de **Taints** (`CriticalAddonsOnly=true:NoExecute`) sur le nƓud *Control Plane*. Seul le cƓur de Kubernetes peut y tourner. Les charges de travail sont forcĂ©es vers les nƓuds *Workers* via des labels et des tolĂ©rances. + +## D. RĂ©sultat final + +Aujourd'hui, l'infrastructure **LoutikCLOUD** fonctionne de maniĂšre autonome selon le principe de la boucle de rĂ©conciliation. + +### 1. Le flux de dĂ©ploiement + +Lorsqu'un dĂ©veloppeur modifie un fichier de configuration dans le dĂ©pĂŽt de manifests : + +1. Le contrĂŽleur **Source** de FluxCD dĂ©tecte le nouveau commit sur GitHub. +2. Le contrĂŽleur **Kustomize** lit les fichiers, assemble les ressources (Namespace, Deployment, Ingress) et les soumet Ă  l'API Server de Kubernetes. +3. Le **Scheduler** place les Pods sur les Workers disponibles. +4. Le **Kubelet** lance les conteneurs via Containerd. + +![SchĂ©ma - Flux de dĂ©ploiement sur K3s avec FluxCD - LoutikCLOUD](./schĂ©ma-flux_kubernetes_mise-a-jour-configuration-fluxcd_loutikcloud.svg) +*SchĂ©ma - Flux de dĂ©ploiement sur K3s avec FluxCD* + +### 2. Le flux de mise Ă  jour automatique (Continuous Deployment) + +Pour mon site vitrine et mes bots, le processus est entiĂšrement automatisĂ© : + +1. Je pousse du code sur le dĂ©pĂŽt de l'application. +2. La CI compile et pousse une nouvelle image Docker sur GHCR. +3. L'**Image Reflector Controller** dĂ©tecte la nouvelle version. +4. L'**Image Automation Controller** met Ă  jour le fichier YAML dans le dĂ©pĂŽt Git et crĂ©e un commit automatique. +5. La boucle GitOps standard se dĂ©clenche pour dĂ©ployer la nouvelle version en production sans aucune intervention de ma part. + +![SchĂ©ma - Flux CI/CD avec dĂ©ploiement automatique des conteneurs Docker - LoutikCLOUD](./schĂ©ma-flux_kubernetes_automatisation-mise-a-jour-fluxcd_loutikcloud.svg) +*SchĂ©ma - Flux CI/CD avec dĂ©ploiement automatique des conteneurs Docker* + +### 3. Exemple d'une requĂȘte utilisateur + +Le trafic utilisateur arrive sur un VPS Infomaniak (Gateway), transite par un tunnel **Tailscale** sĂ©curisĂ© vers mon infrastructure locale, puis est routĂ© par **Traefik** (Ingress Controller) qui effectue le Load Balancing vers les Pods actifs. + +![SchĂ©ma - RequĂȘte utilisateur sur K3s - LoutikCLOUD](. +/schĂ©ma-flux_requete-utilisateur-service-http_loutikcloud.svg) +*SchĂ©ma - RequĂȘte utilisateur sur K3s - LoutikCLOUD* + +## E. Conclusion + +Ce projet m'a permis de passer d'une vision thĂ©orique de Kubernetes Ă  une maĂźtrise opĂ©rationnelle d'une architecture de production. L'adoption de **FluxCD** et de la logique *Infrastructure as Code* a transformĂ© ma gestion de l'homelab : fini les scripts Bash fragiles et les interventions d'urgence, place Ă  la dĂ©clarativitĂ© et Ă  l'auditabilitĂ©. + +Cette architecture, bien que dĂ©ployĂ©e dans un contexte personnel, reprend les standards observĂ©s en entreprise (sĂ©paration des rĂŽles, gestion sĂ©curisĂ©e des secrets, CI/CD automatisĂ©e). Elle constitue une base solide pour *scaler* vers des besoins plus complexes, comme le multi-clustering ou la gestion de politiques de sĂ©curitĂ© avancĂ©es (OPA/Gatekeeper). + +Le code source de cette infrastructure est disponible sur mon GitHub et mes fiches de procĂ©dures dĂ©taillĂ©es sont consultables sur mon site de documentation : + +* 📄 [ProcĂ©dure d'installation de FluxCD sur K3s](https://google.com) +* đŸ—ș [Cartographie et fonctionnement du cluster](https://google.com) +* đŸ’» [DĂ©pĂŽt des Manifests (Applications)](https://github.com/FireToak/loutik-cloud_k3s-manifests) +* ⚙ [DĂ©pĂŽt du SystĂšme FluxCD](https://github.com/FireToak/loutik-cloud_k3s-flux-system) \ No newline at end of file diff --git "a/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_kubernetes_automatisation-mise-a-jour-fluxcd_loutikcloud.svg" "b/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_kubernetes_automatisation-mise-a-jour-fluxcd_loutikcloud.svg" new file mode 100644 index 0000000..b5fb83e --- /dev/null +++ "b/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_kubernetes_automatisation-mise-a-jour-fluxcd_loutikcloud.svg" @@ -0,0 +1,2 @@ +LĂ©gendeFlux de FluxCDFlux utilisateurServices KubernetesServices GitHub (Gitet registry docker)Services FluxCDDĂ©pĂŽt Githubloutik-cloud_flux-systemDĂ©pĂŽt Githubloutik-cloud_manifestsCluster Kubernetessource-controllerkustomize-controllerimage-automation-controllerPods flux-systemSSOAuthentikSite vitrineSpeedtestPods ApplicatifRegistry DockerGHCRDev13API ServerKubeletControl plane678L'automatisation des mises Ă  jourFlux de KubernetesContainers (Pods)DĂ©pĂŽt Githubloutik_site-vitrine2image-reflector-controller45ChaĂźne CI/CD dudĂ©pĂŽt gitFlux CI/CD \ No newline at end of file diff --git "a/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_kubernetes_mise-a-jour-configuration-fluxcd_loutikcloud.svg" "b/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_kubernetes_mise-a-jour-configuration-fluxcd_loutikcloud.svg" new file mode 100644 index 0000000..7e4a530 --- /dev/null +++ "b/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_kubernetes_mise-a-jour-configuration-fluxcd_loutikcloud.svg" @@ -0,0 +1,2 @@ +LĂ©gendeFlux de FluxCDFlux utilisateurServices KubernetesServices GitHub (Gitet registry docker)Services FluxCDDĂ©pĂŽt Githubloutik-cloud_flux-systemDĂ©pĂŽt Githubloutik-cloud_manifestsCluster Kubernetessource-controllerkustomize-controllerimage-automation-controllerPods flux-systemSSOAuthentikSite vitrineSpeedtestPods ApplicatifRegistry DockerGHCRDev123API ServerKubeletControl plane456Modification de configuration des podsFlux de KubernetesContainers \ No newline at end of file diff --git "a/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_requete-utilisateur-service-http_loutikcloud.svg" "b/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_requete-utilisateur-service-http_loutikcloud.svg" new file mode 100644 index 0000000..be01f48 --- /dev/null +++ "b/src/content/docs/blog/2026-03-22-homelab-industrie-deploiement-architecture-kubernetes-fluxcd/sch\303\251ma-flux_requete-utilisateur-service-http_loutikcloud.svg" @@ -0,0 +1,2 @@ +UtilisateurDNSloutik.frVPSgateway01-infomaniakEntrypointVPN TailscaleRT_LOUTIKCluster KubernetesIngressTraefikPodSite vitrine 1PodSite vitrine 2PodSite vitrine 3RĂ©solution DNS12RequĂȘte HTTP au Reverse Proxy3Transmission requĂȘte par le VPN4Routage de la requĂȘte vers K3s 5Transmission de larequĂȘte au podOn premiseInternet \ No newline at end of file diff --git a/src/content/docs/homelab/assets/flux-reseaux-utilisateur-externe.png b/src/content/docs/homelab/assets/flux-reseaux-utilisateur-externe.png new file mode 100644 index 0000000..d8cea35 Binary files /dev/null and b/src/content/docs/homelab/assets/flux-reseaux-utilisateur-externe.png differ diff --git a/src/content/docs/homelab/assets/photo-homelab-loutik.jpg b/src/content/docs/homelab/assets/photo-homelab-loutik.jpg new file mode 100644 index 0000000..c6cb9a1 Binary files /dev/null and b/src/content/docs/homelab/assets/photo-homelab-loutik.jpg differ diff --git a/src/content/docs/homelab/hyperviseur/assets/flux-reseaux-proxmox.png b/src/content/docs/homelab/hyperviseur/assets/flux-reseaux-proxmox.png new file mode 100644 index 0000000..8ed1f3b Binary files /dev/null and b/src/content/docs/homelab/hyperviseur/assets/flux-reseaux-proxmox.png differ diff --git a/src/content/docs/homelab/hyperviseur/choix-proxmox.md b/src/content/docs/homelab/hyperviseur/choix-proxmox.md new file mode 100644 index 0000000..0bfb71c --- /dev/null +++ b/src/content/docs/homelab/hyperviseur/choix-proxmox.md @@ -0,0 +1,69 @@ +--- +title: "Choix - Proxmox VE" +description: "Explication et justification du choix technique de Proxmox VE au sein de LoutikCLOUD." +sidebar: + order: 2 +--- + +## A. Contexte + +Le laboratoire LoutikCLOUD repose sur un parc matĂ©riel hĂ©tĂ©rogĂšne de rĂ©cupĂ©ration et d'optimisation. L'infrastructure physique se compose de deux machines de bureau HP ProDesk 400 G3 (processeur Intel Core i5-6500, 32 Go de RAM, 256 Go SSD + 500 Go HDD) et d'un ordinateur portable Dell Inspiron 15 3225 (processeur AMD Ryzen 5 5500U offrant 12 cƓurs logiques, 16 Go de RAM, 256 Go SSD NVMe + 500 Go HDD). + +Le dĂ©fi actuel est de transformer ces trois Ă©quipements physiques disparates en un environnement d'hĂ©bergement unifiĂ©, rĂ©silient et automatisable. L'objectif est de pouvoir dĂ©ployer des services d'infrastructure Ă  la volĂ©e (via de l'Infrastructure as Code) tout en maĂźtrisant totalement les coĂ»ts. Le besoin d'une solution *bare-metal*[^1] capable de lier ces trois machines est donc critique. + +## B. Cahiers des charges + +| ID | Type | Exigence | Description | +| :--- | :--- | :--- | :--- | +| **[REQ-F01]** | Fonctionnel | GratuitĂ© | La solution ne doit nĂ©cessiter aucune licence payante pour dĂ©bloquer ses fonctionnalitĂ©s fondamentales. | +| **[REQ-F02]** | Fonctionnel | Support de Cloud-Init | CapacitĂ© Ă  injecter automatiquement des configurations (clĂ©s SSH, rĂ©seau, utilisateurs) lors du premier dĂ©marrage d'une machine virtuelle. | +| **[REQ-T01]** | Technique | Fonctionnement en Cluster | PossibilitĂ© de regrouper les trois machines physiques sous une interface et une API de gestion uniques. | +| **[REQ-T02]** | Technique | TolĂ©rance matĂ©rielle (HCL) | Le systĂšme doit accepter du matĂ©riel grand public (cartes rĂ©seau Realtek, chipsets de PC portables) sans exiger de matĂ©riel certifiĂ© serveur. | + +## C. Les solutions du marchĂ© + +### C.1. PrĂ©sentations des solutions + +#### C.1.1. Proxmox VE +* **PrĂ©sentation gĂ©nĂ©rale :** Hyperviseur open-source basĂ© sur la distribution Linux Debian. Il intĂšgre nativement la virtualisation (KVM) et les conteneurs (LXC). +* **Fonctionnement :** Il s'installe directement sur le matĂ©riel physique et propose une interface web complĂšte pour gĂ©rer le stockage, le rĂ©seau et la haute disponibilitĂ©. +* **Profil :** TrĂšs prisĂ© dans l'Ă©cosystĂšme *homelab*[^2] et chez les hĂ©bergeurs alternatifs pour sa flexibilitĂ©, son absence de bridage de licence et sa robustesse. + +#### C.1.2. VMware vSphere (ESXi) +* **PrĂ©sentation gĂ©nĂ©rale :** Le standard historique des entreprises. C'est une solution propriĂ©taire et fermĂ©e (dĂ©sormais gĂ©rĂ©e par Broadcom). +* **Fonctionnement :** Utilise un micro-noyau trĂšs optimisĂ© mais trĂšs strict sur les pilotes matĂ©riels. La gestion en cluster nĂ©cessite un composant externe payant (vCenter). +* **Profil :** OrientĂ© exclusivement vers le monde de l'entreprise (Corporate) avec des budgets de licences consĂ©quents et du matĂ©riel certifiĂ©. + +#### C.1.3. XCP-ng +* **PrĂ©sentation gĂ©nĂ©rale :** DĂ©rivĂ© open-source du projet XenServer, soutenu par l'entreprise Vates. +* **Fonctionnement :** Repose sur l'hyperviseur Xen. Pour ĂȘtre exploitĂ© pleinement, il doit ĂȘtre couplĂ© Ă  "Xen Orchestra", une interface de gestion externe (fournie sous forme d'appliance ou Ă  compiler soi-mĂȘme). +* **Profil :** OrientĂ© infrastructure d'entreprise et fournisseurs de cloud (IaaS), avec une volontĂ© de proposer une alternative open-source sĂ©rieuse Ă  VMware. + +### C.2. Comparatifs des solutions + +| Exigence | Proxmox VE | VMware ESXi | XCP-ng | +| :--- | :--- | :--- | :--- | +| **[REQ-F01 (GratuitĂ©)]** | ValidĂ© (gratuit) | Non validĂ© (Licences gratuites supprimĂ©es) | Évaluation (Gratuit, mais Xen Orchestra nĂ©cessite compilation) | +| **[REQ-F02 (Cloud-Init)]** | ValidĂ© (Support natif UI/API) | Évaluation (Complexe sans vCenter) | Évaluation (NĂ©cessite l'interface Xen Orchestra) | +| **[REQ-T01 (Cluster)]** | ValidĂ© (Natif via Corosync) | Non validĂ© (NĂ©cessite vCenter payant) | ValidĂ© (Natif via Xen Orchestra) | +| **[REQ-T02 (MatĂ©riel)]** | ValidĂ© (Base Debian permissive) | Non validĂ© (HCL trĂšs stricte, refusera le PC portable) | ValidĂ© (Base CentOS permissive) | + +## D. Solution proposĂ©e + +La solution proposĂ©e pour l'infrastructure est **Proxmox VE**. + +Proxmox VE s'impose comme une Ă©vidence technique pour LoutikCLOUD. En s'appuyant sur un noyau Debian standard, il garantit la reconnaissance matĂ©rielle de nos machines HP ProDesk et du PC portable Dell, lĂ  oĂč des hyperviseurs d'entreprise bloqueraient dĂšs l'installation sur des pilotes rĂ©seau. Les trois nƓuds (les deux HP et le Dell) seront rassemblĂ©s au sein d'un mĂȘme **cluster**[^3] Proxmox. Cela permettra de centraliser l'administration sur une seule adresse IP et de gĂ©rer les ressources globales (le calcul CPU et le stockage SSD/HDD) de maniĂšre unifiĂ©e. + +De plus, l'intĂ©gration native de **Cloud-init**[^4] dans l'interface de Proxmox s'aligne parfaitement avec notre approche d'automatisation. Il sera possible de dĂ©ployer un *template* (modĂšle) de Debian, de lui attribuer une IP statique et des clĂ©s SSH Ă  la volĂ©e, le tout pilotĂ© via des outils comme Terraform ou Ansible. + +**Justification du rejet des solutions alternatives :** +* **VMware vSphere (ESXi) :** RejetĂ© catĂ©goriquement. Broadcom a mis fin Ă  la version gratuite (ESXi Free). De plus, son *HCL*[^5] extrĂȘmement stricte aurait trĂšs certainement refusĂ© de s'installer sur la carte rĂ©seau grand public du PC portable Dell. Enfin, la crĂ©ation d'un cluster nĂ©cessite un serveur vCenter, qui est une solution payante et lourde. +* **XCP-ng :** RejetĂ© pour des raisons de complexitĂ© architecturale dans notre contexte prĂ©cis. Bien que la solution soit excellente, la gestion de Cloud-init et du clustering nĂ©cessite de dĂ©ployer Xen Orchestra. Pour l'obtenir gratuitement avec toutes les fonctionnalitĂ©s dĂ©bloquĂ©es, il faut le compiler depuis les sources (XOA), ce qui ajoute une friction de maintenance inutile (mise Ă  jour manuelle de l'orchestrateur) par rapport Ă  l'approche "tout-en-un" native de Proxmox VE. + +--- + +[^1]: **Bare-metal** : Traduit par "mĂ©tal nu". DĂ©signe un systĂšme d'exploitation ou un hyperviseur qui s'installe directement sur le matĂ©riel physique de l'ordinateur, sans passer par un systĂšme classique (comme Windows ou macOS) au prĂ©alable. +[^2]: **Homelab** : Un laboratoire informatique Ă  domicile. C'est un environnement oĂč les passionnĂ©s et professionnels de l'informatique hĂ©bergent des serveurs chez eux pour expĂ©rimenter, apprendre et auto-hĂ©berger des services. +[^3]: **Cluster** : Une "grappe" de serveurs. C'est le fait de relier plusieurs ordinateurs physiques ensemble via le rĂ©seau pour qu'ils se comportent comme un seul grand systĂšme unifiĂ©. +[^4]: **Cloud-init** : Un outil standardisĂ© qui permet de configurer automatiquement une machine virtuelle lors de son tout premier dĂ©marrage (injection de mots de passe, clĂ©s de sĂ©curitĂ©, configuration du rĂ©seau), Ă©vitant ainsi de devoir le faire Ă  la main. +[^5]: **HCL (Hardware Compatibility List)** : Une liste trĂšs stricte tenue par les Ă©diteurs de logiciels d'entreprise qui dĂ©finit exactement quels composants Ă©lectroniques sont autorisĂ©s et reconnus par leur systĂšme. \ No newline at end of file diff --git a/src/content/docs/homelab/hyperviseur/pratique-proxmox.md b/src/content/docs/homelab/hyperviseur/pratique-proxmox.md new file mode 100644 index 0000000..587da7b --- /dev/null +++ b/src/content/docs/homelab/hyperviseur/pratique-proxmox.md @@ -0,0 +1,76 @@ +--- +title: "Pratique - Proxmox VE" +description: "DĂ©ploiement, architecture et retour d'expĂ©rience sur l'intĂ©gration de Proxmox VE dans l'infrastructure." +sidebar: + order: 3 +--- + +## A. Contexte + +Le service Proxmox VE[^1] a Ă©tĂ© intĂ©grĂ© au cƓur de l'infrastructure LoutikCLOUD pour fournir une plateforme de virtualisation robuste. Ce cluster[^2] tourne sur un parc de rĂ©cupĂ©ration hĂ©tĂ©rogĂšne et est hĂ©bergĂ© dans un environnement rĂ©seau isolĂ©, nĂ©cessitant une gestion rigoureuse des flux sortants et de l'authentification. + +## B. Architecture + +L'infrastructure repose sur 3 hyperviseurs avec une sĂ©paration claire du stockage (SystĂšme / DonnĂ©es) pour optimiser les performances : +- **2x NƓuds HP ProDesk 400 G3** : Intel Core i5-6500, 32 Go RAM, SSD 256 Go (OS) et HDD 500 Go (DonnĂ©es). +- **1x NƓud Dell Inspiron 15 3225** : AMD Ryzen 5 5500U (12 cƓurs), 16 Go RAM, SSD NVMe 256 Go (OS) et HDD 500 Go (DonnĂ©es). + +![Flux rĂ©seaux proxmox](./assets/flux-reseaux-proxmox.png) +*Flux rĂ©seaux Proxmox* + +## C. DĂ©ploiement + +Aucune configuration n'a Ă©tĂ© rĂ©alisĂ©e manuellement. L'ensemble a Ă©tĂ© provisionnĂ© en IaC[^3] Ă  l'aide d'Ansible[^4] (via l'utilisateur de service `svc-ansible`). Une Ă©volution est prĂ©vue pour remplacer l'inventaire statique actuel par un inventaire dynamique, ce qui unifiera la logique d'exĂ©cution des playbooks. + +* **Étape 1 / Initialisation :** ExĂ©cution du playbook `pve-bootstrap-cluster.yml` pour initialiser le tout premier nƓud et crĂ©er la base du cluster. +* **Étape 2 / Extension :** ExĂ©cution du playbook `pve-bootstrap-node.yml` pour configurer et faire rejoindre les deux autres machines au cluster existant. +* **Étape 3 / Sauvegardes :** ExĂ©cution du rĂŽle `pve-rclone-backup` qui installe rclone et fuse3, puis crĂ©e un service systemd[^5] pour monter automatiquement un stockage Nextcloud via WebDAV[^6]. Ce point de montage est ensuite dĂ©clarĂ© dans Proxmox pour stocker les sauvegardes. + +## D. DifficultĂ©s + +Lors de la mise en place, quelques ajustements ont Ă©tĂ© nĂ©cessaires : + +* **DĂ©pĂŽts payants inaccessibles :** Par dĂ©faut, Proxmox cherche les dĂ©pĂŽts "Enterprise", ce qui provoque des erreurs APT[^7] puisqu'il n'y a pas de licence. + * **Solution :** Le rĂŽle Ansible `pve-bootstrap` supprime automatiquement le fichier `pve-enterprise.sources` et installe le dĂ©pĂŽt communautaire `pve-no-subscription`. + +```mermaid +sequenceDiagram + autonumber + actor U as Utilisateur + participant PVE as Cluster Proxmox + participant S as Proxy Squid + participant R as Routeur OPNsense + participant SSO as IAM Authentik (SSO) + + U->>PVE: Demande de connexion (Bouton Login OIDC) + + Note over PVE,S: Proxmox ignore le proxy configurĂ© pour ce flux spĂ©cifique + PVE-xS: RequĂȘte d'authentification (Échoue / Ne passe pas par le proxy) + + PVE->>R: La requĂȘte part vers la passerelle par dĂ©faut + + Note over R: VĂ©rification de l'ACL (VLAN ADMOOB)
RĂšgle : HOST_PVE autorisĂ© vers WEBSITE_SSO + R->>SSO: Flux autorisĂ© (Bypass du Proxy) + + SSO-->>R: Validation de l'identitĂ© + R-->>PVE: Retour du Token OIDC + + PVE-->>U: AccĂšs accordĂ© Ă  l'interface d'administration +``` + +* **RĂ©seau isolĂ© :** Les hyperviseurs n'ont pas un accĂšs direct Ă  Internet. + * **Solution :** CrĂ©ation d'un rĂŽle `pve-proxy` qui configure le gestionnaire de paquets et l'API Proxmox pour forcer le passage par le proxy Squid de l'infrastructure (10.0.23.1:3128). +* **Proxy ignorĂ© par le SSO OIDC :** Grosse subtilitĂ© rĂ©seau : Proxmox ne prend pas en compte la configuration proxy pour envoyer ses requĂȘtes vers le fournisseur d'identitĂ© SSO OIDC[^8], ce qui faisait planter la connexion. + * **Solution :** Ajout d'une ACL[^9] sur le routeur autorisant spĂ©cifiquement les nƓuds (alias `HOST_PROXMOX`) Ă  joindre le SSO. Pour anticiper d'Ă©ventuels changements d'IP, la destination a Ă©tĂ© renseignĂ©e via l'alias de domaine `WEBSITE_SSO` afin que le routeur gĂšre la rĂ©solution DNS dynamiquement. + +--- + +[^1]: **Proxmox VE** - Solution open source de virtualisation permettant de crĂ©er et gĂ©rer des machines virtuelles et des conteneurs. +[^2]: **Cluster** - Groupe de serveurs (nƓuds) interconnectĂ©s qui fonctionnent ensemble comme un systĂšme unique pour assurer haute disponibilitĂ© et rĂ©partition de charge. +[^3]: **IaC (Infrastructure as Code)** - MĂ©thode consistant Ă  gĂ©rer et configurer des serveurs Ă  l'aide de fichiers de code (scripts) plutĂŽt que par des actions manuelles. +[^4]: **Ansible** - Outil d'automatisation informatique qui dĂ©ploie des logiciels et configure des systĂšmes en lisant des fichiers d'instructions appelĂ©s "playbooks". +[^5]: **systemd** - Gestionnaire de systĂšme et de services standard sous Linux, utilisĂ© ici pour dĂ©marrer le montage rĂ©seau en tĂąche de fond. +[^6]: **WebDAV** - Protocole web permettant de gĂ©rer des fichiers sur un serveur distant, souvent utilisĂ© pour transformer un stockage cloud en disque rĂ©seau local. +[^7]: **APT** - Le gestionnaire de paquets utilisĂ© par Debian (le systĂšme de base de Proxmox) pour tĂ©lĂ©charger, installer et mettre Ă  jour les logiciels. +[^8]: **SSO OIDC (Single Sign-On / OpenID Connect)** - SystĂšme d'authentification unique permettant Ă  un utilisateur de se connecter une seule fois pour accĂ©der en toute sĂ©curitĂ© Ă  plusieurs applications. +[^9]: **ACL (Access Control List)** - RĂšgle de sĂ©curitĂ© sur un routeur ou pare-feu qui dĂ©finit prĂ©cisĂ©ment qui a le droit de communiquer avec qui sur un rĂ©seau. \ No newline at end of file diff --git a/src/content/docs/homelab/hyperviseur/theorie-proxmox.md b/src/content/docs/homelab/hyperviseur/theorie-proxmox.md new file mode 100644 index 0000000..9e5f3fc --- /dev/null +++ b/src/content/docs/homelab/hyperviseur/theorie-proxmox.md @@ -0,0 +1,53 @@ +--- +title: "ThĂ©orie - Proxmox VE" +description: "Cours et concepts fondamentaux autour de Proxmox VE." +sidebar: + order: 1 +--- + +## A. PrĂ©sentation + +Proxmox Virtual Environment (VE) est une plateforme open-source de gestion de virtualisation d'entreprise. BasĂ©e sur Debian, elle unifie au sein d'une mĂȘme interface web la gestion des machines virtuelles (KVM), des conteneurs lĂ©gers (LXC), du stockage dĂ©fini par logiciel et de la configuration rĂ©seau. C'est la fondation qui permet de dĂ©couper un serveur physique en de multiples environnements isolĂ©s. + +:::note +Dans une infrastructure comme LoutikCLOUD, Proxmox VE agit comme le chef d'orchestre matĂ©riel. Il fournit la couche de calcul brute sur laquelle reposent ensuite les services, les routeurs virtuels et les orchestrateurs de plus haut niveau. +::: + +## B. ProblĂ©matiques rĂ©solues + +* **Consolidation matĂ©rielle et gestion unifiĂ©e :** Évite la prolifĂ©ration de serveurs physiques sous-utilisĂ©s. Proxmox rassemble la gestion des VM (lourdes) et des conteneurs (lĂ©gers) sous une seule API[^1] et interface web, optimisant drastiquement les ressources. +* **Haute disponibilitĂ©[^2] et rĂ©silience :** Face aux pannes matĂ©rielles (comme une dĂ©faillance de disque dur), Proxmox permet de lier plusieurs nƓuds en cluster. Si une machine tombe, les services peuvent migrer Ă  chaud vers un autre nƓud. + +## C. Fonctionnement + +La force de Proxmox VE rĂ©side dans sa fondation Debian standard enrichie d'un noyau personnalisĂ© et d'un systĂšme de fichiers distribuĂ©. Il ne s'agit pas d'une boĂźte noire, mais d'un assemblage robuste d'outils Linux Ă©prouvĂ©s. + +* **KVM (Kernel-based Virtual Machine) :** Hyperviseur permettant la virtualisation complĂšte. Il simule le matĂ©riel pour exĂ©cuter des systĂšmes invitĂ©s totalement isolĂ©s disposant de leur propre noyau (idĂ©al pour des routeurs ou des appliances spĂ©cifiques). +* **LXC (Linux Containers) :** Virtualisation au niveau du systĂšme d'exploitation. Les conteneurs partagent le noyau de l'hĂŽte, offrant des performances quasi-natives et une trĂšs faible consommation de RAM/CPU. +* **pmxcfs (Proxmox Cluster File System) :** SystĂšme de fichiers distribuĂ© (via Corosync) qui rĂ©plique instantanĂ©ment les fichiers de configuration sur l'ensemble des nƓuds d'un cluster, garantissant une cohĂ©rence d'Ă©tat. + +:::tip[Cluster Proxmox] +Un cluster regroupe plusieurs serveurs physiques (nƓuds) sous une seule interface pour qu'ils agissent ensemble. Le pilier central de ce regroupement est le **quorum**[^3]. C'est la rĂšgle de la majoritĂ© absolue : pour que le cluster accepte d'Ă©crire des donnĂ©es ou de modifier des configurations, plus de la moitiĂ© des nƓuds doivent ĂȘtre en ligne et d'accord. Cela protĂšge l'infrastructure contre le redoutĂ© "split-brain"[^4] en cas de coupure rĂ©seau. +::: + +## D. Exemples + +* **DĂ©ploiement de nƓuds d'orchestration :** Provisionnement rapide d'un ensemble de machines virtuelles pour faire tourner un cluster Kubernetes lĂ©ger (K3s), permettant de dĂ©ployer ensuite des applications conteneurisĂ©es. +* **Routeur virtuel et segmentation rĂ©seau :** HĂ©bergement d'une machine virtuelle OPNsense jouant le rĂŽle de pare-feu principal, gĂ©rant directement les interfaces virtuelles et isolant les flux via des VLANs[^5] directement depuis l'hyperviseur. + +## E. Bonnes pratiques + +| RĂšgle | CatĂ©gorie | Justification | +| :--- | :--- | :--- | +| **Isoler le rĂ©seau d'administration (VLAN dĂ©diĂ©)** | SĂ©curitĂ© | EmpĂȘche tout accĂšs non autorisĂ© Ă  l'interface web et Ă  l'API depuis les rĂ©seaux invitĂ©s ou utilisateurs, limitant drastiquement la surface d'attaque. | +| **Automatiser via Proxmox Backup Server (PBS)** | Maintenance | Garantit des sauvegardes incrĂ©mentales[^6], dĂ©dupliquĂ©es[^7] et chiffrĂ©es, avec des politiques de rĂ©tention strictes pour se prĂ©munir d'un sinistre majeur. | +| **PrivilĂ©gier LXC par dĂ©faut pour les services Linux** | Performance | RĂ©duit l'empreinte mĂ©moire et processeur (overhead[^8]) pour les applications standards (bases de donnĂ©es, serveurs web) par rapport Ă  des machines virtuelles complĂštes. | + +[^1]: **API** : Interface qui permet Ă  diffĂ©rents logiciels de communiquer et d'Ă©changer des donnĂ©es automatiquement. +[^2]: **Haute disponibilitĂ© (HA)** : CapacitĂ© d'une infrastructure Ă  rester en ligne et accessible, mĂȘme lorsqu'une panne physique survient. +[^3]: **Quorum** : Le nombre minimum de serveurs (la majoritĂ©) qui doivent ĂȘtre actifs et connectĂ©s pour prendre des dĂ©cisions fiables et valider le fonctionnement du cluster. +[^4]: **Split-brain** : "Cerveau divisĂ©". Une situation critique oĂč un cluster est coupĂ© en deux par une panne rĂ©seau et oĂč chaque moitiĂ© tente de prendre le contrĂŽle, risquant de corrompre les donnĂ©es. +[^5]: **VLAN** : RĂ©seau local virtuel. Une technologie rĂ©seau permettant de sĂ©parer et d'isoler logiquement plusieurs groupes de machines sur un mĂȘme cĂąble ou Ă©quipement physique. +[^6]: **Sauvegarde incrĂ©mentale** : MĂ©thode de sauvegarde qui ne copie que les fichiers ou les blocs de donnĂ©es ayant Ă©tĂ© modifiĂ©s depuis la sauvegarde prĂ©cĂ©dente, ce qui est trĂšs rapide. +[^7]: **DĂ©duplication** : MĂ©canisme qui repĂšre et supprime les donnĂ©es identiques copiĂ©es plusieurs fois afin d'Ă©conomiser massivement de l'espace de stockage sur les disques. +[^8]: **Overhead** : La consommation de ressources (CPU, RAM) purement liĂ©e au fonctionnement du systĂšme de virtualisation lui-mĂȘme, et non Ă  l'application qui tourne. \ No newline at end of file diff --git a/src/content/docs/homelab/index.md b/src/content/docs/homelab/index.md new file mode 100644 index 0000000..fcf32f6 --- /dev/null +++ b/src/content/docs/homelab/index.md @@ -0,0 +1,18 @@ +--- +title: Introduction +description: DĂ©couvrez l'architecture, les choix techniques et la logique de dĂ©ploiement de mon infrastructure Homelab. +sidebar: + order: 0 +--- + +## 🏠 Homelab + +Cette section documente l'intĂ©gralitĂ© de mon infrastructure personnelle, conçue selon les principes de l'ingĂ©nierie de fiabilitĂ© (SRE). L'objectif est de vous fournir une vision globale du laboratoire. Vous y retrouverez les fondements thĂ©oriques, les justifications de mes choix technologiques via des registres de dĂ©cisions d'architecture (ADR), ma logique de dĂ©ploiement, ainsi que mes stratĂ©gies d'orchestration et de sĂ©curisation des accĂšs extĂ©rieurs. + +## 📂 Organisation + +L'arborescence de cette documentation est pensĂ©e par couches logiques pour reflĂ©ter la construction de l'infrastructure : + +* **L'approche thĂ©orique:** État de l'art, analyse des besoins et explications dĂ©taillĂ©es des choix structurants. +* **L'architecture securitĂ©:** Topologie, schĂ©mas de flux, gestion des zones et exposition sĂ©curisĂ©e des services. +* **L'orchestration :** Gestion de la couche de virtualisation, sĂ©lection des orchestrateurs et automatisation du cycle de vie des applications. \ No newline at end of file diff --git a/src/content/docs/homelab/orchestrateur/assets/acces-cluster-kubernetes.png b/src/content/docs/homelab/orchestrateur/assets/acces-cluster-kubernetes.png new file mode 100644 index 0000000..906062a Binary files /dev/null and b/src/content/docs/homelab/orchestrateur/assets/acces-cluster-kubernetes.png differ diff --git a/src/content/docs/homelab/orchestrateur/choix-kubernetes.md b/src/content/docs/homelab/orchestrateur/choix-kubernetes.md new file mode 100644 index 0000000..56df74c --- /dev/null +++ b/src/content/docs/homelab/orchestrateur/choix-kubernetes.md @@ -0,0 +1,77 @@ +--- +title: "Choix - Kubernetes (K3s)" +description: "Explication et justification du choix technique de K3s et Kube-vip au sein de LoutikCLOUD." +sidebar: + order: 2 +--- + +## A. Contexte + +Au sein de l'infrastructure LoutikCLOUD, le besoin d'orchestrer nos conteneurs de maniĂšre fiable et rĂ©siliente est devenu critique. L'objectif est de dĂ©ployer un cluster Kubernetes hautement disponible sur nos trois hyperviseurs Proxmox. + +Afin de garantir cette rĂ©silience, l'architecture prĂ©voit trois nƓuds *Control Plane*[^1] (un sur chaque nƓud Proxmox pour assurer le *quorum*[^2]). Ces machines virtuelles disposent de ressources contraintes : 3 Go de RAM, 2 vCPU et 50 Go de stockage. Pour Ă©viter de saturer ces ressources vitales, aucun *pod*[^3] applicatif ne devra y ĂȘtre exĂ©cutĂ©. Enfin, l'accĂšs Ă  l'API du cluster doit ĂȘtre protĂ©gĂ© par une VIP[^4] (10.0.12.6, rĂ©solue via `api.prd.k3s.infra.loutik.fr`). Le dĂ©fi est de trouver un Ă©cosystĂšme lĂ©ger, capable de gĂ©rer cette VIP nativement, tout en restant simple Ă  installer et Ă  maintenir dans un environnement de type homelab. + +## B. Cahiers des charges + +| ID | Type | Exigence | Description | +| --- | --- | --- | --- | +| **[REQ-F01]** | Fonctionnel | Haute DisponibilitĂ© (Quorum) | La solution doit supporter un dĂ©ploiement multi-maĂźtres sur 3 nƓuds distincts pour tolĂ©rer la perte d'un hyperviseur. | +| **[REQ-F02]** | Fonctionnel | Gestion de la VIP API | Le systĂšme doit attribuer et basculer automatiquement l'adresse IP virtuelle (10.0.12.6) de l'API entre les nƓuds maĂźtres. | +| **[REQ-T01]** | Technique | Empreinte mĂ©moire rĂ©duite | Les composants doivent fonctionner de maniĂšre fluide et stable avec seulement 3 Go de RAM et 2 vCPU par nƓud. | +| **[REQ-T02]** | Technique | Isolation des charges | PossibilitĂ© d'appliquer un *Taint*[^5] sur les nƓuds maĂźtres pour interdire l'exĂ©cution de services applicatifs. | +| **[REQ-T03]** | Technique | SimplicitĂ© d'installation | Le dĂ©ploiement et la maintenance doivent ĂȘtre adaptĂ©s Ă  une gestion homelab, sans l'ingĂ©nierie d'un datacenter. | + +## C. Les solutions du marchĂ© + +### C.1. PrĂ©sentations des solutions + +#### C.1.1. K3s associĂ© Ă  Kube-vip + +* **PrĂ©sentation gĂ©nĂ©rale :** K3s est une distribution Kubernetes allĂ©gĂ©e et certifiĂ©e, créée par Rancher. Kube-vip est un outil rĂ©seau open-source fournissant de la haute disponibilitĂ©. +* **Fonctionnement :** K3s remplace les composants lourds par des alternatives lĂ©gĂšres (etcd optimisĂ© ou base relationnelle). Kube-vip s'exĂ©cute sous forme de composant statique pour diffuser la VIP via le protocole ARP, garantissant que l'IP pointe toujours vers un nƓud sain. +* **Profil :** IdĂ©al pour l'Edge computing, l'IoT et les architectures homelab exigeantes. + +#### C.1.2. Kubernetes Vanilla (Kubeadm) avec Keepalived & HAProxy + +* **PrĂ©sentation gĂ©nĂ©rale :** La distribution standard de Kubernetes, dĂ©ployĂ©e avec l'outil officiel Kubeadm, couplĂ©e Ă  des paquets Linux classiques de routage. +* **Fonctionnement :** Utilise tous les composants standards dans leur intĂ©gralitĂ©. La VIP est gĂ©rĂ©e en externe par un dĂ©mon Keepalived, et le trafic de l'API est rĂ©parti par un load-balancer HAProxy installĂ© sur chaque nƓud. +* **Profil :** OrientĂ© grandes entreprises avec des infrastructures massives et des Ă©quipes dĂ©diĂ©es. + +#### C.1.3. MicroK8s + +* **PrĂ©sentation gĂ©nĂ©rale :** Une distribution Kubernetes lĂ©gĂšre maintenue par Canonical (Ă©diteur d'Ubuntu). +* **Fonctionnement :** S'installe via le gestionnaire de paquets Snap et propose un systĂšme d'extensions intĂ©grĂ©es (add-ons) pour activer des fonctionnalitĂ©s comme la haute disponibilitĂ©. +* **Profil :** Stations de travail des dĂ©veloppeurs, appliances et dĂ©ploiements rapides. + +### C.2. Comparatifs des solutions + +| Exigence | K3s + Kube-vip | Kubernetes Vanilla + Keepalived | MicroK8s | +| --- | --- | --- | --- | +| **[REQ-F01 (HA)]** | ValidĂ© | ValidĂ© | ValidĂ© | +| **[REQ-F02 (VIP API)]** | ValidĂ© (Natif via Kube-vip) | ValidĂ© (Mais nĂ©cessite des services externes) | Évaluation (Moins standardisĂ© pour l'API) | +| **[REQ-T01 (LĂ©gĂšretĂ©)]** | ValidĂ© | Non validĂ© (Gourmand pour 3Go de RAM) | Évaluation (Performances correctes, mais liĂ© Ă  Snap) | +| **[REQ-T02 (Isolation)]** | ValidĂ© | ValidĂ© | ValidĂ© | +| **[REQ-T03 (SimplicitĂ©)]** | ValidĂ© | Non validĂ© (Maintenance trĂšs complexe) | ValidĂ© | + +## D. Solution proposĂ©e + +La solution proposĂ©e pour l'infrastructure est le duo **K3s couplĂ© Ă  Kube-vip**. + +Ce choix rĂ©pond de maniĂšre chirurgicale aux contraintes de LoutikCLOUD. L'installation de K3s est extrĂȘmement frugale, ce qui permet aux trois nƓuds *Control Plane* de fonctionner confortablement avec leurs ressources limitĂ©es. L'architecture distribuĂ©e sur nos trois serveurs Proxmox (PVE1, PVE2, et le nƓud de donnĂ©e PVE3) garantit un *quorum* robuste : la perte d'un hĂŽte n'entraĂźne aucune coupure de service. + +Pour la gestion de l'API, Kube-vip est configurĂ© pour se dĂ©ployer automatiquement lors de l'initialisation des maĂźtres. Il gĂšre l'adresse VIP `10.0.12.6` (`api.prd.k3s.infra.loutik.fr`) de maniĂšre autonome par une Ă©lection d'ARP gratuit. Si le nƓud maĂźtre actif devient indisponible, l'IP bascule sur un autre nƓud en quelques millisecondes, de façon transparente. + +Enfin, la flexibilitĂ© de K3s permet d'appliquer un *Taint* strict dĂšs le dĂ©ploiement. Cela garantit que nos deux nƓuds *Workers* (rĂ©partis sur PVE1 et PVE2) absorberont 100% de la charge applicative, prĂ©servant la stabilitĂ© du *Control Plane*. + +**Justification du rejet des solutions alternatives :** + +* **Kubernetes Vanilla + Keepalived / HAProxy :** RejetĂ© principalement pour sa lourdeur. Les composants standards de Kubernetes consommeraient une part trop importante des 3 Go de RAM disponibles. De plus, la gestion d'une VIP via Keepalived et HAProxy en dehors du cycle de vie du cluster ajoute une complexitĂ© opĂ©rationnelle inutile pour un homelab. +* **MicroK8s :** Bien qu'intĂ©ressant pour sa simplicitĂ©, sa forte dĂ©pendance Ă  l'Ă©cosystĂšme Snap le rend moins universel pour l'apprentissage et l'administration systĂšme brute. De plus, le couplage K3s/Kube-vip offre un contrĂŽle plus granulaire et standardisĂ© sur le rĂ©seau du *Control Plane*. + +--- + +[^1]: **Control Plane** : Le "cerveau" du cluster. Il prend les dĂ©cisions globales (oĂč planifier le travail, surveiller l'Ă©tat des machines) mais n'hĂ©berge pas les applications elles-mĂȘmes. +[^2]: **Quorum** : Le nombre minimum de serveurs actifs nĂ©cessaires (ici au moins 2 sur 3) pour que le systĂšme puisse valider des dĂ©cisions et continuer Ă  fonctionner correctement lors d'une panne matĂ©rielle. +[^3]: **Pod** : La plus petite unitĂ© de travail dĂ©ployable dans Kubernetes. C'est une "capsule" qui enveloppe un ou plusieurs conteneurs d'application (comme des conteneurs Docker). +[^4]: **VIP (Virtual IP)** : Une adresse IP flottante qui n'est pas rattachĂ©e physiquement Ă  une seule carte rĂ©seau. Elle peut basculer automatiquement d'un serveur Ă  l'autre en cas de problĂšme, assurant que le service reste joignable. +[^5]: **Taint (Tache)** : Une rĂšgle de configuration appliquĂ©e sur un serveur qui repousse les *Pods*, leur interdisant de s'y installer. Cela permet de rĂ©server ce serveur uniquement aux processus essentiels du systĂšme (comme le *Control Plane*). \ No newline at end of file diff --git a/src/content/docs/homelab/orchestrateur/pratique-kubernetes.md b/src/content/docs/homelab/orchestrateur/pratique-kubernetes.md new file mode 100644 index 0000000..848e24b --- /dev/null +++ b/src/content/docs/homelab/orchestrateur/pratique-kubernetes.md @@ -0,0 +1,96 @@ +--- +title: "Pratique - Kubernetes (k3s)" +description: "DĂ©ploiement, architecture et retour d'expĂ©rience sur l'intĂ©gration de Kubernetes (k3s) dans l'infrastructure LoutikCLOUD." +sidebar: + order: 3 +--- + +## A. Contexte + +L'orchestration de conteneurs est devenue incontournable pour garantir la scalabilitĂ© et la maintenabilitĂ© d'une infrastructure moderne. Dans le cadre de LoutikCLOUD, j'ai fait le choix d'intĂ©grer Kubernetes via sa distribution allĂ©gĂ©e K3s. L'objectif est de disposer d'une plateforme robuste, capable d'hĂ©berger des services (comme LibreSpeed) tout en appliquant les principes DevSecOps Ă  la lettre : automatisation complĂšte, gestion dĂ©clarative de l'Ă©tat via Git, et sĂ©curitĂ© "by design". + +:::note[Bonnes Pratiques de SĂ©curitĂ©] +J'applique systĂ©matiquement le principe du moindre privilĂšge sur ce cluster : configuration des `securityContext` pour exĂ©cuter les pods avec un utilisateur non-root, dĂ©sactivation de l'accĂšs direct aux tokens d'API pour les pods qui n'en ont pas besoin, et mise en place systĂ©matique de sondes de vie (`livenessProbe` et `readinessProbe`). +::: + +## B. Architecture + +Pour comprendre comment la magie opĂšre, plongeons sous le capot. L'architecture de cette infrastructure repose sur trois piliers fondamentaux : la haute disponibilitĂ© des nƓuds maĂźtres, la gestion dĂ©clarative avec ArgoCD, et la sĂ©curisation des donnĂ©es sensibles. + +### 1. Les control planes (Haute disponibilitĂ©) + +Pour garantir une haute disponibilitĂ© sans point de dĂ©faillance unique, j'ai optĂ© pour une topologie basĂ©e sur trois nƓuds Control Plane[^1] (`mlt1-k3sc-vm-prd-01`, `02` et `03`). Cette configuration permet d'assurer un quorum[^2] via un consensus Raft[^6] natif Ă  K3s. Ainsi, si une machine tombe, le cluster continue de fonctionner normalement. + +L'accĂšs Ă  l'API Kubernetes n'est pas liĂ© Ă  l'IP d'un seul nƓud, mais est gĂ©rĂ© par une IP virtuelle (VIP) via un DaemonSet[^3] nommĂ© `kube-vip`. Ce dernier s'occupe de faire basculer l'adresse `10.0.12.6` d'un nƓud Ă  l'autre en cas de panne. + +![AccĂšs au cluster Kuberntes](./assets/acces-cluster-kubernetes.png) +*AccĂšs au cluster Kubernetes (illustration de la VIP)* + +Ce schĂ©ma illustre les deux flux d'accĂšs principaux au cluster, qui convergent vers l'IP virtuelle (VIP `10.0.12.6`) portĂ©e par Kube-vip : + +* **Le trafic externe (Web/HTTP) :** Les requĂȘtes des utilisateurs provenant d'Internet arrivent d'abord sur le VPS qui hĂ©berge le Loadbalancer NGINX frontal (`infgva1-edge-vm-prd-01`). Celui-ci agit comme un proxy et relaie le trafic vers la VIP du cluster Kubernetes. +* **Le trafic interne (API) :** Les administrateurs (pour la gestion via `kubectl`) et les nƓuds workers (pour communiquer avec le cluster) interrogent directement cette mĂȘme VIP. + +Kube-vip redirige ensuite ces requĂȘtes de maniĂšre transparente vers le nƓud Control Plane actif (le Leader) parmi les trois disponibles, assurant ainsi un accĂšs hautement disponible tant pour les applications hĂ©bergĂ©es que pour l'administration. + +### 2. ArgoCD et le modĂšle "App of Apps" + +Dans une approche GitOps pure, le cluster doit ĂȘtre capable de s'auto-configurer Ă  partir d'un dĂ©pĂŽt Git. Pour cela, j'utilise ArgoCD couplĂ© au puissant modĂšle "App of Apps". + +L'idĂ©e est simple mais redoutablement efficace : au lieu de dĂ©clarer manuellement chaque application dans ArgoCD, on dĂ©clare une seule "Application racine" (`bootstrap/app-of-apps.yml`). Cette application pointe vers un dossier de notre dĂ©pĂŽt Git (`bootstrap/apps/`) qui contient la dĂ©finition de toutes les autres applications (LibreSpeed, Homepage, etc.). Si je veux ajouter un nouveau service, je dĂ©pose juste un fichier YAML dans ce dossier, et ArgoCD s'occupe du reste. + +* DĂ©pĂŽt ArgoCD : [infrastructure-argocd](https://github.com/loutik/infrastructure-argocd) +* Article de Stephane Robert sur ArgoCD : [ArgoCD, Concepts et architecture](https://blog.stephane-robert.info/docs/pipeline-cicd/argocd/concepts/) + +```text + [ DĂ©pĂŽt Git : infrastructure-argocd ] + | + v + +-----------------------------------+ + | Application Racine (ArgoCD) | <-- bootstrap/app-of-apps.yml + +-----------------------------------+ + | (Scrute et gĂ©nĂšre) + v + +-----------------------------------+ + | Applications Enfants | + | - apps/librespeed.yml | <-- Pointe vers le dossier de l'app + | - apps/homepage.yml | + | - infra/traefik-config.yml | + +-----------------------------------+ + | (Synchronise les manifests) + v + [ Cluster Kubernetes ] + (Deployments, Services...) +``` + +### 3. Gestion des secrets + +Le plus gros dĂ©fi du GitOps est la gestion des mots de passe. Il est hors de question de *commit* des secrets en clair sur GitHub. C'est lĂ  qu'intervient **Sealed Secrets**. + +Le principe repose sur le chiffrement asymĂ©trique[^7]. Un contrĂŽleur tourne dans mon cluster K3s et possĂšde une clĂ© privĂ©e. Sur ma machine locale, j'utilise l'utilitaire `kubeseal` avec la clĂ© publique correspondante pour chiffrer mon secret. Le rĂ©sultat est un objet `SealedSecret` que je peux pousser sereinement sur Git. Une fois synchronisĂ© par ArgoCD, seul le contrĂŽleur K3s est capable de le dĂ©chiffrer pour recrĂ©er un secret natif Kubernetes utilisable par mes pods. + +## C. DĂ©ploiement + +Le dĂ©ploiement de l'infrastructure et des applications repose entiĂšrement sur l'automatisation et le paradigme GitOps[^4]. + +* **Provisionnement Ansible :** Comme pour Proxmox, j'ai scindĂ© l'initialisation. Un premier playbook (`k3sc-cluster-init.yml`) amorce le nƓud leader. Une fois ce dernier prĂȘt, un second playbook (`k3sc-init.yml`) fait rejoindre les deux autres maĂźtres. L'API est exposĂ©e sur la VIP `10.0.12.6` attachĂ©e Ă  l'interface `eth0`, avec une rĂ©solution DNS HA (`api.prd.k3s.infra.loutik.fr`) intĂ©grĂ©e aux certificats K3s (`tls-san`). +* **GitOps avec ArgoCD :** L'entiĂšretĂ© des dĂ©ploiements applicatifs est gĂ©rĂ©e par ArgoCD, s'appuyant sur l'infrastructure dĂ©taillĂ©e plus haut (visible sur [le dĂ©pĂŽt GitHub](https://github.com/loutik/infrastructure-argocd)). +* **Gestion des Manifests et Secrets :** Chaque application respecte un triptyque strict : Service, Ingress (gĂ©rĂ© par Traefik), et Deployment. J'utilise occasionnellement Kustomize pour gĂ©nĂ©rer dynamiquement des ConfigMaps as code depuis des fichiers bruts, Ă©vitant ainsi de monter des volumes en dur. Les donnĂ©es sensibles sont gĂ©rĂ©es exclusivement via `kubeseal`. + +## D. DifficultĂ©s + +Lors de la mise en place, quelques ajustements ont Ă©tĂ© nĂ©cessaires : + +* **Le timing de provisionnement Ansible :** Lors du lancement des actions post-installation, Ansible tentait de joindre le cluster avant qu'il ne soit pleinement opĂ©rationnel. Pour rĂ©soudre cela, j'ai ajoutĂ© une tĂąche utilisant le module `ansible.builtin.wait_for` dans mon rĂŽle `k3s-controle-plane`. Cela force Ansible Ă  patienter activement (jusqu'Ă  300 secondes) que le port 6443 de l'IP virtuelle (10.0.12.6) rĂ©ponde correctement avant de continuer. +* **Le proxy systĂšme K3s :** Mes nƓuds n'ayant pas d'accĂšs direct Ă  internet, j'ai dĂ» injecter les variables `HTTP_PROXY` et `HTTPS_PROXY` dans le service systemd de K3s pour qu'il puisse tirer ses images via mon Squid. Il a fallu prendre grand soin d'exclure les plages locales et la VIP via la directive `NO_PROXY` pour ne pas casser le rĂ©seau interne du cluster. +* **Le Reverse Proxy et les erreurs 404 K8s :** Quand un pod tombait, l'utilisateur avait une page 404 brute de Traefik/K8s au lieu de ma jolie page d'erreur personnalisĂ©e. J'ai dĂ©couvert et ajoutĂ© l'argument `proxy_intercept_errors on;` dans mon rĂŽle Ansible Nginx (via la variable `nginx_vhosts`). Cela permet au Nginx frontal d'intercepter les codes d'erreur de Kubernetes et de servir correctement ma page customisĂ©e. + +--- + +[^1]: **Control Plane** - Le "cerveau" d'un cluster Kubernetes, regroupant les composants qui prennent les dĂ©cisions globales (planification, gestion de l'Ă©tat) et rĂ©pondent aux Ă©vĂ©nements du cluster. +[^2]: **Quorum** - Le nombre minimum de nƓuds devant ĂȘtre actifs et connectĂ©s pour que le cluster puisse prendre des dĂ©cisions fiables, Ă©vitant ainsi les problĂšmes de "split-brain". +[^3]: **DaemonSet** - Un objet Kubernetes qui s'assure qu'une copie d'un Pod spĂ©cifique s'exĂ©cute sur tous les nƓuds (ou une sĂ©lection de nƓuds) du cluster. +[^4]: **GitOps** - Une mĂ©thodologie de gestion d'infrastructure oĂč un dĂ©pĂŽt Git est utilisĂ© comme source unique de vĂ©ritĂ©. Les modifications poussĂ©es sur Git sont automatiquement rĂ©pliquĂ©es sur l'infrastructure. +[^5]: **Sealed Secrets** - Un outil permettant de chiffrer des donnĂ©es sensibles (mots de passe, tokens) de maniĂšre asymĂ©trique pour pouvoir les versionner publiquement en toute sĂ©curitĂ© sur Git sans fuite de donnĂ©es. +[^6]: **Raft** - Un algorithme de consensus distribuĂ© utilisĂ© pour s'assurer que tous les nƓuds du Control Plane partagent exactement la mĂȘme vision de l'Ă©tat du cluster, mĂȘme en cas de panne rĂ©seau. +[^7]: **Chiffrement asymĂ©trique** - Une mĂ©thode de cryptographie utilisant une paire de clĂ©s : une clĂ© publique pour chiffrer (verrouiller) la donnĂ©e, et une clĂ© privĂ©e distincte pour la dĂ©chiffrer (dĂ©verrouiller). \ No newline at end of file diff --git a/src/content/docs/homelab/orchestrateur/theorie-kubernetes.md b/src/content/docs/homelab/orchestrateur/theorie-kubernetes.md new file mode 100644 index 0000000..2871a6f --- /dev/null +++ b/src/content/docs/homelab/orchestrateur/theorie-kubernetes.md @@ -0,0 +1,44 @@ +--- +title: "ThĂ©orie - Kubernetes" +description: "Cours et concepts fondamentaux autour de Kubernetes." +sidebar: + order: 1 +--- + +## A. PrĂ©sentation + +Kubernetes (souvent abrĂ©gĂ© K8s) est un orchestrateur de conteneurs[^1]. Son but principal est d'automatiser le dĂ©ploiement, la gestion et la mise Ă  l'Ă©chelle des applications conteneurisĂ©es. Au lieu de gĂ©rer chaque conteneur manuellement sur chaque serveur, vous confiez vos applications Ă  Kubernetes, qui se charge de les faire tourner de maniĂšre fiable, mĂȘme en cas de panne matĂ©rielle. + +:::note +Dans un environnement comme LoutikCLOUD ou un homelab, nous utilisons souvent des distributions allĂ©gĂ©es (comme K3s) pour faire tourner nos clusters de maniĂšre optimisĂ©e, tout en conservant l'intĂ©gralitĂ© des fonctionnalitĂ©s standards de Kubernetes. +::: + +## B. ProblĂ©matiques rĂ©solues + +* **La tolĂ©rance aux pannes (Haute DisponibilitĂ©) :** Si un serveur physique vient Ă  s'Ă©teindre ou Ă  crasher, Kubernetes dĂ©tecte la perte et redĂ©marre instantanĂ©ment les applications concernĂ©es sur un autre serveur sain, sans intervention humaine. +* **La mise Ă  l'Ă©chelle dynamique (Scalability) :** Face Ă  un pic de trafic inattendu, l'outil peut multiplier instantanĂ©ment les instances de l'application (les Pods[^2]) pour absorber la charge, puis les dĂ©truire automatiquement lorsque l'activitĂ© redevient normale. + +## C. Fonctionnement + +Kubernetes fonctionne sur une architecture dĂ©clarative maĂźtre/ouvrier. L'administrateur ne donne pas une suite de commandes Ă  exĂ©cuter, mais dĂ©clare l'Ă©tat final souhaitĂ© (via des fichiers YAML). Le systĂšme observe l'Ă©tat actuel et agit en permanence pour qu'il corresponde Ă  l'Ă©tat souhaitĂ©. + +* **Control Plane (Cerveau) :** C'est le centre de contrĂŽle du cluster. Il prend les dĂ©cisions globales (comme choisir sur quel serveur lancer un Pod) et dĂ©tecte les anomalies. +* **Worker Node (Ouvrier) :** C'est un serveur (physique ou machine virtuelle) qui exĂ©cute rĂ©ellement vos charges de travail. +* **Kubelet :** C'est le petit agent logiciel installĂ© sur chaque Node. Il Ă©coute les ordres du Control Plane et s'assure que les conteneurs tournent correctement sur sa machine. + +## D. Exemples + +* **DĂ©ploiement sans coupure (Rolling Update) :** Mettre Ă  jour le code d'une application (ex: MyOpsAPI) vers une version plus rĂ©cente sans que les utilisateurs ne s'en rendent compte. Kubernetes va remplacer les anciens conteneurs un par un par les nouveaux, en s'assurant qu'ils rĂ©pondent avant de couper les anciens. +* **RĂ©silience locale :** HĂ©berger un reverse proxy et une base de donnĂ©es sur son infrastructure. Si le disque dur du serveur principal lĂąche, le cluster migre les services sur le serveur de secours en quelques secondes. + +## E. Bonnes pratiques + +| RĂšgle | CatĂ©gorie | Justification | +| :--- | :--- | :--- | +| **Fixer les limites de ressources (Requests/Limits)** | Performance | EmpĂȘche un conteneur mal codĂ© ou gourmand de consommer toute la RAM/CPU et de faire planter l'intĂ©gralitĂ© du nƓud (effet "Noisy Neighbor"). | +| **Principe du moindre privilĂšge (Non-Root)** | SĂ©curitĂ© | ExĂ©cuter les conteneurs avec un utilisateur standard. En cas de faille de sĂ©curitĂ© dans l'application, l'attaquant n'aura pas les droits d'administrateur sur le serveur hĂŽte. | +| **Approche GitOps[^3]** | Maintenance | Stocker absolument tous les manifestes YAML dans un dĂ©pĂŽt Git. Toute modification de l'infrastructure est ainsi tracĂ©e, versionnĂ©e et facilement rĂ©versible en cas d'erreur. | + +[^1]: **Conteneur :** Une enveloppe logicielle standardisĂ©e (comme Docker) qui contient une application et toutes ses dĂ©pendances, garantissant qu'elle fonctionnera de la mĂȘme maniĂšre sur n'importe quel ordinateur. +[^2]: **Pod :** La plus petite unitĂ© de calcul gĂ©rĂ©e par Kubernetes. Un Pod enveloppe gĂ©nĂ©ralement un seul conteneur (parfois plusieurs s'ils sont trĂšs liĂ©s) et partage son stockage et son adresse rĂ©seau. +[^3]: **GitOps :** Pratique consistant Ă  utiliser Git comme source unique de vĂ©ritĂ© pour l'infrastructure. Des outils (comme FluxCD) scrutent le dĂ©pĂŽt Git et appliquent automatiquement les changements sur le cluster. \ No newline at end of file diff --git a/src/content/docs/homelab/presentation-homelab.mdx b/src/content/docs/homelab/presentation-homelab.mdx new file mode 100644 index 0000000..5480b3e --- /dev/null +++ b/src/content/docs/homelab/presentation-homelab.mdx @@ -0,0 +1,100 @@ +--- +title: PrĂ©sentation du homelab +description: PrĂ©sentation de loutikHOMELAB, le matĂ©riel, l'architecture et les outils DevSecOps utilisĂ©s au quotidien. +sidebar: + order: 0 +--- + +## Bienvenue chez loutikHOMELAB ! 🚀 + +Salut Ă  toutes et Ă  tous, et bienvenue Ă  bord de l'aventure Loutik ! Dans cet article, on va faire un petit tour d'horizon du matĂ©riel, de l'architecture et des outils utilisĂ©s pour faire tourner ce homelab (qui est, comme tout bon homelab, en perpĂ©tuelle construction). + +:::tip[En bref] +**loutikHOMELAB**, c'est : +- **De la haute disponibilitĂ©** : minimum deux nƓuds par service critique. +- **Une architecture hybride** : un mix entre Cloud et *On-Premise*. +- **Du GitOps & IaC** : tout est gĂ©rĂ© via du code avec Ansible et ArgoCD. +- **Des bonnes pratiques** : plan de nommage strict, rĂ©fĂ©rentiels propres, principe du moindre privilĂšge et segmentation rĂ©seau (ACLs). +::: + +![Photographie du Homelab](./assets/photo-homelab-loutik.jpg) +*Photographie du Homelab.* + +## Pourquoi avoir montĂ© ce homelab ? + +Pour moi, je considĂšre le homelab comme un jeu vidĂ©o dans lequel je peux passer 11 heures par jour sans voir le temps filer. Sauf qu'au lieu de tuer des monstres sur Minecraft, je me casse la tĂȘte pour comprendre pourquoi mon pod Kubernetes redĂ©marre en boucle, ou pourquoi mon application fraĂźchement dĂ©ployĂ©e (et prĂ©tendument en statut *Healthy*) reste injoignable... Bref, un vĂ©ritable jeu de rĂ©flexion et de rĂ©solution de problĂšmes qui me pousse, jour aprĂšs jour, Ă  amĂ©liorer mes services et mon architecture. + +Au-delĂ  de l'aspect purement ludique et de la passion, ce laboratoire grandeur nature me permet de documenter des projets concrets. C'est un excellent moyen de dĂ©montrer mes compĂ©tences en **administration systĂšme** et **DevSecOps**, ainsi que ma mĂ©thodologie de travail, avec de vrais cas d'usage. + +--- + +## ConcrĂštement, il y a quoi sous le capot ? + +Le projet s'appuie sur les standards de l'industrie Cloud et DevSecOps. J'organise par exemple mon travail avec une approche **Agile** pour garder le cap (visualisation de l'avancement, priorisation des tĂąches, historique des actions). + +:::tip[Les dĂ©pĂŽts GIT du homelab] +[Github Loutik Repositories](https://github.com/orgs/loutik/repositories) +::: + +L'infrastructure (*LoutikCLOUD* pour les intimes) est dĂ©coupĂ©e en plusieurs couches logiques. Voici comment tout cela est structurĂ© : + +### 1. La documentation + +Parce qu'une infra sans documentation est une infra qu'on reconstruit tous les 2 mois de zĂ©ro, j'y accorde une importance capitale : + +- **Standard Diataxis** : J'utilise la mĂ©thodologie [Diataxis](https://diataxis.fr/) pour structurer l'information de maniĂšre claire et orientĂ©e utilisateur. +- **ADR (Architecture Decision Records)** : Chaque choix technologique majeur est documentĂ© et justifiĂ©. +- **Segmentation logique** : La documentation est divisĂ©e en catĂ©gories (`architecture` pour les choix techniques, `infrastructure core` pour les bases, `edge` pour le routage type LoadBalancer/Reverse Proxy, `securite` pour le WAF/IDS, et `services` pour les applications hĂ©bergĂ©es comme Authentik ou BentoPDF). +- **Documentation de l'infrastructure :** [Documentation infrastructure - LoutikCLOUD](https://docs-infra.loutik.fr) + +### 2. L'Architecture rĂ©seau + +L'infrastructure n'est pas exposĂ©e directement. Elle repose sur un rĂ©seau segmentĂ© et sĂ©curisĂ© via des ACLs. + +![Flux rĂ©seaux des utilisateurs externes](./assets/flux-reseaux-utilisateur-externe.png) +*Flux rĂ©seaux des utilisateurs externes.* + +**LĂ©gende des Ă©quipements :** + +* **edge1 :** VPS Cloud (hĂ©bergĂ© chez Infomaniak). C'est le point d'entrĂ©e public qui fait office de front pour l'infrastructure. +* **Tunnel VPN :** La liaison sĂ©curisĂ©e qui permet au VPS de communiquer avec le rĂ©seau local. +* **Pare-feu :** SĂ©curitĂ© du rĂ©seau local, route les paquets et filtre les menaces via l'IPS. +* **Switch :** Le commutateur qui s'occupe de distribuer le trafic physiquement et de gĂ©rer l'isolation rĂ©seau via les VLANs. +* **pve1, pve2, pve3 :** Les alias de nos trois nƓuds physiques Proxmox, connectĂ©s au switch, qui portent toute la virtualisation du lab. + +### 3. MatĂ©riel, virtualisation et automatisation + +Pour hĂ©berger tout ça, j'utilise une flotte de machines gĂ©rĂ©es comme du "bĂ©tail" (concept *Cattle vs Pets*) grĂące Ă  Proxmox et Ansible. + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + + + + - **Pare-feu (OPNsense)** : Un valeureux *HP T730 thin client*. Il tourne avec un processeur AMD GX-420CA (4 cƓurs / 4 threads), 8 Go de RAM, et une carte PCIe ajoutĂ©e offrant deux ports Ethernet 1Gb/s pour faire le pont entre l'extĂ©rieur et l'intĂ©rieur. + - **Commutateur (Switch)** : Un *Zyxel GS1200-8* avec 8 ports 1Gb/s. C'est lui qui distribue le trafic et gĂšre l'isolation (VLANs et trunk). L'agrĂ©gation de lien est dispo, mais je ne l'utilise pas encore sur ce lab. + + + + Pour la puissance de calcul, le cluster s'appuie sur trois machines physiques distinctes : + - **Deux nƓuds HP Prodesk 400 G3** : ÉquipĂ©s d'un processeur Intel i5-6500 (4 cƓurs), 32 Go de RAM, un SSD NVMe de 256 Go pour l'OS, et un HDD de 500 Go pour stocker les VMs. + - **Un nƓud Dell Inspiron 15 3225** : Un laptop avec son Ryzen 5 5500U (12 cƓurs !), 16 Go de RAM, un SSD NVMe de 256 Go, et un HDD de 500 Go. + + + +### 4. Conteneurisation + +Le cƓur applicatif de *LoutikCLOUD* repose sur Kubernetes, et plus particuliĂšrement sur **K3s**, une distribution lĂ©gĂšre et parfaitement taillĂ©e pour le homelab. + +Pour garantir la rĂ©silience de l'infrastructure, le cluster est dĂ©coupĂ© stratĂ©giquement : +- **3 nƓuds Control Plane** : Ils gĂšrent le cerveau du cluster et assurent le quorum (si l'un tombe, les deux autres prennent le relai sans sourciller). +- **2 nƓuds Worker** : ComplĂštement sĂ©parĂ©s des plans de contrĂŽle, ce sont les petits ouvriers qui font tourner les applications au quotidien. + +CĂŽtĂ© dĂ©ploiement, on oublie les interventions manuelles et les commandes `kubectl apply` tapĂ©es Ă  la hĂąte. L'ensemble est gĂ©rĂ© selon les principes du **GitOps** grĂące Ă  **ArgoCD**. Le principe est super reposant : mes dĂ©pĂŽts Git sont la source unique de vĂ©ritĂ©. DĂšs que je pousse une modification dans un manifeste, ArgoCD s'en aperçoit et synchronise automatiquement le cluster pour qu'il reflĂšte exactement le code. C'est propre, auditable, et ça Ă©vite pas mal de sueurs froides ! + +:::tip[Haute DisponibilitĂ© (HA) des Control Planes] +Pour pousser la rĂ©silience jusqu'au bout, j'ai mis en place une **VIP (Virtual IP)**. Le principe est simple : si le nƓud maĂźtre principal tombe, cette adresse IP bascule automatiquement sur un autre nƓud sain. Tout le monde communique via cette adresse (`10.0.12.6`) ou via son entrĂ©e DNS (`vip.prd.k3s.infra.loutik.fr`), ce qui rend la panne d'un hĂŽte totalement transparente. +::: + +--- + +Et voilĂ  pour ce grand tour du propriĂ©taire ! Le projet Ă©volue constamment au fil de mes apprentissages et des nouvelles technos que j'ai envie d'expĂ©rimenter. N'hĂ©sitez pas Ă  parcourir le reste de la documentation pour voir tout ça plus en dĂ©tail. \ No newline at end of file diff --git a/src/content/docs/homelab/reverse-proxy/choix-nginx.md b/src/content/docs/homelab/reverse-proxy/choix-nginx.md new file mode 100644 index 0000000..ec3b0f9 --- /dev/null +++ b/src/content/docs/homelab/reverse-proxy/choix-nginx.md @@ -0,0 +1,76 @@ +--- +title: "Choix - NGINX" +description: "Explication et justification du choix technique de NGINX au sein de LoutikCLOUD." +sidebar: + order: 2 +--- + +## A. Contexte + +Au sein de l'infrastructure LoutikCLOUD, l'accĂšs depuis l'extĂ©rieur prĂ©sentait un dĂ©fi architectural majeur. Mon accĂšs Internet domestique repose sur une adresse IP publique dynamique, ce qui rend la joignabilitĂ© incertaine. De plus, exposer l'infrastructure nĂ©cessitait une double redirection de ports complexes : depuis la box opĂ©rateur vers le pare-feu OPNsense, puis vers le reverse proxy interne. + +Pour professionnaliser l'accĂšs et contourner ces limitations, j'ai fait le choix d'acquĂ©rir un VPS[^1] chez Infomaniak. Ce serveur dans le cloud agit comme un point d'entrĂ©e externe avec une adresse IP publique fixe et dĂ©diĂ©e. Il me fallait donc dĂ©ployer sur ce VPS un reverse proxy capable d'encaisser le trafic, de le filtrer, puis de le router de maniĂšre sĂ©curisĂ©e vers mon infrastructure locale (derriĂšre OPNsense). Ce composant critique se devait d'ĂȘtre lĂ©ger, hautement automatisable et taillĂ© pour la sĂ©curitĂ© moderne. + +## B. Cahiers des charges + +| ID | Type | Exigence | Description | +| :--- | :--- | :--- | :--- | +| **[REQ-F01]** | Fonctionnel | Pages d'erreur personnalisĂ©es | CapacitĂ© Ă  servir des pages web statiques (HTML/CSS) sur-mesure pour les codes d'erreur HTTP (404, 403, 502). | +| **[REQ-F02]** | Fonctionnel | Support HTTP/3 | Prise en charge native du protocole HTTP/3[^5] pour garantir des temps de rĂ©ponse optimaux aux utilisateurs. | +| **[REQ-T01]** | Technique | CompatibilitĂ© WAF CrowdSec | IntĂ©gration fluide avec le moteur de dĂ©tection et de remĂ©diation CrowdSec[^3] pour bloquer les IP malveillantes. | +| **[REQ-T02]** | Technique | Provisionnement Ansible | Fichiers de configuration lisibles et facilement dĂ©ployables via des playbooks Ansible[^4]. | +| **[REQ-T03]** | Technique | Empreinte minimale | Consommation trĂšs faible de la mĂ©moire (RAM) et du processeur (CPU) pour ne pas surcharger le VPS. | + +## C. Les solutions du marchĂ© + +### C.1. PrĂ©sentations des solutions + +#### C.1.1. NGINX +* **PrĂ©sentation gĂ©nĂ©rale :** Serveur web et reverse proxy open-source, devenu le standard de l'industrie pour les architectures modernes et cloud-native. +* **Fonctionnement :** Repose sur une architecture asynchrone et Ă©vĂ©nementielle, lui permettant de gĂ©rer des milliers de connexions avec trĂšs peu de ressources. +* **Profil :** OrientĂ© DevOps, idĂ©al pour les infrastructures nĂ©cessitant de la haute performance, du load balancing et une forte personnalisation via des fichiers de configuration clairs. + +#### C.1.2. HAProxy +* **PrĂ©sentation gĂ©nĂ©rale :** Solution open-source ultra-spĂ©cialisĂ©e dans la rĂ©partition de charge (Load Balancing) et le proxying TCP/HTTP. +* **Fonctionnement :** Conçu pour maximiser la disponibilitĂ© et les performances rĂ©seau, il analyse et dispatche le trafic avec une prĂ©cision chirurgicale. +* **Profil :** Architectures d'entreprise Ă  trĂšs haut trafic oĂč la fiabilitĂ© absolue du routage est la prioritĂ©, souvent utilisĂ© en amont d'autres serveurs web. + +#### C.1.3. Caddy +* **PrĂ©sentation gĂ©nĂ©rale :** Serveur web moderne Ă©crit en Go, cĂ©lĂšbre pour sa gestion automatique des certificats HTTPS (Let's Encrypt). +* **Fonctionnement :** Met l'accent sur la simplicitĂ© "out-of-the-box" (prĂȘt Ă  l'emploi) avec un fichier de configuration (`Caddyfile`) extrĂȘmement minimaliste. +* **Profil :** DĂ©veloppeurs et administrateurs cherchant Ă  dĂ©ployer des services rapidement sans se soucier de la gestion des certificats SSL/TLS. + +#### C.1.4. Apache +* **PrĂ©sentation gĂ©nĂ©rale :** Le serveur web historique, extrĂȘmement modulaire et robuste, pilier de l'Internet depuis les annĂ©es 90. +* **Fonctionnement :** Fonctionne historiquement de maniĂšre synchrone (un processus/thread par connexion), bien qu'il puisse ĂȘtre optimisĂ©. Il gĂšre de nombreux modules dynamiques. +* **Profil :** HĂ©bergement mutualisĂ© traditionnel, applications nĂ©cessitant des rĂšgles `.htaccess` spĂ©cifiques ou des architectures Legacy. + +### C.2. Comparatifs des solutions + +| Exigence | NGINX | HAProxy | Caddy | Apache | +| :--- | :--- | :--- | :--- | :--- | +| **[REQ-F01 (Pages sur-mesure)]** | ValidĂ© (Natif et trĂšs simple) | Évaluation (Possible mais moins adaptĂ© pour du statique) | ValidĂ© | ValidĂ© | +| **[REQ-F02 (Support HTTP/3)]** | ValidĂ© (Module officiel) | ValidĂ© | ValidĂ© (Natif par dĂ©faut) | Évaluation (ExpĂ©rimental/Complexe) | +| **[REQ-T01 (CrowdSec)]** | ValidĂ© (Bouncer officiel mature) | ValidĂ© (Bouncer officiel) | ValidĂ© (Bouncer officiel rĂ©cent) | ValidĂ© (Bouncer officiel) | +| **[REQ-T02 (Ansible)]** | ValidĂ© (ÉcosystĂšme DevOps massif) | ValidĂ© | Évaluation (Configuration parfois "trop magique" Ă  templater) | ValidĂ© (Mais syntaxe lourde) | +| **[REQ-T03 (LĂ©gĂšretĂ©)]** | ValidĂ© | ValidĂ© (Excellent) | ValidĂ© | Non validĂ© (Plus gourmand) | + +## D. Solution proposĂ©e + +La solution proposĂ©e pour l'infrastructure est **NGINX**. + +DĂ©ployĂ© directement sur le VPS Infomaniak, NGINX s'intĂšgre parfaitement comme bouclier frontal pour LoutikCLOUD. Son intĂ©gration est entiĂšrement automatisĂ©e via Ansible (rĂŽles et templates Jinja2), ce qui garantit une reproductibilitĂ© totale de la configuration. Le trafic externe arrive sur l'IP publique fixe du VPS, est inspectĂ© par le *bouncer* CrowdSec intĂ©grĂ© Ă  NGINX, puis, s'il est lĂ©gitime, est acheminĂ© de maniĂšre sĂ©curisĂ©e vers mon OPNsense local. De plus, sa capacitĂ© native Ă  agir comme un serveur web statique me permet de servir des pages d'erreurs Ă©lĂ©gantes et brandĂ©es "LoutikCLOUD" sans solliciter l'infrastructure backend. Enfin, la prise en charge de HTTP/3 assure une navigation fluide aux utilisateurs. + +**Justification du rejet des solutions alternatives :** + +* **Apache :** Son architecture historique basĂ©e sur les processus est beaucoup trop gourmande en ressources pour le simple rĂŽle de reverse proxy sur un petit VPS. Sa syntaxe de configuration est Ă©galement trop verbeuse pour une automatisation Ă©lĂ©gante. +* **HAProxy :** Bien qu'il soit le roi incontestĂ© des performances rĂ©seau pures, il est moins intuitif que NGINX pour servir de simples pages HTML statiques (pages d'erreur 404/502). Il s'agit d'un outil surdimensionnĂ© pour ce besoin spĂ©cifique. +* **Caddy :** Caddy est fantastique pour sa gestion automatique du HTTPS. Cependant, la magie de sa configuration (qui fait beaucoup de choses par dĂ©faut) rend parfois le templating via Ansible moins granulaire et explicite qu'avec les blocs `server` et `location` trĂšs structurĂ©s de NGINX. L'intĂ©gration de CrowdSec sur NGINX dispose Ă©galement d'une antĂ©rioritĂ© et d'une communautĂ© DevSecOps plus vaste. + +--- + +[^1]: **VPS (Virtual Private Server)** : Un serveur virtuel louĂ© chez un fournisseur cloud (comme Infomaniak), offrant une machine disponible en permanence sur Internet avec une adresse IP fixe. +[^2]: **Redirection de ports (Port Forwarding)** : RĂšgle rĂ©seau permettant d'autoriser et de transfĂ©rer le trafic venant d'Internet vers une machine prĂ©cise cachĂ©e dans un rĂ©seau local. +[^3]: **WAF (Web Application Firewall)** : Un bouclier de sĂ©curitĂ© qui analyse en temps rĂ©el les requĂȘtes web pour bloquer les comportements suspects et les cyberattaques. +[^4]: **Ansible** : Un outil d'Infrastructure as Code (IaC) qui permet de configurer et dĂ©ployer des serveurs de maniĂšre totalement automatisĂ©e grĂące Ă  du code. +[^5]: **HTTP/3** : La derniĂšre Ă©volution du langage d'Internet. Il rend le chargement des sites plus rapide et plus rĂ©sistant aux coupures rĂ©seau en utilisant un protocole moderne (QUIC). \ No newline at end of file diff --git a/src/content/docs/homelab/reverse-proxy/pratique-nginx.md b/src/content/docs/homelab/reverse-proxy/pratique-nginx.md new file mode 100644 index 0000000..23b69ff --- /dev/null +++ b/src/content/docs/homelab/reverse-proxy/pratique-nginx.md @@ -0,0 +1,69 @@ +--- +title: "Pratique - NGINX" +description: "DĂ©ploiement, architecture et retour d'expĂ©rience sur l'intĂ©gration de NGINX dans l'infrastructure LoutikCLOUD." +sidebar: + order: 3 +--- + +## A. Contexte + +Dans la topologie LoutikCLOUD, NGINX n'est pas hĂ©bergĂ© directement dans mon rĂ©seau local (LAN), mais sur un serveur externe agissant comme un point de prĂ©sence "Edge". DĂ©ployĂ© sur une machine virtuelle (VPS[^1]) sous Debian situĂ©e chez Infomaniak Ă  GenĂšve, ce serveur encaisse le trafic public via une IP dĂ©diĂ©e. NGINX agit ici en tandem avec CrowdSec (pour la partie pare-feu applicatif) et communique de maniĂšre chiffrĂ©e avec mes serveurs internes locaux au travers d'un tunnel VPN[^2] (Tailscale). C'est la pierre angulaire qui sĂ©curise et route l'accĂšs Ă  des services comme le SSO, le portail de documentation ou le tableau de bord de supervision. + +## B. Architecture + +L'objectif de cette architecture est de garantir qu'aucun de mes serveurs locaux (bases de donnĂ©es, conteneurs K3s, proxmox) ne soit directement exposĂ© Ă  Internet. Le VPS "Edge" absorbe la charge et filtre les requĂȘtes malveillantes. + +```text + [ Utilisateurs Externes ] + | + | HTTP/3 (QUIC) / HTTPS + v ++---------------------------------------------+ +| VPS EDGE (Infomaniak) - IP: 83.228.215.158 | +| | +| 1. [ WAF CrowdSec ] (Analyse IP/RequĂȘtes) | +| | | +| 2. [ Reverse Proxy NGINX ] | +| - Terminaison TLS (Certbot) | +| - Headers de sĂ©curitĂ© (HSTS) | +| - Routage dynamique | ++---------------------------------------------+ + | + | Tunnel chiffrĂ© (10.0.x.x) + v + [ VPN Tailscale (RĂ©seau privĂ©) ] + | + v ++---------------------------------------------+ +| HOMELAB (LoutikCLOUD - Local) | +| | +| - 10.0.12.2 : Docker / Portainer | +| - 10.0.22.x : Kubernetes K3s | +| - 10.0.13.1 : DNS PowerDNS | ++---------------------------------------------+ +``` + +## C. DĂ©ploiement + +Toute l'installation et la configuration sont strictement gĂ©rĂ©es via l'outil d'automatisation Ansible, au travers de deux rĂŽles distincts (`edge-nginx-install` et `edge-nginx-config`). Cette sĂ©paration me permet de rĂ©installer la machine de zĂ©ro en quelques minutes. + +* **Durcissement et "Catch-all" :** La premiĂšre Ă©tape dĂ©ploie les paquets de base et masque immĂ©diatement la version de NGINX (`server_tokens off`). J'ai configurĂ© un serveur par dĂ©faut (Catch-all) qui rejette systĂ©matiquement les requĂȘtes HTTP/HTTPS visant directement l'adresse IP du serveur ou des noms de domaine non reconnus. +* **Templating dynamique des vHosts :** J'utilise un fichier Jinja2[^3] (`vhost.conf.j2`) qui boucle sur une liste de domaines (ex: `sso.loutik.fr`, `docs.loutik.fr`). Le code gĂ©nĂšre automatiquement la configuration du proxy, lance Certbot pour rĂ©cupĂ©rer les certificats Let's Encrypt via la mĂ©thode HTTP01 et injecte des pages d'erreur HTML personnalisĂ©es utilisant les balises SSI[^4] pour afficher dynamiquement le statut de l'erreur. + +:::note +La gestion des pages d'erreurs (403, 404, 502...) se fait de maniĂšre unifiĂ©e via un fichier partagĂ© `error_pages.conf`. Ainsi, si un conteneur crash dans le homelab, le visiteur voit une page de maintenance propre aux couleurs de LoutikCLOUD plutĂŽt qu'une erreur brute du navigateur. +::: + +## D. DifficultĂ©s + +Lors de la mise en place, quelques ajustements ont Ă©tĂ© nĂ©cessaires pour fiabiliser le routage vers mes applications locales : + +* **Erreurs 502 (Bad Gateway) sur le SSO Authentik :** Les requĂȘtes d'authentification gĂ©nĂšrent parfois de trĂšs gros en-tĂȘtes HTTP (cookies, tokens). NGINX, par dĂ©faut, utilise des tampons (buffers) assez petits. Lorsqu'ils Ă©taient saturĂ©s, NGINX coupait la connexion avec l'erreur 502. J'ai rĂ©solu ce problĂšme en crĂ©ant un fichier de configuration additionnel (`99-proxy-buffers.conf`) augmentant la taille des tampons (`proxy_buffer_size 128k; proxy_buffers 4 256k;`). +* **DĂ©connexions intempestives des WebSockets :** Certaines applications comme le portail SSO ou la supervision en temps rĂ©el nĂ©cessitent des connexions bidirectionnelles persistantes (WebSockets). Le proxy HTTP standard de NGINX les bloquait. J'ai dĂ» modifier mon template Ansible pour injecter dynamiquement les en-tĂȘtes `Upgrade $http_upgrade` et Ă©tendre les `timeout` (jusqu'Ă  3600s) spĂ©cifiquement pour les hĂŽtes nĂ©cessitant cette technologie. + +--- + +[^1]: **VPS (Virtual Private Server)** - Un serveur distant louĂ© chez un hĂ©bergeur cloud offrant des ressources dĂ©diĂ©es et une adresse IP publique fixe, idĂ©al pour exposer des services. +[^2]: **VPN (Virtual Private Network)** - Un tunnel rĂ©seau chiffrĂ© qui permet de relier de maniĂšre transparente et sĂ©curisĂ©e des machines situĂ©es dans des lieux gĂ©ographiques diffĂ©rents. +[^3]: **Jinja2** - Un moteur de crĂ©ation de modĂšles utilisĂ© par Ansible permettant de gĂ©nĂ©rer des fichiers de configuration dynamiques en utilisant des variables et des boucles de code. +[^4]: **SSI (Server Side Includes)** - Une fonctionnalitĂ© de NGINX permettant d'exĂ©cuter des directives simples pour injecter des variables (comme le code de l'erreur HTTP ou le nom d'hĂŽte) Ă  l'intĂ©rieur d'une page HTML statique avant de l'envoyer au visiteur. \ No newline at end of file diff --git a/src/content/docs/homelab/reverse-proxy/theorie-nginx.md b/src/content/docs/homelab/reverse-proxy/theorie-nginx.md new file mode 100644 index 0000000..5a32451 --- /dev/null +++ b/src/content/docs/homelab/reverse-proxy/theorie-nginx.md @@ -0,0 +1,46 @@ +--- +title: "ThĂ©orie - NGINX" +description: "Cours et concepts fondamentaux autour du Reverse Proxy NGINX." +sidebar: + order: 1 +--- + +## A. PrĂ©sentation + +Un reverse proxy (ou proxy inverse) agit comme l'unique porte d'entrĂ©e d'une infrastructure comme LoutikCLOUD. Lorsqu'un visiteur demande Ă  accĂ©der Ă  un service, NGINX intercepte la requĂȘte, vĂ©rifie sa validitĂ©, et la redirige discrĂštement vers le bon serveur en arriĂšre-plan. Cela permet de masquer la complexitĂ© du rĂ©seau interne tout en centralisant le contrĂŽle du trafic. + +:::note +Contrairement Ă  un proxy classique qui protĂšge les utilisateurs (en cachant leur origine vers Internet), le reverse proxy protĂšge les serveurs (en cachant l'infrastructure interne aux yeux d'Internet). +::: + +## B. ProblĂ©matiques rĂ©solues + +* **SĂ©curitĂ© et exposition restreinte :** Évite d'exposer directement les serveurs d'applications et les bases de donnĂ©es sur Internet. NGINX encaisse les requĂȘtes et filtre le trafic. +* **Centralisation des certificats SSL/TLS[^1] :** GĂšre le chiffrement HTTPS en un seul point de contrĂŽle plutĂŽt que de devoir le configurer individuellement sur chaque application. +* **RĂ©partition de charge (Load Balancing)[^2] :** Distribue intelligemment le trafic entrant sur plusieurs serveurs ou conteneurs pour Ă©viter les surcharges. + +## C. Fonctionnement + +NGINX fonctionne de maniĂšre asynchrone et Ă©vĂ©nementielle, ce qui lui permet de traiter des milliers de connexions simultanĂ©es avec une empreinte mĂ©moire extrĂȘmement faible. Il lit ses fichiers de configuration pour savoir comment router chaque requĂȘte HTTP/HTTPS[^3] entrante. + +* **Le Worker Process :** C'est le moteur de NGINX. Au lieu de crĂ©er un nouveau processus lourd pour chaque visiteur, un *worker* unique gĂšre de multiples connexions simultanĂ©ment grĂące Ă  une boucle d'Ă©vĂ©nements. +* **Le Server Block (Virtual Host) :** C'est la rĂšgle de routage. Il indique Ă  NGINX : "Si on te demande *app.loutik.cloud*, envoie le trafic vers l'adresse interne *10.0.0.50* sur le port *8080*". + +## D. Exemples + +* **HĂ©bergement multi-sites :** HĂ©berger un blog, une instance Nextcloud et un portail d'audit sur la mĂȘme adresse IP publique. NGINX lit le nom de domaine demandĂ© et aiguille la requĂȘte vers le bon conteneur Docker. +* **Terminaison SSL :** DĂ©charger les serveurs internes de la lourdeur du chiffrement. NGINX s'occupe de la sĂ©curisation HTTPS avec Let's Encrypt cĂŽtĂ© public, puis dialogue en HTTP simple et rapide avec les applications locales. + +## E. Bonnes pratiques + +| RĂšgle | CatĂ©gorie | Justification | +| :--- | :--- | :--- | +| **DĂ©sactiver les tokens (server_tokens off;)** | SĂ©curitĂ© | Masque le numĂ©ro de version de NGINX dans les en-tĂȘtes HTTP pour compliquer la tĂąche des attaquants cherchant des failles connues. | +| **Forcer la redirection HTTPS** | SĂ©curitĂ© | Garantit que toutes les communications entre le client et l'infrastructure soient systĂ©matiquement chiffrĂ©es. | +| **Modulariser la configuration** | Maintenance | Utiliser un fichier de configuration distinct par service (`/etc/nginx/conf.d/`) pour faciliter l'automatisation (via Ansible, par exemple) et le dĂ©pannage. | + +--- + +[^1]: **SSL/TLS** : Un protocole de sĂ©curitĂ© qui crĂ©e un tunnel chiffrĂ© (le fameux cadenas dans le navigateur) pour protĂ©ger les donnĂ©es Ă©changĂ©es contre l'interception. +[^2]: **Load Balancing** : Technique consistant Ă  rĂ©partir la charge de travail entre plusieurs machines identiques pour qu'aucune ne tombe en panne sous la pression. +[^3]: **HTTP/HTTPS** : Le langage de communication (protocole) standard utilisĂ© par les navigateurs web pour demander et afficher des pages internet de maniĂšre claire ou sĂ©curisĂ©e. \ No newline at end of file diff --git a/src/content/docs/homelab/routeur-pare-feu/choix-opnsense.md b/src/content/docs/homelab/routeur-pare-feu/choix-opnsense.md new file mode 100644 index 0000000..a2b2fda --- /dev/null +++ b/src/content/docs/homelab/routeur-pare-feu/choix-opnsense.md @@ -0,0 +1,76 @@ +--- +title: "Choix - OPNsense" +description: "Explication et justification du choix technique d'OPNsense au sein de LoutikCLOUD." +sidebar: + order: 2 +--- + +## A. Contexte + +Au sein de l'infrastructure LoutikCLOUD, j'ai rapidement ressenti le besoin d'extraire le rĂŽle critique de routeur et de pare-feu de mon hyperviseur Proxmox. Virtualiser son routeur principal crĂ©e une dĂ©pendance cyclique complexe (le fameux problĂšme de l'Ɠuf et de la poule lors des redĂ©marrages de l'hyperviseur ou des maintenances matĂ©rielles). + +Pour garantir une haute disponibilitĂ© d'accĂšs et une vĂ©ritable sĂ©grĂ©gation physique, j'ai fait le choix d'acquĂ©rir un Ă©quipement bare-metal[^1] dĂ©diĂ© : un client lĂ©ger HP T730. Ce matĂ©riel est Ă©quipĂ© d'une carte rĂ©seau PCI Express Intel d'1 Gb/s et prĂ©sente l'Ă©norme avantage de consommer trĂšs peu d'Ă©lectricitĂ©, un critĂšre essentiel pour une machine destinĂ©e Ă  tourner 24h/24 et 7j/7. Il me fallait ensuite choisir le systĂšme d'exploitation (OS) capable d'exploiter cette machine pour sĂ©curiser et router le trafic de mon infrastructure de maniĂšre moderne, automatisable et robuste. + +## B. Cahiers des charges + +| ID | Type | Exigence | Description | +| :--- | :--- | :--- | :--- | +| **[REQ-F01]** | Fonctionnel | Filtrage et IPS[^2] | CapacitĂ© Ă  bloquer activement le trafic illĂ©gitime en local via des listes de contrĂŽle d'accĂšs (ACL[^3]) et un systĂšme de prĂ©vention d'intrusion performant. | +| **[REQ-F02]** | Fonctionnel | GratuitĂ© et pĂ©rennitĂ© | La solution doit ĂȘtre 100% gratuite (open-source de prĂ©fĂ©rence) et bĂ©nĂ©ficier de mises Ă  jour de sĂ©curitĂ© trĂšs rĂ©guliĂšres. | +| **[REQ-T01]** | Technique | API REST native | PrĂ©sence d'une API[^4] complĂšte permettant l'automatisation totale des configurations et la gestion des sauvegardes (notamment via Ansible). | +| **[REQ-T02]** | Technique | ObservabilitĂ© Ă©tendue | PossibilitĂ© d'exporter facilement des mĂ©triques de performance et de trafic vers des outils de supervision comme Prometheus ou Zabbix. | +| **[REQ-T03]** | Technique | CompatibilitĂ© x86 basse conso | L'OS doit ĂȘtre optimisĂ© pour tourner sur une architecture standard (comme le HP T730) sans surcharger inutilement le processeur. | + +## C. Les solutions du marchĂ© + +### C.1. PrĂ©sentations des solutions + +#### C.1.1. OPNsense +* **PrĂ©sentation gĂ©nĂ©rale :** Pare-feu et routeur open-source basĂ© sur HardenedBSD. C'est un *fork* (une dĂ©rivation) de pfSense créé pour offrir une interface plus moderne et un code plus ouvert. +* **Fonctionnement :** Utilise un pare-feu *stateful* robuste, intĂšgre Suricata pour la partie IPS, et propose une architecture modulaire basĂ©e sur des plugins. +* **Profil :** OrientĂ© DevSecOps, administrateurs rĂ©seaux modernes et entreprises cherchant une plateforme ouverte, hautement automatisable via API. + +#### C.1.2. pfSense (Community Edition) +* **PrĂ©sentation gĂ©nĂ©rale :** La rĂ©fĂ©rence historique des pare-feux open-source, massivement dĂ©ployĂ©e Ă  travers le monde et soutenue par l'entreprise Netgate. +* **Fonctionnement :** ExtrĂȘmement stable et complet, il propose un Ă©cosystĂšme de paquets trĂšs vaste pour Ă©tendre ses fonctionnalitĂ©s (Snort, pfBlockerNG). +* **Profil :** Administrateurs rĂ©seaux traditionnels et entreprises cherchant une solution Ă©prouvĂ©e avec une immense base de documentation. + +#### C.1.3. OpenWRT +* **PrĂ©sentation gĂ©nĂ©rale :** Projet open-source historique, extrĂȘmement lĂ©ger, conçu Ă  l'origine pour remplacer les firmwares propriĂ©taires des routeurs grand public. +* **Fonctionnement :** AxĂ© sur la lĂ©gĂšretĂ© et la flexibilitĂ©, il se configure via l'interface web LuCI ou directement en ligne de commande via son systĂšme UCI. +* **Profil :** Bidouilleurs, environnements embarquĂ©s, points d'accĂšs Wi-Fi et matĂ©riels avec de trĂšs faibles ressources matĂ©rielles (RAM/CPU). + +#### C.1.4. VyOS +* **PrĂ©sentation gĂ©nĂ©rale :** OS de routage open-source orientĂ© entreprise, entiĂšrement dĂ©pourvu d'interface graphique (GUI). +* **Fonctionnement :** Toute la configuration se fait via une ligne de commande unifiĂ©e (CLI) trĂšs puissante, inspirĂ©e du matĂ©riel Juniper. +* **Profil :** IngĂ©nieurs rĂ©seaux purs et durs, dĂ©ploiements cloud massifs oĂč le routage BGP/OSPF prime sur l'interface visuelle. + +### C.2. Comparatifs des solutions + +| Exigence | OPNsense | pfSense (CE) | OpenWRT | VyOS | +| :--- | :--- | :--- | :--- | :--- | +| **[REQ-F01 (IPS & ACL)]** | ValidĂ© (Suricata natif) | ValidĂ© (Snort/Suricata) | Évaluation (Complexe Ă  intĂ©grer) | Évaluation (Moins intuitif) | +| **[REQ-T01 (API & Ansible)]** | ValidĂ© (API REST riche) | Non validĂ© (Pas d'API native gratuite) | ValidĂ© (Via SSH/UCI) | ValidĂ© (Excellent support) | +| **[REQ-T02 (Supervision)]** | ValidĂ© (Plugins natifs) | ValidĂ© | ValidĂ© | ValidĂ© | +| **[REQ-F02 (Gratuit/À jour)]** | ValidĂ© | Évaluation (ModĂšle vers le payant) | ValidĂ© | Évaluation (Images LTS payantes) | + +## D. Solution proposĂ©e + +La solution proposĂ©e pour l'infrastructure est **OPNsense**. + +DĂ©ployĂ© sur le client lĂ©ger HP T730, OPNsense remplit parfaitement son rĂŽle de douanier physique pour l'ensemble du rĂ©seau LoutikCLOUD. Son intĂ©gration native de Suricata me permet d'activer un IPS performant qui inspecte les flux entrants et sortants pour couper net toute tentative de compromission dĂ©tectĂ©e par ses signatures. + +Ce qui a dĂ©finitivement fait pencher la balance en sa faveur, c'est son ouverture aux pratiques DevSecOps. GrĂące Ă  son API REST native, je peux orchestrer OPNsense avec Ansible sans avoir Ă  "bidouiller" via SSH. C'est cette mĂȘme API qui me permet d'automatiser l'extraction des sauvegardes de configuration au format XML pour les externaliser de maniĂšre sĂ©curisĂ©e, assurant une reprise d'activitĂ© immĂ©diate en cas de panne du HP T730. L'export des mĂ©triques vers ma stack d'observabilitĂ© locale se fait en quelques clics grĂące aux plugins communautaires rĂ©guliĂšrement mis Ă  jour. + +**Justification du rejet des solutions alternatives :** + +* **pfSense :** Bien que ce soit une solution lĂ©gendaire, l'absence d'une vĂ©ritable API REST dans sa version communautaire (Community Edition) rendait l'automatisation de mes sauvegardes et configurations via Ansible beaucoup trop lourde et instable. De plus, la trajectoire rĂ©cente de l'Ă©diteur pousse de plus en plus vers la version payante (pfSense Plus). +* **OpenWRT :** C'est un OS fantastique pour des routeurs Wi-Fi limitĂ©s en puissance, mais son Ă©cosystĂšme n'est pas pensĂ© en prioritĂ© pour hĂ©berger un moteur IPS complet et lourd (comme Suricata) sur une architecture x86. L'interface aurait demandĂ© trop de personnalisations pour atteindre mes objectifs de sĂ©curitĂ©. +* **VyOS :** Bien que son interface en ligne de commande unifiĂ©e soit un rĂ©gal pour l'automatisation rĂ©seau, l'absence d'interface web rend le diagnostic rapide et la gestion visuelle des rĂšgles de pare-feu au quotidien moins pratique dans le cadre d'un homelab polyvalent oĂč je suis le seul mainteneur. + +--- + +[^1]: **Bare-metal** : Fait rĂ©fĂ©rence Ă  un Ă©quipement informatique physique dĂ©diĂ©, sur lequel le systĂšme d'exploitation est installĂ© directement sur le matĂ©riel, sans couche de virtualisation intermĂ©diaire. +[^2]: **IPS (Intrusion Prevention System)** : Un systĂšme de sĂ©curitĂ© rĂ©seau qui analyse le trafic en temps rĂ©el pour dĂ©tecter et bloquer automatiquement les activitĂ©s malveillantes (comme les attaques informatiques ou les virus) avant qu'elles n'atteignent leur cible. +[^3]: **ACL (Access Control List)** : Une liste de rĂšgles de sĂ©curitĂ© strictes dĂ©finissant trĂšs prĂ©cisĂ©ment qui ou quoi a le droit d'entrer ou de sortir d'un rĂ©seau ou d'accĂ©der Ă  un service (ex: bloquer l'adresse IP X sur le port Y). +[^4]: **API (Application Programming Interface)** : Un pont logiciel qui permet Ă  deux applications diffĂ©rentes de communiquer et d'Ă©changer des donnĂ©es entre elles de maniĂšre automatisĂ©e, sans intervention humaine. \ No newline at end of file diff --git a/src/content/docs/homelab/routeur-pare-feu/pratique-opnsense.md b/src/content/docs/homelab/routeur-pare-feu/pratique-opnsense.md new file mode 100644 index 0000000..11d4ad4 --- /dev/null +++ b/src/content/docs/homelab/routeur-pare-feu/pratique-opnsense.md @@ -0,0 +1,63 @@ +--- +title: "Pratique - OPNsense" +description: "DĂ©ploiement, architecture et retour d'expĂ©rience sur l'intĂ©gration d'OPNsense dans l'infrastructure." +sidebar: + order: 3 +--- + +## A. Contexte + +Dans l'infrastructure LoutikCLOUD, OPNsense occupe la place centrale. Il n'est pas virtualisĂ©, mais installĂ© directement sur un matĂ©riel dĂ©diĂ© (un HP T730 dit "bare-metal"). Ce choix garantit que le rĂ©seau reste opĂ©rationnel mĂȘme en cas de maintenance ou de panne de mes hyperviseurs Proxmox. Il agit comme le point de terminaison de mon rĂ©seau local, gĂ©rant la connexion vers Internet (via ma box opĂ©rateur sur le port `WAN`), et distribuant le trafic vers de multiples rĂ©seaux isolĂ©s (VLANs) sur son port `LAN`. C'est lui qui hĂ©berge le tunnel VPN (Tailscale) permettant de relier mon infrastructure locale au proxy frontal situĂ© chez Infomaniak. + +## B. Architecture + +L'architecture s'articule autour d'une approche "Zero Trust" intra-rĂ©seau. PlutĂŽt que d'avoir un grand rĂ©seau plat oĂč toutes les machines discutent librement, OPNsense dĂ©coupe et route le trafic (rĂ©seau `10.0.x.0/16`) Ă  travers de multiples VLANs hermĂ©tiques, regroupĂ©s par zones de sĂ©curitĂ© logiques. + +```text + [ INTERNET ] + | + (WAN) ++-------------------------------------------------------------------------+ +| OPNsense (HP T730) | +| | +| [ IPS Suricata ] <---> [ Pare-feu (RĂšgles strictes) ] <---> [ Routage] | ++-------------------------------------------------------------------------+ + | + (Trunk 802.1Q) + | + +------------------+-----------+-------+------------------+ + | | | | +[ DMZ Externe ] [ DMZ Interne ] [ ZDR ] [ LAN ] +- VLAN 10 (VPN) - VLAN 12 (Services) - VLAN 17 (DB) - VLAN 19 (Users) +- VLAN 11 (Bastion)- VLAN 13 (Infra) - VLAN 18 (PKI) - VLAN 20 (Admoob) + - VLAN 14 (Supervision) + - VLAN 15 (Deploy) +---------------------------+ + - VLAN 16 (Backup) | [ Pre-Production ] | + - VLAN 23 (Proxy) | - VLAN 21 & 22 (Dev/Test) | + +---------------------------+ + +``` + +Cette macro-segmentation permet un contrĂŽle granulaire. Par exemple, la Zone de Diffusion Restreinte (ZDR) abritant les bases de donnĂ©es (VLAN 17) est strictement isolĂ©e d'Internet et n'accepte des requĂȘtes que depuis la DMZ Interne (VLAN 12) sur le port PostgreSQL. + +## C. DĂ©ploiement + +Contrairement au reste de l'infrastructure, la configuration pure d'OPNsense n'a pas Ă©tĂ© rĂ©alisĂ©e via Ansible (IaC[^1]). Bien que l'outil propose une API, celle-ci ne couvre pas l'intĂ©gralitĂ© des fonctionnalitĂ©s requises. Tenter de tout automatiser importait une complexitĂ© disproportionnĂ©e (dĂ©veloppement de modules complexes) pour un faible retour sur investissement, sachant qu'il n'y a qu'un seul routeur physique dans ce homelab. J'ai donc optĂ© pour une configuration manuelle via l'interface web. + +Cependant, l'automatisation conserve un rĂŽle critique pour la pĂ©rennitĂ© du service : + +* **Configuration manuelle structurĂ©e :** CrĂ©ation des interfaces (`vlan0.11` Ă  `vlan0.23`), configuration du pare-feu avec une politique de rejet par dĂ©faut ("Default Deny"), et mise en place des services (Unbound DNS, Tailscale, Suricata). +* **Sauvegardes automatisĂ©es via Ansible :** Un rĂŽle Ansible dĂ©diĂ© se connecte rĂ©guliĂšrement Ă  l'API d'OPNsense avec un compte de service restreint (`svc-opnsense-backup`). Il orchestre l'export du fichier de configuration XML et le pousse sĂ©curitairement vers mon instance Proxmox Backup Server (PBS) locale, ainsi que vers mon Nextcloud distant (Infomaniak) pour le stockage froid hors-site. + +## D. DifficultĂ©s + +Lors de la mise en place et de l'exploitation, plusieurs incidents riches en enseignements se sont produits : + +* **Auto-blocage par l'IDS/IPS :** Lors de l'activation du moteur de dĂ©tection d'intrusions (Suricata), des rĂšgles un peu trop agressives ont identifiĂ© mon trafic d'administration lĂ©gitime comme une menace. ConsĂ©quence : blocage total de l'accĂšs Ă  l'interface web d'OPNsense. J'ai dĂ» implĂ©menter une rĂšgle explicite d'anti-lockout pour le sous-rĂ©seau d'administration (`10.0.20.0/24`) afin de m'en prĂ©munir dĂ©finitivement. +* **Perte d'accĂšs Ă  distance via le VPN :** Suite Ă  une mauvaise manipulation lors d'une rĂ©vision des rĂšgles de filtrage sur l'interface virtuelle Tailscale (`opt2`), j'ai totalement coupĂ© mon accĂšs externe Ă  l'infrastructure. La rĂ©solution a nĂ©cessitĂ© une intervention physique locale sur le VLAN d'administration pour annuler la modification. +* **Corruptions silencieuses de configuration :** À un moment donnĂ©, des morceaux entiers de ma configuration (rĂšgles, alias) disparaissaient alĂ©atoirement aprĂšs des redĂ©marrages. Le diagnostic a rĂ©vĂ©lĂ© que le disque SSD NVMe[^2] du HP T730 Ă©tait dĂ©fectueux. GrĂące Ă  ma stratĂ©gie de sauvegarde automatisĂ©e Ansible, j'ai pu remplacer le disque, rĂ©installer l'OS et rĂ©injecter le fichier XML en moins de dix minutes. + +--- + +[^1]: **IaC (Infrastructure as Code)** - Pratique consistant Ă  gĂ©rer et configurer des infrastructures informatiques Ă  l'aide de fichiers de code (comme Ansible ou Terraform) plutĂŽt que via des interfaces graphiques manuelles. +[^2]: **SSD NVMe (Non-Volatile Memory express)** - Un type de composant de stockage matĂ©riel ultra-rapide qui se branche directement sur la carte mĂšre de l'ordinateur, remplaçant les anciens disques durs mĂ©caniques. \ No newline at end of file diff --git a/src/content/docs/homelab/routeur-pare-feu/theorie-opnsense.md b/src/content/docs/homelab/routeur-pare-feu/theorie-opnsense.md new file mode 100644 index 0000000..85be39c --- /dev/null +++ b/src/content/docs/homelab/routeur-pare-feu/theorie-opnsense.md @@ -0,0 +1,46 @@ +--- +title: "ThĂ©orie - OPNsense" +description: "Cours et concepts fondamentaux autour d'OPNsense." +sidebar: + order: 1 +--- + +## A. PrĂ©sentation + +OPNsense est une plateforme open-source de routage et de pare-feu (firewall). Son but principal est de sĂ©curiser, de diriger et d'analyser le trafic rĂ©seau qui entre, qui sort, ou qui circule au sein de l'infrastructure. Il agit comme le point de contrĂŽle central (la douane et le chef d'orchestre) du rĂ©seau, garantissant que seules les communications lĂ©gitimes sont autorisĂ©es. + +:::note +Bien qu'il soit souvent comparĂ© Ă  de simples routeurs de box internet, OPNsense offre des fonctionnalitĂ©s de niveau entreprise (filtrage avancĂ©, VPN, dĂ©tection d'intrusions) indispensables pour maĂźtriser son infrastructure de bout en bout. +::: + +## B. ProblĂ©matiques rĂ©solues + +* **SĂ©curitĂ© pĂ©rimĂ©trique et interne :** EmpĂȘche les attaques venant d'Internet d'atteindre les serveurs locaux, mais protĂšge Ă©galement les diffĂ©rentes zones du rĂ©seau interne entre elles (pour Ă©viter qu'une machine compromise ne contamine les autres). +* **Segmentation et isolation du trafic :** Permet de dĂ©couper physiquement ou logiquement le rĂ©seau pour sĂ©parer, par exemple, un environnement d'expĂ©rimentation (homelab) du rĂ©seau familial principal. + +## C. Fonctionnement + +OPNsense fonctionne en interceptant les paquets de donnĂ©es (le trafic rĂ©seau) au niveau de ses interfaces (les ports rĂ©seau de la machine). Il lit les Ă©tiquettes de ces paquets (source, destination, port) et les confronte Ă  une liste de rĂšgles strictes lues de haut en bas pour prendre une dĂ©cision : bloquer, rejeter ou autoriser. + +* **Le Pare-feu Stateful[^1] :** C'est le moteur de sĂ©curitĂ©. Il ne se contente pas d'appliquer des rĂšgles bĂȘtes ; il comprend quelles connexions ont Ă©tĂ© lĂ©gitimement initiĂ©es de l'intĂ©rieur pour autoriser automatiquement le trafic de retour, sans nĂ©cessiter de rĂšgle supplĂ©mentaire. +* **Le Routage :** Le routage dĂ©termine le meilleur chemin pour qu'un paquet atteigne sa destination entre diffĂ©rents sous-rĂ©seaux. +* **Le NAT[^2] :** Le NAT permet Ă  plusieurs machines d'un rĂ©seau local privĂ© d'utiliser une seule adresse IP publique pour naviguer sur Internet. + +## D. Exemples + +* **DĂ©ploiement de VLANs[^3] pour l'hygiĂšne rĂ©seau :** Isoler les flux d'administration (accĂšs aux hyperviseurs Proxmox, iLO) sur un VLAN dĂ©diĂ©, strictement sĂ©parĂ© du VLAN des conteneurs applicatifs publics (comme K3s) ou du Wi-Fi invitĂ©. +* **Passerelle VPN sĂ©curisĂ©e :** Configurer un serveur WireGuard ou OpenVPN directement sur OPNsense pour permettre aux administrateurs de se connecter Ă  l'infrastructure de maniĂšre chiffrĂ©e depuis n'importe oĂč dans le monde, comme s'ils Ă©taient sur place. + +## E. Bonnes pratiques + +| RĂšgle | CatĂ©gorie | Justification | +| :--- | :--- | :--- | +| **RĂšgle du "Default Deny"** | SĂ©curitĂ© | Bloquer absolument tout le trafic par dĂ©faut Ă  la fin de la liste des rĂšgles, et n'autoriser que les flux strictement nĂ©cessaires (Principe du moindre privilĂšge). | +| **Sauvegarde automatisĂ©e** | Maintenance | Externaliser rĂ©guliĂšrement la configuration XML (via Ansible ou Rclone vers un stockage froid type Nextcloud) pour remonter l'infrastructure en quelques minutes en cas de crash matĂ©riel. | +| **Nommage clair des alias** | Maintenance / Performance | Utiliser des "Alias" (des groupes de ports ou d'IP nommĂ©s, ex: `SERVEURS_INFRA`) plutĂŽt que de taper les adresses IP en dur dans les rĂšgles. Cela allĂšge la lecture et accĂ©lĂšre la mise Ă  jour des rĂšgles. | + +--- + +[^1]: **Stateful (Ă  Ă©tats)** - Un pare-feu intelligent qui garde en mĂ©moire (dans une table d'Ă©tats) les connexions actives. S'il voit qu'un PC du rĂ©seau a demandĂ© Ă  afficher un site web, il laisse naturellement entrer la rĂ©ponse du site web sans bloquer la porte. +[^2]: **NAT (Network Address Translation)** - MĂ©canisme qui traduit les adresses IP privĂ©es (invisibles sur Internet) en une adresse IP publique pour communiquer avec l'extĂ©rieur, masquant ainsi la topologie interne du rĂ©seau. +[^3]: **VLAN (Virtual Local Area Network)** - Technologie permettant de crĂ©er plusieurs rĂ©seaux locaux logiques virtuels et totalement isolĂ©s en utilisant le mĂȘme matĂ©riel physique (cĂąbles et switchs). \ No newline at end of file diff --git a/src/content/docs/homelab/sauvegarde/choix-proxmox-backup-server.md b/src/content/docs/homelab/sauvegarde/choix-proxmox-backup-server.md new file mode 100644 index 0000000..fb90338 --- /dev/null +++ b/src/content/docs/homelab/sauvegarde/choix-proxmox-backup-server.md @@ -0,0 +1,69 @@ +--- +title: "Choix - Proxmox Backup Server" +description: "Explication et justification du choix technique de Proxmox Backup Server au sein de LoutikCLOUD." +sidebar: + order: 2 +--- + +## A. Contexte + +La pĂ©rennitĂ© d'un homelab comme LoutikCLOUD repose entiĂšrement sur la fiabilitĂ© de ses sauvegardes. Actuellement, je dispose d'un serveur physique Proxmox dĂ©diĂ© spĂ©cifiquement Ă  la sauvegarde et Ă  l'hĂ©bergement des donnĂ©es persistantes. Pour isoler proprement le service, j'ai allouĂ© une machine virtuelle dotĂ©e de 50 Go pour le systĂšme d'exploitation et d'un disque de 500 Go pour le stockage des archives. + +Le dĂ©fi est double : je dois pouvoir sauvegarder de maniĂšre transparente des machines virtuelles entiĂšres (VMs), mais Ă©galement des fichiers plus granulaires comme des configurations brutes ou des sauvegardes de bases de donnĂ©es (dumps[^1]). L'espace de 500 Go Ă©tant relativement limitĂ© pour des sauvegardes rĂ©guliĂšres, le futur outil doit impĂ©rativement optimiser le stockage. Enfin, dans une logique DevSecOps, la solution doit s'intĂ©grer Ă  mon Ă©cosystĂšme Linux (Debian) et ĂȘtre entiĂšrement pilotable par le code. + +## B. Cahiers des charges + +| ID | Type | Exigence | Description | +| :--- | :--- | :--- | :--- | +| **[REQ-F01]** | Fonctionnel | Polyvalence des cibles | CapacitĂ© Ă  sauvegarder Ă  la fois des machines virtuelles complĂštes (blocs) et des fichiers/dossiers isolĂ©s (configurations, dumps). | +| **[REQ-F02]** | Fonctionnel | DĂ©duplication avancĂ©e | MĂ©canisme de dĂ©duplication[^2] performant pour maximiser la rĂ©tention des donnĂ©es sur le disque restreint de 500 Go. | +| **[REQ-T01]** | Technique | CompatibilitĂ© Debian/Linux | L'outil doit s'installer nativement sur une base Debian ou Linux pour respecter les standards de l'infrastructure. | +| **[REQ-T02]** | Technique | API et Automatisation | PrĂ©sence d'une API REST[^3] complĂšte permettant le provisionnement et la gestion des tĂąches via Ansible. | +| **[REQ-T03]** | Technique | Sauvegardes incrĂ©mentielles | Support natif des sauvegardes incrĂ©mentielles[^4] pour rĂ©duire la consommation de bande passante et le temps de traitement. | + +## C. Les solutions du marchĂ© + +### C.1. PrĂ©sentations des solutions + +#### C.1.1. Proxmox Backup Server (PBS) +* **PrĂ©sentation gĂ©nĂ©rale :** Solution open-source de sauvegarde d'entreprise, dĂ©veloppĂ©e par l'Ă©diteur de Proxmox VE. Elle est basĂ©e sur Debian. +* **Fonctionnement :** Utilise une architecture client-serveur avec une dĂ©duplication agressive Ă  la source et un systĂšme de "chunks" (morceaux de donnĂ©es) chiffrĂ©s. +* **Profil :** Administrateurs et DevSecOps gĂ©rant des clusters Proxmox VE, cherchant une intĂ©gration native et une optimisation drastique du stockage. + +#### C.1.2. Veeam Backup & Replication (Community Edition) +* **PrĂ©sentation gĂ©nĂ©rale :** Le leader mondial propriĂ©taire de la sauvegarde en entreprise, offrant une version communautaire gratuite jusqu'Ă  10 charges de travail. +* **Fonctionnement :** Agit comme un orchestrateur central puissant capable de s'interfacer avec presque tous les hyperviseurs et systĂšmes d'exploitation du marchĂ©. +* **Profil :** Entreprises et environnements fortement hĂ©tĂ©rogĂšnes (Windows/Linux, VMware/Hyper-V) cherchant une solution "tout-en-un" avec support professionnel. + +#### C.1.3. Restic / BorgBackup +* **PrĂ©sentation gĂ©nĂ©rale :** Outils open-source en ligne de commande, rĂ©putĂ©s pour leur rapiditĂ©, leur sĂ©curitĂ© et leur lĂ©gĂšretĂ©. +* **Fonctionnement :** Se basent sur la dĂ©duplication au niveau des fichiers et des blocs de fichiers, poussant les donnĂ©es chiffrĂ©es vers des stockages locaux ou cloud. +* **Profil :** DĂ©veloppeurs et sysadmins cherchant Ă  sauvegarder des fichiers, des conteneurs ou des bases de donnĂ©es avec une trĂšs faible empreinte systĂšme. + +### C.2. Comparatifs des solutions + +| Exigence | Proxmox Backup Server | Veeam (Community) | Restic / BorgBackup | +| :--- | :--- | :--- | :--- | +| **[REQ-F01 (VMs & Fichiers)]** | ValidĂ© (via Proxmox Backup Client) | ValidĂ© | Évaluation (Excellent pour fichiers, inadaptĂ© pour VMs Ă  chaud) | +| **[REQ-F02 (DĂ©duplication)]** | ValidĂ© (Excellente Ă  la source) | ValidĂ© | ValidĂ© (TrĂšs performante) | +| **[REQ-T01 (Debian/Linux)]** | ValidĂ© (Base Debian native) | Non validĂ© (Le serveur principal nĂ©cessite Windows) | ValidĂ© | +| **[REQ-T02 (API & Ansible)]** | ValidĂ© (API exhaustive) | ValidĂ© (API disponible) | Évaluation (Pas d'API web, uniquement CLI) | +| **[REQ-T03 (IncrĂ©mentiel)]** | ValidĂ© (IncrĂ©mentiel natif et rapide) | ValidĂ© | ValidĂ© | + +## D. Solution proposĂ©e + +La solution proposĂ©e pour l'infrastructure est **Proxmox Backup Server (PBS)**. + +PBS coche absolument toutes les cases pour LoutikCLOUD. InstallĂ© sur la machine virtuelle Debian dĂ©diĂ©e (avec ses 50 Go d'OS et 500 Go de stockage), il s'intĂšgre nativement, en quelques clics, Ă  l'hyperviseur Proxmox VE de production. Cette synergie permet des sauvegardes incrĂ©mentielles de VMs extrĂȘmement rapides. De plus, pour rĂ©pondre au besoin de sauvegarder des dumps de bases de donnĂ©es ou des fichiers de configuration, PBS met Ă  disposition le `proxmox-backup-client`. Cet utilitaire en ligne de commande s'installe sur n'importe quel conteneur ou machine Linux et permet de pousser des fichiers isolĂ©s directement vers le serveur PBS, tout en bĂ©nĂ©ficiant de la mĂȘme dĂ©duplication puissante. L'intĂ©gralitĂ© de cette plateforme expose une API REST riche, ce qui m'a permis d'automatiser sa configuration et la crĂ©ation de ses "datastores" via Ansible. + +**Justification du rejet des solutions alternatives :** + +* **Veeam Backup & Replication :** Bien qu'il s'agisse d'une solution exceptionnellement puissante, son architecture impose que le serveur de gestion principal tourne sous Windows Server. Cela rompt totalement ma contrainte technique (REQ-T01) d'un Ă©cosystĂšme 100% Linux/Debian. De plus, sa consommation en ressources (RAM/CPU) serait dĂ©mesurĂ©e pour un simple homelab. +* **Restic / BorgBackup :** Ce sont d'excellents outils que j'utilise parfois pour des sauvegardes locales de fichiers. Cependant, ils ne sont pas conçus pour interagir nativement avec l'hyperviseur Proxmox VE pour rĂ©aliser des sauvegardes Ă  chaud de machines virtuelles. Il aurait fallu scripter manuellement l'arrĂȘt, le snapshot et l'export des VMs, ce qui ajoute une complexitĂ© de maintenance inutile et un risque d'erreur important lĂ  oĂč PBS gĂšre cela de maniĂšre native. + +--- + +[^1]: **Dump** : Une copie brute de l'intĂ©gralitĂ© des donnĂ©es contenues dans une base de donnĂ©es Ă  un instant T, gĂ©nĂ©ralement sous la forme d'un gros fichier texte lisible ou compressĂ©. +[^2]: **DĂ©duplication** : Technologie d'optimisation qui analyse les donnĂ©es lors de la sauvegarde. Si un mĂȘme bloc de donnĂ©es (ex: les fichiers de base de Linux) est dĂ©tectĂ© sur plusieurs machines, il n'est Ă©crit qu'une seule fois sur le disque physique pour Ă©conomiser de la place. +[^3]: **API REST** : Une interface de programmation qui permet Ă  des scripts ou des logiciels de communiquer avec le serveur (pour le configurer ou lancer des actions) en utilisant des requĂȘtes web standards (HTTP). +[^4]: **Sauvegarde incrĂ©mentielle** : Une mĂ©thode de sauvegarde intelligente qui, aprĂšs une premiĂšre copie complĂšte, ne va sauvegarder que les donnĂ©es qui ont Ă©tĂ© modifiĂ©es ou ajoutĂ©es depuis la veille, permettant de gagner Ă©normĂ©ment de temps et de ressources rĂ©seau. \ No newline at end of file diff --git a/src/content/docs/homelab/sauvegarde/pratique-sauvegarde.md b/src/content/docs/homelab/sauvegarde/pratique-sauvegarde.md new file mode 100644 index 0000000..750562e --- /dev/null +++ b/src/content/docs/homelab/sauvegarde/pratique-sauvegarde.md @@ -0,0 +1,74 @@ +--- +title: "Pratique - Proxmox Backup Server" +description: "DĂ©ploiement, architecture et retour d'expĂ©rience sur l'intĂ©gration de Proxmox Backup Server dans l'infrastructure LoutikCLOUD." +sidebar: + order: 3 +--- + +## A. Contexte + +Dans l'infrastructure LoutikCLOUD, la gestion des donnĂ©es persistantes est une prioritĂ© absolue. J'ai donc dĂ©ployĂ© Proxmox Backup Server (PBS) sous forme de machine virtuelle (VM) sur mon hyperviseur. AssignĂ©e au rĂ©seau dĂ©diĂ© aux sauvegardes (VLAN 16), cette machine dispose d'un disque systĂšme et d'un second disque virtuel de 400 Go formatĂ© en `ext4` agissant comme *Datastore* (banque de donnĂ©es). Ce serveur centralise deux flux distincts : la sauvegarde classique des machines virtuelles complĂštes, et la sauvegarde granulaire des configurations de services (comme OPNsense ou PostgreSQL) orchestrĂ©e par Ansible. + +## B. Architecture + +L'architecture de sauvegarde repose sur la cĂ©lĂšbre rĂšgle du "3-2-1" et sur le principe de dĂ©couplage (sĂ©parer le systĂšme de sa configuration). J'ai mis en place une double externalisation : une sauvegarde "chaude" sur le PBS local (pour la rapiditĂ© et la dĂ©duplication) et une sauvegarde "froide" sur un espace Nextcloud hĂ©bergĂ© chez Infomaniak. + +```text + [ Infrastructure LoutikCLOUD ] + | + +-------------+-------------+ + | | +[ Hyperviseurs PVE ] [ Services ] +(Sauvegarde globale) (OPNsense, PostgreSQL) + | | + | API Token / TLS | Orchestration Ansible + | | + v v ++---------------------------------------------+ +| PROXMOX BACKUP SERVER (Local - Chaud) | +| - DĂ©duplication Ă  la source | +| - RĂ©tention : 7 jours | +| - Datastore : backups-loutikcloud | ++---------------------------------------------+ + | + | Rclone (WebDAV) + | + v ++---------------------------------------------+ +| NEXTCLOUD INFOMANIAK (Cloud - Froid) | +| - Archive brute (fichiers / dumps) | +| - RĂ©tention : 7 jours | +| - Protocole : HTTPS / WebDAV | ++---------------------------------------------+ + +``` + +:::note +Cette topologie permet une flexibilitĂ© totale. Si je perds une VM entiĂšre, je restaure depuis PBS. Si je perds l'hyperviseur physique (et donc mon PBS local), je peux reconstruire mes services vitaux (comme le routeur) en rĂ©cupĂ©rant simplement le fichier de configuration XML stockĂ© sur le Nextcloud externe. +::: + +## C. DĂ©ploiement + +Le dĂ©ploiement combine des actions manuelles critiques pour le stockage et une automatisation poussĂ©e via Ansible pour la collecte des donnĂ©es. + +* **Initialisation du PBS et liaison Proxmox :** AprĂšs l'installation via l'ISO officielle, j'ai formatĂ© le disque de 400 Go en `ext4` et créé le datastore `backups-loutikcloud`. Ensuite, j'ai gĂ©nĂ©rĂ© un jeton d'API[^1] (API Token) et rĂ©cupĂ©rĂ© l'empreinte cryptographique TLS (Fingerprint). J'ai dĂ©clarĂ© ces informations dans mon cluster Proxmox VE. Cela permet Ă  l'hyperviseur de lancer des *Backup Jobs* (tĂąches planifiĂ©es) toutes les nuits en mode `snapshot`[^2], sauvegardant les VMs Ă  chaud, sans interruption de service. +* **Sauvegardes applicatives via Ansible et WebDAV :** Pour les services critiques (OPNsense, PostgreSQL), j'utilise Ansible. Mon orchestrateur extrait la configuration (via API ou `pg_dump`), puis l'expĂ©die simultanĂ©ment vers deux cibles : +1. Vers le PBS local via l'outil en ligne de commande `proxmox-backup-client` (qui dĂ©duplique[^3] les donnĂ©es). +2. Vers mon Nextcloud distant via l'outil Rclone en utilisant le protocole WebDAV[^4]. Les mots de passe d'application Nextcloud sont obscurcis (chiffrĂ©s) et injectĂ©s dynamiquement pour garantir la sĂ©curitĂ©. + + + +## D. DifficultĂ©s + +Lors de la mise en place, quelques ajustements architecturaux ont Ă©tĂ© nĂ©cessaires pour fiabiliser les transferts et la gestion du stockage : + +* **Saturation rapide du Datastore PBS :** Au bout de quelques semaines, mon disque virtuel de 400 Go s'est rempli, bloquant les nouvelles sauvegardes. Le problĂšme venait de l'absence de nettoyage automatisĂ©. J'ai rĂ©solu cela en intĂ©grant une tĂąche de purge (Pruning) dans mes playbooks Ansible (`proxmox-backup-client prune --keep-last 7`) et en planifiant un *Garbage Collection*[^5] rĂ©gulier sur PBS pour effacer physiquement les blocs de donnĂ©es orphelins. +* **InstabilitĂ© des transferts WebDAV (Timeout) :** Lors de l'envoi de gros fichiers de sauvegarde vers Nextcloud via Rclone, la connexion sautait parfois, faisant Ă©chouer la tĂąche Ansible. En analysant la documentation Rclone, j'ai corrigĂ© le problĂšme en ajoutant l'argument `--vfs-cache-mode writes` lors des montages. Cela permet Ă  Rclone de mettre le fichier en cache localement avant de l'expĂ©dier de maniĂšre asynchrone et sĂ©curisĂ©e vers le cloud, lissant ainsi les micro-coupures rĂ©seau. + +--- + +[^1]: **Jeton d'API (API Token)** - Une clĂ© secrĂšte gĂ©nĂ©rĂ©e par un logiciel (ici PBS) qui permet Ă  un autre systĂšme (comme Proxmox VE ou Ansible) de s'authentifier et d'exĂ©cuter des actions sans avoir besoin d'utiliser un mot de passe humain. +[^2]: **Snapshot** - Une "photographie" instantanĂ©e de l'Ă©tat d'un disque dur virtuel. Cela permet de sauvegarder une machine en cours de fonctionnement sans devoir l'Ă©teindre, car la sauvegarde s'effectue sur l'Ă©tat figĂ© par le snapshot. +[^3]: **DĂ©duplication** - MĂ©canisme intelligent qui dĂ©coupe les fichiers en petits morceaux. Si le serveur de sauvegarde possĂšde dĂ©jĂ  un morceau identique (ex: un fichier systĂšme de Linux), il ne le transfĂšre pas et ne le stocke pas une seconde fois, Ă©conomisant drastiquement l'espace disque. +[^4]: **WebDAV** - Une extension du protocole web classique (HTTP/HTTPS) qui permet de transformer un serveur distant (comme Nextcloud) en un disque dur virtuel sur lequel on peut lire, Ă©crire et modifier des fichiers. +[^5]: **Garbage Collection (Ramasse-miettes)** - Un processus de maintenance interne Ă  PBS qui scanne les disques durs pour trouver et supprimer dĂ©finitivement les morceaux de donnĂ©es qui n'appartiennent plus Ă  aucune sauvegarde valide. \ No newline at end of file diff --git a/src/content/docs/homelab/sauvegarde/theorie-proxmox-backup-server.md b/src/content/docs/homelab/sauvegarde/theorie-proxmox-backup-server.md new file mode 100644 index 0000000..b9809bc --- /dev/null +++ b/src/content/docs/homelab/sauvegarde/theorie-proxmox-backup-server.md @@ -0,0 +1,46 @@ +--- +title: "ThĂ©orie - Proxmox Backup Server" +description: "Cours et concepts fondamentaux autour de Proxmox Backup Server." +sidebar: + order: 1 +--- + +## A. PrĂ©sentation + +Proxmox Backup Server (PBS) est une solution de sauvegarde d'entreprise taillĂ©e sur mesure pour les environnements virtualisĂ©s (notamment Proxmox VE). Son but principal est de sauvegarder et de restaurer des machines virtuelles, des conteneurs et des hĂŽtes physiques de la maniĂšre la plus rapide et la plus Ă©conome en espace de stockage possible. Contrairement Ă  une simple copie de fichiers, PBS est conçu pour manipuler intelligemment les donnĂ©es Ă  grande Ă©chelle. + +:::note +Si Proxmox VE est l'usine qui fait tourner vos serveurs, Proxmox Backup Server est le coffre-fort hautement sĂ©curisĂ© et optimisĂ© qui en conserve les plans et l'Ă©tat Ă  l'instant T. +::: + +## B. ProblĂ©matiques rĂ©solues + +* **L'explosion de l'espace de stockage :** Les sauvegardes traditionnelles complĂštes prennent Ă©normĂ©ment de place. PBS rĂ©sout cela grĂące Ă  la dĂ©duplication[^1], qui garantit qu'un mĂȘme bloc de donnĂ©es n'est stockĂ© qu'une seule fois sur le disque, mĂȘme s'il est prĂ©sent sur 50 serveurs diffĂ©rents. +* **Les fenĂȘtres de sauvegarde trop longues :** Copier des tĂ©raoctets de donnĂ©es chaque nuit sature le rĂ©seau et ralentit les disques. PBS utilise des sauvegardes incrĂ©mentielles[^2] rapides, ne transmettant au serveur que les blocs qui ont Ă©tĂ© modifiĂ©s depuis la veille. + +## C. Fonctionnement + +La logique interne de PBS repose sur un dĂ©coupage chirurgical de l'information. Lorsqu'une sauvegarde est lancĂ©e, le systĂšme source ne transfĂšre pas un gros bloc monolithique. Il lit le disque virtuel, le dĂ©coupe en petits morceaux (les *chunks*), calcule leur empreinte numĂ©rique, et demande Ă  PBS : "As-tu dĂ©jĂ  ce morceau ?". Si oui, il ne l'envoie pas. + +* **Le datastore :** Le *Datastore* est le disque physique ou le dossier oĂč sont stockĂ©es les donnĂ©es brutes. +* **Les namespaces :** Les *Namespaces* (espaces de noms) permettent de crĂ©er des dossiers virtuels Ă  l'intĂ©rieur de ce Datastore pour organiser proprement les sauvegardes (ex: sĂ©parer le lab de dĂ©veloppement de la production) sans perdre le bĂ©nĂ©fice de la dĂ©duplication globale. +* **Le garbage collection :** C'est le processus de nettoyage de PBS. Lorsque de vieilles sauvegardes sont supprimĂ©es, leurs "morceaux" restent sur le disque car ils pourraient appartenir Ă  d'autres sauvegardes plus rĂ©centes. Le *Garbage Collection* inspecte tout le stockage et supprime physiquement les blocs devenus totalement orphelins. + +## D. Exemples + +* **Restauration granulaire d'un fichier :** Un utilisateur supprime par erreur un fichier critique dans une machine virtuelle Linux (VLAN Utilisateur). Au lieu de restaurer les 50 Go de la machine virtuelle entiĂšre, l'administrateur explore le contenu de la sauvegarde directement depuis l'interface PBS, tĂ©lĂ©charge uniquement le fichier `.conf` manquant et le rĂ©injecte. +* **Sauvegarde hors-site optimisĂ©e :** Dans le cadre de LoutikCLOUD, un serveur PBS local sauvegarde les machines tous les soirs. PBS est capable de se synchroniser de maniĂšre chiffrĂ©e avec un second PBS (ou un stockage externe) situĂ© dans le cloud. Comme il ne transfĂšre que les blocs dĂ©dupliquĂ©s modifiĂ©s, la synchronisation ne prend que quelques secondes mĂȘme sur une petite connexion internet. + +## E. Bonnes pratiques + +| RĂšgle | CatĂ©gorie | Justification | +| :--- | :--- | :--- | +| **Chiffrement cĂŽtĂ© client** | SĂ©curitĂ© | Les donnĂ©es doivent ĂȘtre chiffrĂ©es par le serveur source (Proxmox VE) *avant* de transiter sur le rĂ©seau et d'ĂȘtre stockĂ©es sur PBS. Ainsi, mĂȘme si le serveur de backup est compromis ou volĂ©, les donnĂ©es sont illisibles sans la clĂ© cryptographique[^3]. | +| **Automatisation du Pruning et du GC** | Maintenance | Il faut configurer des tĂąches planifiĂ©es pour le *Prune* (la suppression des vieilles sauvegardes selon une politique de rĂ©tention) suivi du *Garbage Collection*. Sans cela, le serveur finira inĂ©luctablement par saturer son espace disque. | +| **SĂ©grĂ©gation rĂ©seau (VLAN dĂ©diĂ©)** | SĂ©curitĂ© / Performance | Le trafic de sauvegarde est massif et critique. Placer PBS sur un VLAN de management dĂ©diĂ© isole les donnĂ©es sensibles du trafic public et empĂȘche la saturation de la bande passante allouĂ©e aux applications des utilisateurs. | + +--- + +[^1]: **DĂ©duplication** - Technique informatique consistant Ă  identifier les donnĂ©es rĂ©pĂ©tĂ©es en double (ou en mille exemplaires) et Ă  n'en conserver qu'une seule copie physique pour Ă©conomiser drastiquement l'espace sur les disques durs. +[^2]: **Sauvegarde incrĂ©mentielle** - MĂ©thode de sauvegarde qui consiste Ă  ne sauvegarder que les donnĂ©es qui ont Ă©tĂ© ajoutĂ©es ou modifiĂ©es depuis la toute derniĂšre sauvegarde effectuĂ©e, ce qui fait gagner beaucoup de temps. +[^3]: **ClĂ© cryptographique** - Une sorte de mot de passe mathĂ©matique extrĂȘmement long et complexe, indispensable pour verrouiller (chiffrer) ou dĂ©verrouiller (dĂ©chiffrer) des donnĂ©es confidentielles. \ No newline at end of file diff --git a/src/content/docs/homelab/sauvegarde/theorie-webdav.md b/src/content/docs/homelab/sauvegarde/theorie-webdav.md new file mode 100644 index 0000000..26847d2 --- /dev/null +++ b/src/content/docs/homelab/sauvegarde/theorie-webdav.md @@ -0,0 +1,45 @@ +--- +title: "ThĂ©orie - Sauvegarde WebDAV (Nextcloud)" +description: "Cours et concepts fondamentaux autour de la sauvegarde hors site via WebDAV sur Nextcloud." +sidebar: + order: 1 +--- + +## A. PrĂ©sentation + +La sauvegarde WebDAV vers Nextcloud est une stratĂ©gie d'externalisation des donnĂ©es critiques. ConcrĂštement, cela consiste Ă  utiliser un protocole standard d'Internet (WebDAV) pour envoyer de maniĂšre sĂ©curisĂ©e des copies de vos serveurs locaux vers un espace de stockage situĂ© dans le cloud (ici, une instance Nextcloud). L'objectif est d'avoir un "plan B" robuste, totalement indĂ©pendant physiquement et gĂ©ographiquement de votre infrastructure principale LoutikCLOUD. + +:::note +Dans le jargon, on appelle cela du "stockage hors site" (off-site backup). Si un sinistre grave survient localement (incendie, vol, inondation, cyberattaque destructrice), vos donnĂ©es restent saines et sauves ailleurs. +::: + +## B. ProblĂ©matiques rĂ©solues + +* **VulnĂ©rabilitĂ© aux sinistres locaux :** RĂ©sout le risque de perte totale des donnĂ©es si le matĂ©riel physique du homelab est dĂ©truit ou volĂ©. L'outil permet d'appliquer la fameuse rĂšgle d'or du "3-2-1" en informatique (au moins une copie Ă  l'extĂ©rieur). +* **DĂ©pendance aux solutions propriĂ©taires :** Évite de s'enfermer dans un Ă©cosystĂšme de sauvegarde fermĂ©. WebDAV est un standard universel et open-source ; n'importe quel systĂšme (Linux, Windows, routeurs) sait communiquer avec lui sans nĂ©cessiter de logiciels coĂ»teux ou complexes. + +## C. Fonctionnement + +Le processus repose sur un dialogue asynchrone entre un outil d'expĂ©dition local (souvent un utilitaire en ligne de commande comme Rclone[^1]) et le serveur de rĂ©ception cloud. Le serveur local prĂ©pare l'archive de sauvegarde, ouvre un tunnel sĂ©curisĂ© vers le cloud, et utilise les commandes Ă©tendues du web pour y dĂ©poser le fichier. + +* **Le protocole WebDAV[^2] :** C'est le transporteur. C'est une simple extension du langage classique du web (HTTP/HTTPS) qui permet non plus seulement de *lire* des pages, mais aussi de *crĂ©er, modifier ou dĂ©placer* des fichiers directement sur un serveur distant. +* **Le point de terminaison (Nextcloud) :** C'est le coffre-fort de destination. Il reçoit les requĂȘtes WebDAV, stocke les fichiers sur ses propres disques de maniĂšre organisĂ©e, et gĂšre les droits d'accĂšs. + +## D. Exemples + +* **Externalisation des configurations vitales :** Chaque nuit, le routeur OPNsense gĂ©nĂšre un fichier XML contenant toutes ses rĂšgles de sĂ©curitĂ©. Un script l'expĂ©die via WebDAV sur le Nextcloud distant. En cas de panne matĂ©rielle du routeur, la configuration peut ĂȘtre retĂ©lĂ©chargĂ©e depuis le cloud depuis n'importe quel ordinateur pour remonter l'infrastructure. +* **Sauvegarde "Ă  froid" des bases de donnĂ©es :** Les sauvegardes (dumps) de la base de donnĂ©es PostgreSQL de l'infrastructure sont chiffrĂ©es localement puis envoyĂ©es sur Nextcloud. Ces archives "froides"[^3] sont conservĂ©es pour une longue durĂ©e, au cas oĂč une corruption de donnĂ©es ne serait dĂ©tectĂ©e que plusieurs mois plus tard. + +## E. Bonnes pratiques + +| RĂšgle | CatĂ©gorie | Justification | +| :--- | :--- | :--- | +| **Chiffrement cĂŽtĂ© client** | SĂ©curitĂ© | Les donnĂ©es doivent impĂ©rativement ĂȘtre chiffrĂ©es (verrouillĂ©es avec une clĂ© secrĂšte) *avant* de quitter l'infrastructure locale. Ainsi, mĂȘme si le serveur cloud Nextcloud est compromis, les fichiers y seront illisibles. | +| **Utilisation de mots de passe d'application** | SĂ©curitĂ© | Il ne faut jamais utiliser le mot de passe de votre compte administrateur principal pour configurer la sauvegarde. CrĂ©ez un jeton d'accĂšs (token) dĂ©diĂ© uniquement Ă  cette tĂąche, qui pourra ĂȘtre rĂ©voquĂ© instantanĂ©ment en cas de fuite. | +| **RĂ©tention et rotation (Pruning)** | Maintenance | L'espace cloud coĂ»te cher. Il faut automatiser la suppression des sauvegardes les plus anciennes (ex: garder les 7 derniers jours et les 4 derniĂšres semaines) pour Ă©viter que le stockage Nextcloud n'arrive Ă  saturation et bloque les futures sauvegardes. | + +--- + +[^1]: **Rclone** - Un programme informatique trĂšs puissant, comparable Ă  un couteau suisse, qui permet de synchroniser et de transfĂ©rer des fichiers entre votre ordinateur et plus de 40 fournisseurs de stockage cloud diffĂ©rents. +[^2]: **WebDAV (Web Distributed Authoring and Versioning)** - Une technologie standard qui transforme un serveur web (Internet) en un disque dur rĂ©seau classique. On peut y copier/coller des fichiers comme sur une clĂ© USB. +[^3]: **Stockage Ă  froid (Cold Storage)** - Un espace de stockage destinĂ© aux donnĂ©es que l'on n'a presque jamais besoin de lire ou de modifier au quotidien, mais que l'on doit conserver prĂ©cieusement sur le long terme au cas oĂč (les archives). \ No newline at end of file diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx new file mode 100644 index 0000000..e07b217 --- /dev/null +++ b/src/content/docs/index.mdx @@ -0,0 +1,23 @@ +--- +title: Homepage +description: Documentation technique, infrastructures et projets BTS SIO. +template: splash +--- + +import AccueilLoutik from '../../components/AccueilLoutik.astro'; + + + + \ No newline at end of file diff --git a/src/content/docs/introduction/assets/photo_louismedo.jpg b/src/content/docs/introduction/assets/photo_louismedo.jpg new file mode 100644 index 0000000..ab27a22 Binary files /dev/null and b/src/content/docs/introduction/assets/photo_louismedo.jpg differ diff --git a/src/content/docs/introduction/bienvenue.mdx b/src/content/docs/introduction/bienvenue.mdx new file mode 100644 index 0000000..202c903 --- /dev/null +++ b/src/content/docs/introduction/bienvenue.mdx @@ -0,0 +1,48 @@ +--- +title: Bienvenue 👋 +description: A guide in my new Starlight docs site. +--- + +import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components'; + +
+ Photo de Louis MEDO +
+ ### Qui suis-je ? + Je m'appelle Louis MEDO, et je suis un passionné d'informatique. Ma nature curieuse m'a toujours poussé à comprendre le fonctionnement des choses, des frigos aux ordinateurs. J'ai commencé par le hardware : monter des PC, comprendre le stockage... Puis est venu le déclic au collÚge avec la découverte d'un logiciel de vie scolaire bien connu, Pronote. Ma curiosité a été piquée au vif : comment cela fonctionnait-il ? En cherchant, j'ai découvert qu'on pouvait l'auto-héberger. Ma vocation de sysadmin était née. +
+
+ +## Mais qu'est-ce que Loutik au juste ? + +Loutik est un projet personnel que j'ai créé pour nourrir ma passion pour l'ingĂ©nierie systĂšme. Mon objectif est de simuler un vĂ©ritable environnement de production pour hĂ©berger des outils open source, tout en respectant l'Ă©tat de l'art de l'industrie (SRE, FinOps, GreenOps). C'est mon laboratoire d'expĂ©rimentation pour "casser proprement" et apprendre Ă  rĂ©parer. + +Loutik est l'aboutissement de ce parcours. C'est ma plateforme pour concevoir une infrastructure d'entreprise, de la documentation as Code au dĂ©ploiement des services. Outre la technique, je souhaite Ă©galement hĂ©berger des projets open source pour permettre au plus grand nombre de tester ces outils gratuitement. Mon objectif Ă  terme est de travailler en tant qu'architecte d'infrastructure, de penser des infrastructures rĂ©silientes, sĂ©curisĂ©es et souveraines. + +## Pourquoi faire une documentation ? + +Pour moi, la documentation est la fondation d'une infrastructure. Sans organisation, un systĂšme s'effondrera Ă  la premiĂšre dĂ©faillance de production, avec des consĂ©quences lourdes (j'en ai fait l'expĂ©rience, quand un disque meurt et que tu perds tes vm...). C'est pourquoi j'applique la mĂ©thode "Docs-as-Code". Écrire ces guides me permet de valider mes acquis, de partager mes dĂ©couvertes, et de garantir la reproductibilitĂ© de mon environnement. + +## Comment naviguer Ă  travers ma documentation ? + +La documentation est composĂ©e de 3 sections principales : + + + + Documente l'intĂ©gralitĂ© de mon infrastructure personnelle. Vous y trouverez les registres de dĂ©cisions d'architecture justifiant mes choix d'hyperviseurs et d'orchestrateurs, ainsi que les topologies rĂ©seau et les stratĂ©gies de conception. +

+ +
+ + + Une base de connaissances dédiée à la compréhension théorique et pratique des technologies exploitées (conteneurisation, LLM, etc.). C'est ici que je démystifie les paradigmes informatiques et les concepts d'ingénierie. +

+ +
+ + + Présentation des infrastructures et solutions techniques réalisées dans le cadre de ma formation (ex: projets Millenuits, Imdeo). Analyse des cahiers des charges, justifications techniques et cartographies réseau. +

+ +
+
\ No newline at end of file diff --git a/src/content/docs/notions/ansible/qu'est-ce-que-ansible.md b/src/content/docs/notions/ansible/qu'est-ce-que-ansible.md new file mode 100644 index 0000000..726b93f --- /dev/null +++ b/src/content/docs/notions/ansible/qu'est-ce-que-ansible.md @@ -0,0 +1,100 @@ +--- +title: "Qu'est-ce que Ansible ?" +description: "DĂ©finition de l'outil et vue d'ensemble sur Ansible" +--- + +## 1. PrĂ©sentation gĂ©nĂ©rale + +### 1.1 Origine + +Créé par Michael DeHaan en 2012, rachetĂ© par Red Hat en 2015. C'est un outil majoritairement open-source (avec une version entreprise propriĂ©taire nommĂ©e Ansible Automation Platform). + +### 1.2 DĂ©finition technique + +Ansible est un outil de gestion de configuration (Configuration Management) et d'automatisation des dĂ©ploiements en mode dĂ©claratif, fonctionnant sans agent (agentless). + +### 1.3 La problĂ©matique rĂ©solue + +Historiquement, les administrateurs exĂ©cutaient manuellement des scripts Bash serveur par serveur, crĂ©ant une maintenance complexe et un fort taux d'erreur humaine. Ansible remplace ces scripts par une approche automatisĂ©e et idempotente, rĂ©duisant massivement le "toil" (travail manuel rĂ©pĂ©titif) sans nĂ©cessiter l'installation de logiciels supplĂ©mentaires sur les serveurs cibles. + +--- + +## 2. Concepts clĂ©s et Vocabulaire + +* **Playbook :** Fichier Ă©crit en YAML dĂ©crivant l'Ă©tat dĂ©sirĂ© de l'infrastructure. C'est la "recette" qui contient la liste des tĂąches Ă  exĂ©cuter. +* **Inventory (Inventaire) :** Fichier (format INI ou YAML) listant les adresses IP ou les noms DNS des serveurs cibles, souvent organisĂ©s par groupes (ex: web, bdd). +* **Module :** UnitĂ© de code (souvent en Python) exĂ©cutant une action trĂšs spĂ©cifique (ex: `apt` pour gĂ©rer un paquet, `service` pour dĂ©marrer un dĂ©mon, `copy` pour transfĂ©rer un fichier). +* **Task (TĂąche) :** L'appel d'un module unique avec ses paramĂštres spĂ©cifiques au sein d'un Playbook. +* **Role (RĂŽle) :** Structure de dossiers standardisĂ©e permettant de regrouper et de rĂ©utiliser des variables, des tĂąches et des fichiers (modularisation du code). + +--- + +## 3. Architecture et Fonctionnement + +### 3.1 MĂ©canique interne + +Ansible fonctionne selon un modĂšle "Push" et "Agentless". Depuis un nƓud de contrĂŽle (Control Node), il lit le Playbook et l'inventaire, gĂ©nĂšre des mini-scripts Python correspondants aux tĂąches, et se connecte aux nƓuds cibles (Managed Nodes) via le protocole standard SSH (ou WinRM pour Windows). Il transfĂšre ces scripts, les exĂ©cute en mĂ©moire pour atteindre l'Ă©tat dĂ©sirĂ© (idempotence : si l'Ă©tat est dĂ©jĂ  correct, aucune action n'est effectuĂ©e), puis se dĂ©connecte et supprime les scripts temporaires, garantissant une empreinte nulle sur la cible. + +### 3.2 SchĂ©ma d'architecture + +```text ++----------------+ git pull +-------------------+ +| DĂ©veloppeur/CI | -----------------> | NƓud de ContrĂŽle | +| (Git, GitLab) | | (Ansible installĂ©)| ++----------------+ +-------------------+ + | | | (ModĂšle Push) + +-----------+ | +-----------+ + SSH / | | SSH | \ WinRM + Python| | Python | \ PowerShell + +-------v-------+ +-----v---------+ +---v-----------+ + | Serveur Linux | | Serveur Linux | | Serveur Windows| + | (Groupe: Web) | | (Groupe: BDD) | | (Groupe: AD) | + +---------------+ +---------------+ +----------------+ +``` + +--- + +## 4. Cas d'utilisation concret + +**Contexte :** Une entreprise doit dĂ©ployer un cluster de serveurs web Nginx avec une configuration sĂ©curisĂ©e et des certificats SSL fraĂźchement gĂ©nĂ©rĂ©s. + +**Mise en place SRE :** Au lieu de configurer chaque machine Ă  la main, un pipeline CI/CD (ex: GitLab CI) dĂ©clenche automatiquement un Playbook Ansible lors d'un "commit". Ansible rĂ©cupĂšre les certificats chiffrĂ©s depuis un coffre-fort (Vault), se connecte aux nouvelles machines virtuelles via SSH, installe Nginx, pousse le fichier de configuration et redĂ©marre le service. Si le Playbook est relancĂ© le lendemain, Ansible dĂ©tecte que Nginx est dĂ©jĂ  installĂ© et configurĂ© (idempotence) et ne modifie rien, garantissant la fiabilitĂ© et la prĂ©dictibilitĂ© de l'infrastructure sans intervention humaine. + +--- + +## 5. Mise en pratique (Pareto 80/20) + +* `ansible-playbook -i inventory.ini deploy.yml --check` : ExĂ©cute un Playbook complet sur une infrastructure. + * `-i inventory.ini` : Indique le chemin vers le fichier d'inventaire dĂ©finissant les cibles. + * `deploy.yml` : SpĂ©cifie le fichier YAML contenant les tĂąches Ă  exĂ©cuter. + * `--check` : Lance l'exĂ©cution en mode "Dry Run" (simulation). Valide ce qui *serait* modifiĂ© sans appliquer les changements rĂ©ellement (essentiel pour la sĂ©curitĂ© et les revues de code). + +* `ansible all -m ping` : Commande ad-hoc pour vĂ©rifier la communication avec les cibles. + * `all` : Cible absolument tous les serveurs listĂ©s dans l'inventaire. + * `-m ping` : Fait appel au module `ping` pour vĂ©rifier la connectivitĂ© SSH et s'assurer qu'un interprĂ©teur Python est utilisable sur la cible. + +* `ansible-vault encrypt secrets.yml` : Chiffre un fichier contenant des donnĂ©es sensibles (mots de passe, tokens API). + * `encrypt` : Action ordonnant le chiffrement au format AES256. + * `secrets.yml` : Fichier cible qui sera remplacĂ© par sa version chiffrĂ©e. + +* `ansible-playbook site.yml --tags "nginx" --limit "prod"` : ExĂ©cution trĂšs ciblĂ©e pour gagner du temps et limiter les risques. + * `site.yml` : Playbook global de l'entreprise. + * `--tags "nginx"` : Ignore toutes les tĂąches sauf celles explicitement marquĂ©es avec l'Ă©tiquette "nginx". + * `--limit "prod"` : Restreint l'exĂ©cution au seul groupe d'hĂŽtes nommĂ© "prod" dans l'inventaire. + +* `ansible-galaxy install -r requirements.yml` : TĂ©lĂ©charge et installe des rĂŽles externes (comme un gestionnaire de paquets). + * `install` : Action d'installation de composants. + * `-r requirements.yml` : Pointe vers le fichier listant les dĂ©pendances (noms des rĂŽles et versions) Ă  tĂ©lĂ©charger depuis le dĂ©pĂŽt central Ansible Galaxy ou un dĂ©pĂŽt Git. + +--- + +## 6. État de l'art et Bonnes Pratiques + +* **SĂ©curitĂ© :** + * Ne jamais versionner de secrets en clair sur Git. Toujours utiliser `Ansible Vault` pour chiffrer les variables sensibles, ou coupler Ansible avec des outils externes spĂ©cialisĂ©s comme HashiCorp Vault. + +* **MaintenabilitĂ© :** + * Toujours dĂ©couper ses Playbooks monolithiques en **RĂŽles** indĂ©pendants, versionnĂ©s et rĂ©utilisables, en suivant scrupuleusement la structure de dossiers standard d'Ansible. + +* **Anti-pattern :** + * Abuser des modules `command` ou `shell`. ExĂ©cuter des commandes Linux brutes (ex: `shell: apt-get install nginx`) casse l'idempotence native d'Ansible, car le script tentera de se rĂ©installer Ă  chaque exĂ©cution. Il faut impĂ©rativement utiliser les modules dĂ©diĂ©s (ex: module `apt`, `yum` ou `package`) qui vĂ©rifient d'abord l'Ă©tat du systĂšme. \ No newline at end of file diff --git a/src/content/docs/notions/cloud/03-externalisation-herbergement-web.md b/src/content/docs/notions/cloud/03-externalisation-herbergement-web.md new file mode 100644 index 0000000..5f3253d --- /dev/null +++ b/src/content/docs/notions/cloud/03-externalisation-herbergement-web.md @@ -0,0 +1,54 @@ +--- +title: Externalisation et hĂ©bergement Web +description: SynthĂšse sur l'externalisation et l'hĂ©bergement Web. Cours BTS SIO - Paul-louis Courier +--- + +## PrĂ©sentation de la problĂ©matique + +L'externalisation consiste Ă  confier l'hĂ©bergement et la gestion d'applications ou d'infrastructures Ă  un prestataire tiers. Cette dĂ©marche soulĂšve plusieurs enjeux techniques, juridiques et financiers. + +Dans le cas du groupe HervĂ© Consultant, la question se pose pour leur site vitrine, dĂ©veloppĂ© en interne sous Joomla 6. Ce site traite des donnĂ©es personnelles, imposant une conformitĂ© stricte au RGPD. Le choix de l'hĂ©bergement (DĂ©diĂ©, VPS, MutualisĂ©, CDN) doit permettre au dĂ©veloppeur d'accĂ©der aux fichiers web (HTML/CSS/PHP) et Ă  la base de donnĂ©es MariaDB Ă  tout moment , tout en garantissant la sĂ©curitĂ© (authentification multifacteur, protection DoS/DDoS) et la disponibilitĂ© via des engagements contractuels forts. + +## Les solutions + +Voici un rĂ©capitulatif des solutions d'hĂ©bergement envisageables pour une application web telle que le site vitrine du groupe. + +|**Type d'hĂ©bergement**|**Description**|**Avantages**|**InconvĂ©nients**| +|---|---|---|---| +|**MutualisĂ©**|Ressources partagĂ©es entre plusieurs clients sur un mĂȘme serveur.|Économique, maintenance gĂ©rĂ©e par l'hĂ©bergeur.|Performances limitĂ©es, aucun accĂšs d'administration profond.| +|**VPS (Serveur PrivĂ© Virtuel)**|Machine virtuelle allouĂ©e Ă  un client avec ses propres ressources dĂ©diĂ©es.|Bon compromis coĂ»t/performance, libertĂ© d'administration (accĂšs root).|NĂ©cessite des compĂ©tences en administration systĂšme.| +|**Serveur DĂ©diĂ©**|Serveur physique entiĂšrement louĂ© par un seul client.|Puissance maximale, contrĂŽle total, isolement physique.|CoĂ»t Ă©levĂ©, gestion matĂ©rielle et logicielle Ă  la charge du client.| +|**CDN (Content Delivery Network)**|RĂ©seau de serveurs distribuĂ©s pour mettre en cache le contenu (ex: Cloudflare, Akamai).|AccĂ©lĂšre le chargement mondial, offre une protection DoS/DDoS.|Ne remplace pas l'hĂ©bergement principal, ajoute une couche de complexitĂ©.| + +## Les concepts + +### Les modĂšles de services Cloud (IaaS, PaaS, SaaS) + +L'externalisation s'appuie souvent sur le Cloud Computing, divisĂ© en trois modĂšles : + +- **IaaS (Infrastructure as a Service) :** Le fournisseur loue le matĂ©riel (serveurs, rĂ©seau, stockage). Vous gĂ©rez l'OS et les applications (ex: un VPS ou un serveur dĂ©diĂ©). +- **PaaS (Platform as a Service) :** Le fournisseur gĂšre le matĂ©riel et l'environnement d'exĂ©cution (OS, bases de donnĂ©es). Vous vous concentrez uniquement sur le code et l'application. +- **SaaS (Software as a Service) :** L'application est livrĂ©e clĂ© en main et accessible via un navigateur web (ex: un webmail). L'hĂ©bergeur gĂšre tout. + +### Le choix de l'hĂ©bergement pour le site vitrine + +Pour le groupe HervĂ© Consultant, le site repose sur NGINX, PHP 8.3 et MariaDB 11. Le dĂ©veloppeur exige de pouvoir modifier les fichiers sources et accĂ©der Ă  la base de donnĂ©es librement. + +- **Le serveur mutualisĂ©** est Ă  proscrire car il restreint trop l'accĂšs Ă  la configuration du serveur web. +- **Le VPS** est la solution la plus pertinente. Il offre l'accĂšs complet nĂ©cessaire au dĂ©veloppeur pour gĂ©rer NGINX et MariaDB, tout en gardant un coĂ»t maĂźtrisĂ© pour un site vitrine. +- Un **CDN** peut ĂȘtre ajoutĂ© en façade pour assurer la protection DoS et DDoS requise. + +### Les garanties contractuelles (SLA et RĂ©versibilitĂ©) + +Lorsqu'on externalise, le contrat est le seul filet de sĂ©curitĂ©. + +- **SLA (Service Level Agreement) :** C'est l'engagement de niveau de service. Le prestataire doit ici garantir une disponibilitĂ© (uptime) de 99 % par an. Des pĂ©nalitĂ©s financiĂšres s'appliquent si ce seuil n'est pas atteint. +- **Clause de rĂ©versibilitĂ© :** Elle est indispensable. Elle oblige l'hĂ©bergeur Ă  restituer l'intĂ©gralitĂ© des donnĂ©es et de l'application (Joomla et MariaDB) dans un format exploitable Ă  la fin du contrat. Cela Ă©vite la dĂ©pendance (vendor lock-in). + +### SĂ©curitĂ© et Sauvegarde ExternalisĂ©e + +Externaliser ne dispense pas de sĂ©curiser. L'hĂ©bergeur doit permettre une stratĂ©gie de sauvegarde stricte (rĂšgle du 3-2-1) et protĂ©ger l'accĂšs Ă  ses plateformes par une authentification multifacteur (MFA). De plus, pour respecter le RGPD, l'accĂšs aux donnĂ©es personnelles de la base de donnĂ©es doit ĂȘtre strictement limitĂ© aux personnes autorisĂ©es. + +## Conclusion + +L'externalisation est pertinente lorsqu'une entreprise cherche Ă  garantir une haute disponibilitĂ© sans investir massivement dans du matĂ©riel physique. Pour un site vitrine nĂ©cessitant de la flexibilitĂ©, un serveur VPS couplĂ© Ă  un CDN reprĂ©sente le meilleur choix technique. Cependant, la rĂ©ussite de ce projet repose avant tout sur un contrat solide, exigeant un SLA de 99 % , une clause de rĂ©versibilitĂ© claire et un hĂ©bergeur conforme aux exigences du RGPD. \ No newline at end of file diff --git a/src/content/docs/notions/conteneurisation/conteneur.md b/src/content/docs/notions/conteneurisation/conteneur.md new file mode 100644 index 0000000..242ec26 --- /dev/null +++ b/src/content/docs/notions/conteneurisation/conteneur.md @@ -0,0 +1,95 @@ +--- +title: "Conteneur Docker" +description: "Fiche technique sur la containerisation Docker pour administrateurs systĂšme et Ă©tudiants BTS SIO" +--- + +## DĂ©finition +Un conteneur Docker est une unitĂ© d'exĂ©cution standardisĂ©e empaquetant une application avec ses dĂ©pendances, bibliothĂšques et configuration dans un environnement isolĂ© au niveau du systĂšme d'exploitation. Il partage le noyau Linux de l'hĂŽte tout en maintenant une sĂ©paration des espaces de noms (namespaces) et des groupes de contrĂŽle (cgroups). Cette technologie se positionne entre la virtualisation traditionnelle et le dĂ©ploiement natif, offrant une granularitĂ© fine pour l'orchestration et le cycle de vie applicatif. + +## Le problĂšme rĂ©solu +L'incompatibilitĂ© des environnements entre dĂ©veloppement, test et production gĂ©nĂšre des incidents rĂ©currents et des temps de rĂ©solution prolongĂ©s. + +| Avant (Le problĂšme) | Avec Docker (La solution) | +|---|---| +| IncohĂ©rence des dĂ©pendances entre environnements | Image immuable garantissant l'identitĂ© binaire du dev Ă  la prod | +| Provisionnement lent des serveurs (minutes/heures) | Instantiation de conteneurs en quelques secondes | +| Conflits de versions de bibliothĂšques sur un mĂȘme hĂŽte | Isolation par conteneur permettant des versions multiples coexistantes | +| Surcharge resource des machines virtuelles complĂštes | Partage du noyau hĂŽte rĂ©duisant l'overhead mĂ©moire et CPU | + +## Comment ça fonctionne concrĂštement +L'architecture Docker repose sur un modĂšle client-serveur avec les composants suivants : + +- **Docker Daemon (dockerd)** : Service systĂšme gĂ©rant la crĂ©ation, l'exĂ©cution et la distribution des conteneurs +- **Docker Client** : Interface CLI communiquant avec le daemon via l'API REST +- **Registry** : DĂ©pĂŽt centralisĂ© d'images (Docker Hub, registry privĂ©) +- **Image** : Template en lecture seule constituĂ© de couches empilĂ©es (layers) +- **Conteneur** : Instance exĂ©cutable d'une image avec une couche writable Ă©phĂ©mĂšre + +Cycle de vie d'un conteneur : +1. Pull de l'image depuis un registry vers le daemon local +2. CrĂ©ation de la couche writable et allocation des namespaces (PID, network, mount, user, IPC, UTS) +3. Application des cgroups pour la limitation des ressources (CPU, mĂ©moire, I/O) +4. ExĂ©cution du processus dĂ©fini par l'instruction CMD ou ENTRYPOINT +5. Supervision du processus PID 1 et collecte des logs stdout/stderr +6. LibĂ©ration des ressources Ă  l'arrĂȘt ou suppression du conteneur + +## Vocabulaire technique +- **Image** : Artefact binaire immuable contenant le filesystem et les mĂ©tadonnĂ©es d'exĂ©cution +- **Dockerfile** : Fichier de manifeste dĂ©claratif dĂ©finissant les instructions de build d'une image +- **Layer** : Couche de filesystem en lecture seule rĂ©sultant d'une instruction du Dockerfile +- **Volume** : MĂ©canisme de persistance des donnĂ©es montĂ© dans le filesystem du conteneur +- **Network bridge** : RĂ©seau virtuel NATĂ© permettant la communication inter-conteneurs sur un mĂȘme hĂŽte + +## Exemple concret +```dockerfile +# Dockerfile pour une application Node.js en production +FROM node:20-alpine AS builder + +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only=production + +FROM node:20-alpine AS runtime + +# CrĂ©ation d'un utilisateur non-root pour la sĂ©curitĂ© +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 + +WORKDIR /app +COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules +COPY --chown=nodejs:nodejs . . + +USER nodejs +EXPOSE 3000 + +# HEALTHCHECK pour l'orchestration +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 + +CMD ["node", "server.js"] +``` + +```bash +# Commandes d'administration systĂšme courantes +docker build -t monapp:1.0.0 --no-cache . +docker run -d --name monapp_prod \ + --restart unless-stopped \ + --memory="512m" --cpus="1.0" \ + --log-driver=json-file --log-opt max-size=10m \ + -p 8080:3000 \ + monapp:1.0.0 +``` + +## Production : Bonnes pratiques et Anti-patterns +| Ce qu'il faut faire (Bonne pratique) | Ce qu'il faut fuir (Anti-pattern) | Pourquoi ? | +|---|---|---| +| Utiliser des images minimales (alpine, distroless) | Images complĂštes avec shells et outils inutiles | RĂ©duction de la surface d'attaque et du temps de pull | +| ExĂ©cuter les processus en non-root (USER directive) | Lancement systĂ©matique avec l'utilisateur root | Limitation des privilĂšges en cas de compromission du conteneur | +| Monter les secrets via Docker Swarm secrets ou volumes | Hardcoder les credentials dans l'image ou variables d'environnement | Les secrets dans les images persistent dans l'historique des layers | +| DĂ©finir des HEALTHCHECK pour chaque service | Compter uniquement sur le statut du processus PID 1 | Un processus peut tourner sans que le service soit fonctionnel | +| Limiter les ressources via --memory et --cpus | Laisser les conteneurs consommer sans quota | Évite le dĂ©ni de service par Ă©puisement des ressources de l'hĂŽte | + +## L'essentiel Ă  retenir +- Une image est immuable ; un conteneur est une instance exĂ©cutable avec une couche writable Ă©phĂ©mĂšre +- L'isolation repose sur les namespaces et cgroups du noyau Linux, pas sur une virtualisation matĂ©rielle +- La sĂ©curitĂ© en production exige : images minimales, utilisateur non-root, ressources limitĂ©es, secrets externalisĂ©s \ No newline at end of file diff --git a/src/content/docs/notions/conteneurisation/dockerfile.md b/src/content/docs/notions/conteneurisation/dockerfile.md new file mode 100644 index 0000000..eae6b6a --- /dev/null +++ b/src/content/docs/notions/conteneurisation/dockerfile.md @@ -0,0 +1,90 @@ +--- +title: "Dockerfile" +description: "Fiche technique sur le Dockerfile : dĂ©finition, fonctionnement, architecture des couches, directive de build et bonnes pratiques DevSecOps pour la production." +--- + +## DĂ©finition +Un Dockerfile est un fichier texte brut contenant la sĂ©quence d'instructions dĂ©claratives nĂ©cessaires pour assembler une image de conteneur. Il agit comme un script d'automatisation de l'infrastructure immuable, transformant un systĂšme d'exploitation de base (image parente) en un environnement d'exĂ©cution applicatif isolĂ©. Dans l'Ă©cosystĂšme IT, il reprĂ©sente le pilier de l'approche Infrastructure-as-Code (IaC) pour la conteneurisation, garantissant la reproductibilitĂ© stricte des dĂ©ploiements de l'intĂ©gration continue jusqu'Ă  la production. + +## Le problĂšme rĂ©solu +Historiquement, la configuration des environnements d'exĂ©cution applicatifs souffrait d'un manque de standardisation et de dĂ©rives de configuration entre les serveurs. +| Avant (Le problĂšme) | Avec Dockerfile (La solution) | +|---|---| +| DĂ©ploiement manuel ou scripts shell hĂ©tĂ©rogĂšnes entraĂźnant le syndrome "ça marche sur ma machine" (Configuration Drift). | DĂ©finition dĂ©clarative, immuable et versionnable (Git) de l'environnement d'exĂ©cution garantissant l'identicitĂ© des builds. | +| IncohĂ©rence des dĂ©pendances et bibliothĂšques entre les environnements de dĂ©veloppement, staging et production. | PortabilitĂ© absolue : l'artefact gĂ©nĂ©rĂ© (l'image) embarque ses dĂ©pendances et s'exĂ©cute Ă  l'identique sur n'importe quel moteur (Docker, Podman, containerd). | + +## Comment ça fonctionne concrĂštement +Le processus de build (moteur de conteneurisation) lit le Dockerfile sĂ©quentiellement de haut en bas et exĂ©cute chaque directive dans un conteneur Ă©phĂ©mĂšre. +- **Contexte de build :** Le dĂ©mon reçoit le Dockerfile et les fichiers du rĂ©pertoire courant de l'hĂŽte (le contexte) pour initier la construction des artefacts. +- **SystĂšme de calques (Layers) :** Chaque directive modificatrice du systĂšme de fichiers (`RUN`, `COPY`, `ADD`) gĂ©nĂšre une nouvelle couche immuable en lecture seule au-dessus de la prĂ©cĂ©dente, reposant sur un systĂšme de fichiers de type UnionFS (ex: OverlayFS). +- **Cache de build (Build Cache) :** Le moteur identifie chaque couche par un hash SHA256. Si l'instruction et les fichiers sources n'ont pas Ă©tĂ© modifiĂ©s, le cache est rĂ©utilisĂ© pour Ă©viter une reconstruction coĂ»teuse. +- **Multi-stage build :** Un motif d'architecture permettant de chainer plusieurs directives `FROM` pour isoler la phase de compilation (nĂ©cessitant des outils lourds) de la phase de crĂ©ation de l'artefact final de production (ultra-lĂ©ger). + +## Vocabulaire technique +- **Layer (Couche) :** Modification granulaire du systĂšme de fichiers gĂ©nĂ©rĂ©e par une instruction. Les layers sont empilĂ©s et partagĂ©s entre diffĂ©rentes images pour Ă©conomiser l'espace de stockage. +- **Image parente (Base Image) :** L'image dĂ©finie par l'instruction `FROM` qui sert de point de dĂ©part fondamental (ex: `alpine`, `debian:bullseye-slim`, `scratch`). +- **Entrypoint / CMD :** Directives dĂ©finissant le processus principal (PID 1) exĂ©cutĂ© au dĂ©marrage du conteneur. L'`ENTRYPOINT` verrouille l'exĂ©cutable, tandis que `CMD` fournit les arguments par dĂ©faut (surchargeables Ă  l'exĂ©cution). +- **Distroless :** Concept d'images de production dĂ©pouillĂ©es au maximum, ne contenant ni gestionnaire de paquets (apt, apk), ni shell (bash), ni utilitaires systĂšme superflus. + +## Exemple concret +L'exemple ci-dessous illustre la crĂ©ation d'une image pour une API Python, en appliquant le principe du *Multi-stage build* et l'exĂ©cution sans privilĂšges, optimisĂ© pour un environnement OCI (Docker/Podman). + +```dockerfile +# Étape 1 : Build (Environnement de compilation avec outils nĂ©cessaires) +# Utilisation d'une image officielle lĂ©gĂšre comme base +FROM python:3.11-alpine AS builder + +# DĂ©finit le rĂ©pertoire de travail par dĂ©faut dans le conteneur +WORKDIR /app + +# Copie uniquement le fichier des dĂ©pendances en premier pour maximiser l'utilisation du cache de build +COPY requirements.txt . + +# Installation des dĂ©pendances dans un rĂ©pertoire local utilisateur pour faciliter le transfert ultĂ©rieur +RUN pip install --user --no-cache-dir -r requirements.txt + +# Étape 2 : Production (Environnement minimaliste final d'exĂ©cution) +FROM python:3.11-alpine AS runner + +# CrĂ©ation d'un utilisateur systĂšme non-privilĂ©giĂ© "appuser" pour sĂ©curiser l'exĂ©cution +# -D : Pas de mot de passe, -H : Pas de rĂ©pertoire home par dĂ©faut, -u : Attribution d'un UID explicite (1000) +RUN adduser -D -H -u 1000 appuser + +WORKDIR /app + +# RĂ©cupĂ©ration exclusive des dĂ©pendances prĂ©-compilĂ©es depuis l'Ă©tape 'builder' (isolation des outils de build) +COPY --from=builder /root/.local /home/appuser/.local + +# Copie du code source applicatif depuis le contexte de l'hĂŽte vers le conteneur +COPY src/ . + +# Attribution des droits de propriĂ©tĂ© des fichiers au nouvel utilisateur non-root +RUN chown -R appuser:appuser /app + +# Bascule du contexte d'exĂ©cution : toutes les commandes suivantes (et le PID 1) s'exĂ©cuteront sous cet utilisateur +USER appuser + +# Injection du chemin des binaires Python locaux dans la variable d'environnement systĂšme PATH +ENV PATH=/home/appuser/.local/bin:$PATH + +# DĂ©claration documentaire du port d'Ă©coute rĂ©seau du conteneur +EXPOSE 8080 + +# DĂ©finition de la commande d'exĂ©cution du processus principal (serveur ASGI Uvicorn) +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"] + +``` + +## Production : Bonnes pratiques et Anti-patterns + +| Ce qu'il faut faire (Bonne pratique) | Ce qu'il faut fuir (Anti-pattern) | Pourquoi ? | +| --- | --- | --- | +| DĂ©finir un `USER` non-root explicite avant le processus d'exĂ©cution final. | ExĂ©cuter le conteneur en tant que `root` (comportement par dĂ©faut). | Respecte le principe de moindre privilĂšge. EmpĂȘche l'escalade vers l'hĂŽte si une vulnĂ©rabilitĂ© permet une Ă©vasion du conteneur (container breakout). | +| Utiliser le *Multi-stage build* et des images de base distroless ou Alpine. | Inclure les chaĂźnes de compilation (gcc, make) et outils de debug dans l'image finale. | RĂ©duit drastiquement la surface d'attaque (moins de paquets = moins de CVEs potentielles) et optimise les temps de transfert rĂ©seau (pull/push). | +| ChaĂźner les commandes `RUN` (ex: `apt-get update && apt-get install -y p && rm -rf /var/lib/apt/lists/*`). | Multiplier les directives `RUN` sĂ©parĂ©es pour installer chaque paquet individuellement. | Chaque directive `RUN` crĂ©e une couche immuable. Le chaĂźnage permet de nettoyer les caches temporaires d'installation dans la mĂȘme couche, rĂ©duisant le poids final de l'image. | + +## L'essentiel Ă  retenir + +* Le Dockerfile est un contrat d'infrastructure as code (IaC) qui documente techniquement et garantit l'immutabilitĂ© d'un environnement d'exĂ©cution. +* L'optimisation sĂ©quentielle de l'ordre des instructions est critique : les fichiers changeant le moins (dĂ©pendances) doivent ĂȘtre copiĂ©s en premier pour exploiter le cache des layers. +* La sĂ©curitĂ© d'une image de production exige proactivement l'abandon des droits root, la minimisation stricte de l'OS sous-jacent et le retrait systĂ©matique des outils de compilation via le multi-stage. \ No newline at end of file diff --git a/src/content/docs/notions/dns/fonctionnement-dns.md b/src/content/docs/notions/dns/fonctionnement-dns.md new file mode 100644 index 0000000..46bb9a5 --- /dev/null +++ b/src/content/docs/notions/dns/fonctionnement-dns.md @@ -0,0 +1,60 @@ +--- +title: Fonctionnement du dns (overview) +description: PrĂ©sentation et explication du DNS (Domain Name System) ainsi que de son fonctionnement. +--- + +## PrĂ©sentation du DNS + +Le DNS (Domain Name System) agit comme l'annuaire tĂ©lĂ©phonique d'Internet. Il traduit les noms de domaine comprĂ©hensibles par l'homme (comme `google.fr`) en adresses IP comprĂ©hensibles par les machines (comme `142.250.179.99`), permettant ainsi le routage des paquets sur le rĂ©seau. + +L'organisme principal qui supervise la gestion mondiale des noms de domaine est l'**ICANN** (Internet Corporation for Assigned Names and Numbers). Elle coordonne l'attribution des identifiants uniques (IP et domaines) et dĂ©lĂšgue la gestion des extensions (comme le `.fr`) Ă  des registres spĂ©cifiques (comme l'AFNIC en France). + +Dans les rĂ©seaux actuels, le DNS est une infrastructure critique. Sans lui, la navigation web, l'envoi d'e-mails et la connexion Ă  tout service rĂ©seau nĂ©cessiteraient de mĂ©moriser et de saisir manuellement des suites complexes de nombres (adresses IPv4 et IPv6). Il apporte flexibilitĂ© et tolĂ©rance aux pannes en permettant de changer l'adresse IP d'un serveur sans modifier son nom d'accĂšs. + +## Notions importantes Ă  savoir + +* **Zone DNS :** Espace administratif de l'arborescence DNS dĂ©lĂ©guĂ© Ă  une entitĂ© ou un serveur spĂ©cifique pour la gestion de ses enregistrements. +* **FQDN (Fully Qualified Domain Name) :** Nom de domaine absolu, terminĂ© par un point (ex: `www.loutikcloud.fr.`), spĂ©cifiant sa position exacte dans la hiĂ©rarchie. +* **TTL (Time To Live) :** DurĂ©e (en secondes) pendant laquelle une rĂ©ponse DNS peut ĂȘtre conservĂ©e en cache par un rĂ©solveur avant de devoir ĂȘtre actualisĂ©e. + +**Principaux types d'enregistrements (Records) :** + +* **A :** Associe un nom d'hĂŽte Ă  une adresse IPv4. +* **AAAA :** Associe un nom d'hĂŽte Ă  une adresse IPv6. +* **CNAME (Canonical Name) :** CrĂ©e un alias qui pointe vers un autre nom de domaine, Ă©vitant la duplication d'adresses IP. +* **MX (Mail eXchange) :** Indique le serveur chargĂ© de recevoir les e-mails pour ce domaine. +* **NS (Name Server) :** DĂ©finit les serveurs DNS qui font autoritĂ© pour la zone (ceux qui dĂ©tiennent les fichiers de configuration officiels). +* **TXT :** Contient du texte brut, aujourd'hui massivement utilisĂ© pour valider la propriĂ©tĂ© du domaine et sĂ©curiser les e-mails (SPF, DKIM, DMARC). + +## Fonctionnement du DNS + +Pour rĂ©soudre un nom de domaine en adresse IP, le protocole DNS ne s'appuie pas sur un serveur unique, mais sur une architecture hiĂ©rarchique et distribuĂ©e. Le processus utilise diffĂ©rentes couches de serveurs qui communiquent entre elles en "entonnoir" pour affiner la recherche jusqu'Ă  trouver l'information exacte. + +```mermaid +graph LR + A[Client / Navigateur Web] -->|1. Demande www.domaine.fr| B(RĂ©solveur DNS) + B -->|2. Qui gĂšre .fr ?| C[Serveurs Racines - Root] + C -->|3. Voici l'IP du serveur .fr| B + B -->|4. Qui gĂšre domaine.fr ?| D[Serveurs TLD - Top Level Domain] + D -->|5. Voici l'IP du serveur d'autoritĂ©| B + B -->|6. Quelle est l'IP de www ?| E[Serveurs d'AutoritĂ©] + E -->|7. Voici l'IP de www.domaine.fr| B + B -->|8. RĂ©ponse IP envoyĂ©e au client| A +``` + +* **Le RĂ©solveur DNS :** C'est le serveur (souvent fourni par le FAI ou configurĂ© en local comme Unbound) qui reçoit la requĂȘte de votre machine. Il se charge de faire le travail de recherche de maniĂšre rĂ©cursive en interrogeant les couches supĂ©rieures. +* **Les Serveurs Racines (Root Servers) :** Ils constituent le sommet de la hiĂ©rarchie mondiale. Ils ne connaissent pas les adresses IP des sites, mais savent vers quels serveurs diriger la requĂȘte en fonction de l'extension (le point final virtuel `.`). +* **Les Serveurs TLD (Top Level Domain) :** GĂšrent une extension spĂ©cifique (comme `.com`, `.fr`, `.org`). Ils connaissent les serveurs DNS d'autoritĂ© enregistrĂ©s pour chaque domaine sous leur extension. +* **Les Serveurs d'AutoritĂ© :** C'est le dernier maillon. Ils hĂ©bergent officiellement la zone DNS du domaine demandĂ© et fournissent la rĂ©ponse finale (l'enregistrement A ou CNAME) au rĂ©solveur. + +## Exemple concret + +Prenons l'exemple de l'achat et de la configuration du nom de domaine `loutikcloud.fr`. + +1. **L'achat :** Vous achetez `loutikcloud.fr` chez un registraire (comme OVH ou Cloudflare). Le registraire informe l'AFNIC (qui gĂšre le TLD `.fr`) que ce domaine vous appartient et lui transmet l'adresse de vos **Serveurs d'AutoritĂ©** (les serveurs NS du registraire). +2. **La configuration :** Sur l'interface du registraire, vous accĂ©dez Ă  la zone DNS de `loutikcloud.fr`. Vous devez y crĂ©er un enregistrement de type **A**. Vous nommez cet enregistrement `web` et vous lui associez l'adresse IP publique de votre routeur. +3. **L'utilisation :** Un client tape `web.loutikcloud.fr` dans son navigateur. +4. **La requĂȘte en coulisses :** +* Le rĂ©solveur du client interroge un serveur racine, qui le renvoie vers les serveurs du `.fr`. +* Le serveur du `.fr` indique au rĂ©solveur que les serveurs d'autoritĂ© pour `loutikcloud.fr` sont chez OVH. +* Le rĂ©solveur interroge le serveur d'autoritĂ© d'OVH, qui lui renvoie l'adresse IP publique configurĂ©e Ă  l'Ă©tape 2. Le navigateur peut alors afficher votre page. \ No newline at end of file diff --git a/src/content/docs/notions/documentation/guide-redaction-post-mortem.md b/src/content/docs/notions/documentation/guide-redaction-post-mortem.md new file mode 100644 index 0000000..a2407b8 --- /dev/null +++ b/src/content/docs/notions/documentation/guide-redaction-post-mortem.md @@ -0,0 +1,131 @@ +--- +title: Guide de rĂ©daction d'un Post-Mortem +description: Guide complet pour rĂ©diger un Post-Mortem efficace aprĂšs un incident, afin d'analyser les causes, l'impact et les actions correctives. +--- + +# Guide de rĂ©daction d'un Post-Mortem + +## 1. DĂ©finition +Un Post-Mortem (ou RCA - *Root Cause Analysis*) est un document rĂ©digĂ© aprĂšs un incident pour comprendre ce qui s'est passĂ© et Ă©viter que cela ne se reproduise. Ce n'est pas pour blĂąmer, mais pour apprendre. + +## 2. Quand en rĂ©diger un ? +* Lors d'une interruption de service (*Downtime*). +* Lors d'une perte de donnĂ©es. +* Lors d'un dysfonctionnement majeur impactant les utilisateurs. + +## 3. Structure type (Template) + +### MĂ©ta-donnĂ©es +* **Date de l'incident :** JJ/MM/AAAA +* **Service impactĂ© :** Nom du service (ex: Application Web, API, Base de donnĂ©es) +* **GravitĂ© :** Mineure / Majeure / Critique +* **Statut :** RĂ©solu 🟱 | Non-rĂ©solu 🔮 + +### I. RĂ©sumĂ© de l'incident (Executive Summary) +*Description narrative courte (2-3 phrases) : Que s'est-il passĂ© globalement ? Quelle a Ă©tĂ© la durĂ©e de coupure ?* + +### II. Impact Utilisateur +* **SymptĂŽme :** Quel Ă©tait le ressenti concret des utilisateurs (ex: page blanche, lenteur, erreur 403) ? +* **PĂ©rimĂštre :** Qui a Ă©tĂ© affectĂ© (Tous les clients, Interne, Admin seulement) ? +* **ConsĂ©quences :** RĂ©percussions mĂ©tier (Perte de donnĂ©es, arrĂȘt de production, etc.). + +### III. Analyse de la cause racine (Root Cause Analysis) +* **DĂ©clencheur technique :** Quel composant a initialement causĂ© l'incident ? +* **DĂ©faut de dĂ©tection :** Pourquoi l'incident n'a-t-il pas Ă©tĂ© dĂ©tectĂ© plus tĂŽt ? +* **Facteur aggravant :** Y a-t-il eu des Ă©lĂ©ments qui ont amplifiĂ© l'impact ? + +### IV. RĂ©solution et Correctif technique +* **Action immĂ©diate :** Qu'a-t-on fait pour rĂ©tablir le service rapidement ? +* **Correctif durable :** Quelle modification technique a Ă©tĂ© appliquĂ©e (lien vers le fichier/commit) ? + +### V. Analyse de risque rĂ©siduel (Optionnel) +* **Risque :** Y a-t-il des risques qui subsistent aprĂšs la rĂ©solution ? +* **Mitigation :** Quelles mesures sont prises pour minimiser ces risques ? + +### VI. Leçons apprises +* Quelles sont les principales leçons tirĂ©es pour l'avenir (Monitoring Ă  ajouter, Doc Ă  mettre Ă  jour) ? + +--- + +## 4. Exemple de Post-Mortem + +````markdown +# Post-Mortem - Incident de bannissement abusif (Faux-positifs Outline) + +**Phase 1 – Socle physique et rĂ©seau** + +![Logo Loutik](/img/logo_loutik.png) + +--- +## Informations gĂ©nĂ©rales + +- **Date de crĂ©ation :** 04/01/2026 +- **DerniĂšre modification :** 04/01/2026 +- **Auteur :** MEDO Louis +- **Version :** 1.0 + +--- +## Informations Post-Mortem + +* **Date de l'incident :** 22 dĂ©cembre 2025 +* **Service impactĂ© :** Outline (Wiki/Documentation) +* **GravitĂ© :** Majeure (IndisponibilitĂ© de service pour utilisateurs lĂ©gitimes) +* **Statut :** RĂ©solu 🟱 + +--- +## 1. RĂ©sumĂ© de l'incident + +Le 22 dĂ©cembre, plusieurs utilisateurs ont signalĂ© une perte d'accĂšs Ă  l'application Outline, se retrouvant bloquĂ©s par une page de bannissement de l'IPS (CrowdSec). L'incident a Ă©tĂ© causĂ© par une rĂšgle de dĂ©tection CrowdSec trop agressive vis-Ă -vis du comportement normal de l'API Outline (requĂȘtes multiples avec token expirĂ©). Un correctif (whitelist) a Ă©tĂ© dĂ©ployĂ© pour ignorer ces erreurs spĂ©cifiques. + +--- +## 2. Impact Utilisateur + +* **SymptĂŽme :** Les utilisateurs dont la session Outline venait d'expirer ne pouvaient pas se reconnecter. Ils recevaient une rĂ©ponse 403 (Forbidden) gĂ©nĂ©rĂ©e par le pare-feu applicatif. +* **PĂ©rimĂštre :** Utilisateurs internes ayant une session active en arriĂšre-plan. +* **ConsĂ©quence :** Interruption de service nĂ©cessitant une intervention manuelle (unban) avant l'application du correctif global. + +--- +## 3. Analyse de la Cause Racine (Root Cause Analysis) + +- **DĂ©clencheur technique :** L'application Outline est une "Single Page Application" (SPA). Lorsqu'un token de session expire, le client tente de rafraĂźchir les donnĂ©es ou d'accĂ©der Ă  l'API, gĂ©nĂ©rant une rafale de requĂȘtes en Ă©chec (Codes HTTP **401 Unauthorized** ou **403 Forbidden**). +- **DĂ©faut de dĂ©tection :** CrowdSec a interprĂ©tĂ© cette sĂ©rie rapide d'erreurs 401/403 provenant d'une mĂȘme IP comme une tentative de force brute (scĂ©nario `crowdsecurity/http-crawl-non_statics` ou `http-generic-401-bf`). +- **Facteur aggravant :** Le parser Nginx par dĂ©faut ne distingue pas un Ă©chec d'authentification lĂ©gitime (token expirĂ©) d'une attaque malveillante sur ces endpoints spĂ©cifiques. + +--- +## 4. RĂ©solution et Correctif technique + +Pour rĂ©tablir le service et prĂ©venir la rĂ©cidive, une rĂšgle d'exclusion (whitelist) a Ă©tĂ© dĂ©veloppĂ©e. + +**Action rĂ©alisĂ©e :** CrĂ©ation d'un parser personnalisĂ© ciblant spĂ©cifiquement les logs Nginx de l'API Outline. + +- **Fichier :** `/etc/crowdsec/parsers/s02-enrich/outline-whitelist.yaml` +- **Logique appliquĂ©e :** Utilisation d'une Regex sur la ligne de log brute (`evt.Line.Raw`) pour contourner les ambiguĂŻtĂ©s de parsing (String vs Int). + +**Code de la whitelist :** +```yaml +name: custom/outline-whitelist-final +description: "Whitelist brute pour l'API Outline" +whitelist: + reason: "Outline API False Positives (Auth 401/403)" + expression: + # Autorise les erreurs 401/403 uniquement sur les routes /api/ + - evt.Line.Raw matches ".* /api/.*" && evt.Line.Raw matches ".* (401|403) .*" +``` +> Pensez Ă  redĂ©marrer CrowdSec aprĂšs la mise en place de la whitelist (`systemctl reload crowdsec`). + +--- + +## 5. Analyse de risque rĂ©siduel + +En appliquant cette whitelist, CrowdSec cesse de surveiller les tentatives de brute-force sur les routes `/api/` d'Outline. + +* **Risque :** Un attaquant pourrait tenter de deviner des identifiants via l'API sans ĂȘtre banni par l'IPS. +* **Mitigation :** Ce risque est acceptĂ© car l'application Outline intĂšgre nativement une limitation de dĂ©bit (*rate-limiting*) et des dĂ©lais progressifs sur les Ă©checs d'authentification. La sĂ©curitĂ© est dĂ©lĂ©guĂ©e Ă  la couche applicative pour ce pĂ©rimĂštre prĂ©cis. + +--- + +## 6. Leçons apprises + +* **Surveillance :** Les logs d'erreurs 4xx doivent ĂȘtre corrĂ©lĂ©s avec le type d'application (SPA vs Site statique) lors de la mise en place d'un WAF. +* **Configuration :** Les parsers CrowdSec nĂ©cessitent une attention particuliĂšre sur le type des donnĂ©es (Int/String) lors de l'Ă©criture de rĂšgles custom (d'oĂč l'utilisation de `evt.Line.Raw` pour la robustesse). +```` \ No newline at end of file diff --git a/src/content/docs/notions/documentation/guide-redaction-runbook.md b/src/content/docs/notions/documentation/guide-redaction-runbook.md new file mode 100644 index 0000000..926c77e --- /dev/null +++ b/src/content/docs/notions/documentation/guide-redaction-runbook.md @@ -0,0 +1,125 @@ +--- +title: Guide de rĂ©daction d'un Runbook +description: Guide complet pour rĂ©diger un Runbook efficace pour les opĂ©rations rĂ©currentes et la rĂ©ponse Ă  incident. +--- + +# Guide de rĂ©daction d'un Runbook + +## 1. DĂ©finition +Un Runbook (ou *Playbook*) est une documentation opĂ©rationnelle destinĂ©e Ă  ĂȘtre exĂ©cutĂ©e "Ă  chaud" lors d'un incident, d'une maintenance ou d'une demande rĂ©currente. Contrairement Ă  une procĂ©dure d'installation, il doit ĂȘtre direct et actionnable immĂ©diatement. + +## 2. Quand en rĂ©diger un ? +* Pour les tĂąches rĂ©currentes (Maintenance, Sauvegardes manuelles). +* Pour la rĂ©ponse Ă  incident (Que faire si le disque est plein ? Si un utilisateur est banni ?). +* Pour les opĂ©rations sensibles (Mise en production, Restauration de BDD). + +## 3. Structure type (Template) + +### MetadonnĂ©es + +* **Nom du Runbook :** Verbe d'action + Objet (ex: *DĂ©bannir une IP*, *Restaurer un backup*) +* **Service concernĂ© :** Nom du service (ex: CrowdSec, PostgreSQL) +* **DĂ©clencheur :** Quel Ă©vĂ©nement lance ce runbook ? (Alerte, Ticket, Cron) +* **RĂŽles requis :** Qui peut l'exĂ©cuter ? (Admin, User, Bot) + +### I. Contexte et Objectif +* **Pourquoi :** À quoi sert cette opĂ©ration ? +* **Quand :** Dans quel cas prĂ©cis doit-on l'utiliser ? + +### II. PrĂ©requis techniques +* **AccĂšs :** Quels serveurs/droits sont nĂ©cessaires (SSH, VPN, Credentials) ? +* **Outils :** Commandes ou logiciels requis (`cscli`, `kubectl`, `psql`). + +### III. ProcĂ©dure pas-Ă -pas +* **Étape 1 :** Action prĂ©cise + Commande Ă  copier-coller. +* **Étape 2 :** Action suivante... +*(Les commandes doivent ĂȘtre explicites et sĂ©curisĂ©es).* + +### IV. VĂ©rification (Sanity Check) +* Comment s'assurer que l'action a rĂ©ussi ? (Commande de test, log Ă  vĂ©rifier). + +### V. Rollback (Retour arriĂšre) +* **ProcĂ©dure d'annulation :** Si l'opĂ©ration Ă©choue ou casse la prod, comment revenir Ă  l'Ă©tat initial ? + +## 4. Exemple de Runbook + +````markdown +# Runbook - Gestion des bannissements CrowdSec + +**Phase 1 – socle physique et rĂ©seau** + +![Logo Loutik](/img/logo_loutik.png) + +--- +## Informations gĂ©nĂ©rales + +- **Date de crĂ©ation :** 04/01/2026 +- **DerniĂšre modification :** 04/01/2026 +- **Auteur :** MEDO Louis +- **Version :** 1.0 + +--- +## Informations Runbook + +* **Nom du Runbook :** DĂ©bannir une adresse IP manuellement +* **Service concernĂ© :** CrowdSec (IPS) +* **DĂ©clencheur :** Ticket support utilisateur ou Alerte de faux-positif +* **RĂŽles requis :** Administrateur (Sudoer) + +--- +## 1. Contexte et Objectif + +Ce runbook doit ĂȘtre utilisĂ© lorsqu'un utilisateur lĂ©gitime signale ne plus pouvoir accĂ©der aux services (erreur 403 ou Timeout), soupçonnant un blocage par l'IPS CrowdSec. L'objectif est de vĂ©rifier le bannissement et de lever la sanction si elle est injustifiĂ©e. + +--- +## 2. PrĂ©requis techniques + +* **AccĂšs :** Connexion SSH au serveur hĂ©bergeant CrowdSec. +* **Droits :** PrivilĂšges `sudo` ou `root`. +* **Info requise :** L'adresse IP publique de l'utilisateur (via whatismyip.com). + +--- +## 3. ProcĂ©dure pas-Ă -pas + +### Étape 1 : VĂ©rifier le statut de l'IP +Avant de dĂ©bloquer, vĂ©rifier si l'IP est effectivement bannie et pour quelle raison. + +```bash +sudo cscli decisions list --ip +``` + +* **Cas A :** `No decision for IP ...` ➔ Le problĂšme n'est pas CrowdSec. **ArrĂȘter ici.** +* **Cas B :** Une ligne apparaĂźt avec `Type: ban` ➔ **Passer Ă  l'Ă©tape 2.** + +### Étape 2 : Supprimer le bannissement + +ExĂ©cuter la commande suivante pour retirer l'IP de la base de donnĂ©es locale et du pare-feu. + +```bash +sudo cscli decisions delete --ip +``` + +--- + +## 4. VĂ©rification + +VĂ©rifier que l'IP n'apparaĂźt plus dans la liste des dĂ©cisions actives. + +```bash +sudo cscli decisions list --ip +``` + +*Le retour doit ĂȘtre vide.* + +Demander Ă  l'utilisateur de tester l'accĂšs au service. + +--- + +## 5. Rollback (Retour arriĂšre) + +Si vous avez dĂ©banni une IP malveillante par erreur, vous pouvez la rebannir manuellement pour 4 heures. + +```bash +sudo cscli decisions add --ip --duration 4h --reason "Erreur de manipulation admin" +``` +```` \ No newline at end of file diff --git a/src/content/docs/notions/git/initialisation-git.md b/src/content/docs/notions/git/initialisation-git.md new file mode 100644 index 0000000..76d0a3d --- /dev/null +++ b/src/content/docs/notions/git/initialisation-git.md @@ -0,0 +1,221 @@ +--- +title: Initialisation de Git +description: Documentation approfondie pour l'installation, la configuration et l'utilisation de Git avec GitHub. Inclut les mĂ©canismes d'authentification, le cycle de vie des fichiers et la gestion des erreurs courantes. +--- + +# Initialisation de Git + +## PrĂ©sentation de Git + +### Pourquoi le versioning ? + +Dans un environnement de production, la gestion manuelle des versions (fichiers nommĂ©s `projet_final_v2.zip`) constitue un risque critique (perte de donnĂ©es, Ă©crasement). Git rĂ©pond Ă  cette problĂ©matique en historisant chaque modification du code source. Il assure la traçabilitĂ© ("qui a fait quoi et quand"), la rĂ©versibilitĂ© des actions et permet la collaboration simultanĂ©e de plusieurs dĂ©veloppeurs via des mĂ©canismes de fusion. + +### Architecture DĂ©centralisĂ©e + +Contrairement aux systĂšmes centralisĂ©s, Git est **dĂ©centralisĂ©**. Chaque poste de travail dispose d'une copie autonome et intĂ©grale de l'historique du projet. Les opĂ©rations courantes (commits, consultation de l'historique, changement de branche) s'effectuent localement, sans dĂ©pendance au rĂ©seau. La connexion internet est uniquement requise pour la synchronisation avec le dĂ©pĂŽt commun (GitHub, GitLab). + +### Le cycle de vie des fichiers (Les 3 zones) + +La maĂźtrise de Git repose sur la comprĂ©hension du flux de donnĂ©es entre trois zones logiques distinctes : + +```txt ++----------------+ +----------------+ +----------------+ +----------------+ +| Working Dir | | Staging Area | | Repository | | Remote | +| (Travail) | ------> | (Index) | ---------> | (Local) | -------> | (Serveur) | +| Vos fichiers | git add | Zone de transit| git commit | Historique | git push | GitHub/GitLab | +| modifiĂ©s | | PrĂȘt Ă  partir | | validĂ© | | Sauvegarde | ++----------------+ +----------------+ +----------------+ +----------------+ +``` + +--- +## PrĂ©requis + +* Disposer d'un compte **GitHub** actif. +* Avoir un environnement de dĂ©veloppement intĂ©grĂ© (IDE) installĂ© (Recommandation : Visual Studio Code). + +--- +## Installation de Git + +### MĂ©thode A : Environnement Windows (Interface Graphique) + +> Cette section couvre le dĂ©ploiement de la suite "Git for Windows", incluant l'Ă©mulateur Bash nĂ©cessaire pour exĂ©cuter les commandes Unix dans un environnement Microsoft. + +1. **TĂ©lĂ©chargement.** RĂ©cupĂ©rez l'exĂ©cutable certifiĂ© sur le site officiel [git-scm.com](https://git-scm.com/download/win). +2. **Configuration de l'installateur.** Durant l'installation, une vigilance particuliĂšre est requise Ă  l'Ă©tape "Choosing the default editor used by Git". +* Action : SĂ©lectionnez **Use Visual Studio Code as Git's default editor**. +* *Justification :* Facilite la rĂ©daction des messages de commit et la rĂ©solution graphique des conflits de fusion. +* *Note :* Conservez les valeurs par dĂ©faut pour les autres options. + +3. **Validation de l'installation.** Ouvrez un terminal (PowerShell) et exĂ©cutez le test de version : +```powershell +git --version +``` + +* `git` : Appel du programme principal. +* `--version` : Retourne le numĂ©ro de build, confirmant l'ajout correct aux variables d'environnement (PATH). + +### MĂ©thode B : Environnement Linux (CLI) + +Sur les distributions basĂ©es sur Debian/Ubuntu, l'installation s'effectue via le gestionnaire de paquets APT. + +1. **Mise Ă  jour des rĂ©fĂ©rentiels.** +```bash +sudo apt update +``` + +* `sudo` : ExĂ©cution avec Ă©lĂ©vation de privilĂšges. +* `update` : Actualisation des index de paquets disponibles. + +2. **Installation du paquet.** +```bash +sudo apt install git -y +``` + +* `install git` : DĂ©ploie le binaire Git. +* `-y` : Validation automatique des prompts pour l'automatisation. + +--- +## Configuration Globale + +> L'identification de l'auteur est une Ă©tape obligatoire pour garantir la traçabilitĂ© (audit trail). Ces mĂ©tadonnĂ©es sont scellĂ©es de maniĂšre immuable dans chaque commit. + +1. **ParamĂ©trage de l'identitĂ©.** +```bash +git config --global user.name "Prenom Nom" +git config --global user.email "email@exemple.com" +``` + +* `config` : Modifie les paramĂštres de Git. +* `--global` : Applique la configuration au niveau de la session utilisateur (fichier `~/.gitconfig`). +* `user.email` : **Point de vigilance :** Doit correspondre strictement Ă  l'email de votre compte GitHub pour l'attribution des contributions. + +--- +## Authentification et Liaison GitHub + +> Pour Ă©crire sur un dĂ©pĂŽt distant, votre machine doit ĂȘtre authentifiĂ©e. Cette section dĂ©taille les deux protocoles supportĂ©s : HTTPS (via Credential Manager) et SSH (RecommandĂ©). + +### Option A : Git Credential Manager (HTTPS) + +> **Usage :** MĂ©thode recommandĂ©e pour les dĂ©butants, reposant sur une authentification web standard via navigateur. + +1. **DĂ©clenchement.** Initiez une premiĂšre commande rĂ©seau (ex: `git clone` ou `git push`). Une fenĂȘtre "Git Credential Manager" apparaĂźt. +2. **Connexion.** SĂ©lectionnez "Sign in with your browser" et authentifiez-vous sur la page GitHub qui s'ouvre. +3. **RĂ©sultat.** Un jeton d'accĂšs (OAuth token) est gĂ©nĂ©rĂ© et stockĂ© de maniĂšre sĂ©curisĂ©e dans le gestionnaire d'identifiants Windows. Aucune ressaisie n'est nĂ©cessaire. + +### Option B : Protocole SSH (RecommandĂ©) + +> **Usage :** Standard industriel privilĂ©giant la cryptographie asymĂ©trique. Cette mĂ©thode offre une sĂ©curitĂ© accrue et facilite l'automatisation des flux sans intervention humaine. + +1. **GĂ©nĂ©ration de la paire de clĂ©s.** +```bash +ssh-keygen -t ed25519 -C "votre.email@exemple.com" +``` + +* `ssh-keygen` : Utilitaire de gĂ©nĂ©ration de clĂ©s. +* `-t ed25519` : Algorithme de courbe elliptique moderne (plus performant et sĂ©curisĂ© que RSA). +* `-C` : Étiquette (commentaire) pour identifier la clĂ©. + +2. **RĂ©cupĂ©ration de la clĂ© publique.** +```bash +cat ~/.ssh/id_ed25519.pub +``` + +* `.pub` : Extension dĂ©signant la clĂ© publique (diffusable). **Ne jamais divulguer la clĂ© privĂ©e.** + +3. **Enregistrement sur GitHub.** Copiez la chaĂźne de caractĂšres. Dans GitHub : **Settings > SSH and GPG keys > New SSH key**. + +4. **Test de connectivitĂ©.** +```bash +ssh -T git@github.com +``` + +* `-T` : DĂ©sactive le pseudo-terminal (GitHub n'autorisant pas de Shell interactif). +* *Validation :* Le message "Hi [Username]! You've successfully authenticated" doit apparaĂźtre. + +--- +## Flux de travail (Workflow) + +> Ce cycle opĂ©rationnel dĂ©crit les commandes essentielles pour transformer des modifications locales en une version partagĂ©e sur le serveur. + +### 1. Initialisation d'un projet + +Pour placer un rĂ©pertoire sous contrĂŽle de version : + +```bash +git init +``` + +* `init` : GĂ©nĂšre le dossier cachĂ© `.git` contenant la base de donnĂ©es relationnelle du projet. + +### 2. VĂ©rification de l'Ă©tat (Audit) + +Pour visualiser les modifications en cours et l'Ă©tat de l'index : +```bash +git status +``` + +### 3. Indexation (Staging) + +SĂ©lection des fichiers Ă  inclure dans la prochaine version (passage de "Working Dir" Ă  "Staging Area") : + +```bash +git add fichier.txt +# Ou pour indexer l'ensemble du dossier : +git add . +``` + +### 4. Validation (Commit) + +Enregistrement dĂ©finitif des modifications indexĂ©es dans l'historique local : + +```bash +git commit -m "feat: Ajout de la navbar responsive" +``` + +* `-m` : Permet de spĂ©cifier le message de commit. +* **Bonne pratique :** Le message doit ĂȘtre impĂ©ratif et dĂ©crire la valeur ajoutĂ©e fonctionnelle. + +### 5. Synchronisation (Push) + +Transfert de l'historique local vers le serveur distant : + +```bash +git push -u origin main +``` + +* `push` : Envoi des objets vers le "remote". +* `-u` : (Upstream) Configure le suivi permanent entre la branche locale `main` et la branche distante. + +--- +## Gestion des anomalies et Rollback + +> **SĂ©curitĂ© :** Git permet la rĂ©versibilitĂ©. Cette section distingue les actions non destructives (restauration de fichiers) des actions réécrivant l'historique (reset). + +### Restauration de fichiers (Discard changes) + +> **Cas d'usage :** Annuler des modifications non dĂ©sirĂ©es sur un fichier pour revenir Ă  son Ă©tat lors du dernier commit. + +```bash +git restore nom_du_fichier.txt +``` + +* `restore` : Écrase la version locale par la version stockĂ©e dans l'index. **Action irrĂ©versible pour les modifications non sauvegardĂ©es.** + +### Correction du dernier commit (Soft Reset) + +> **Cas d'usage :** Le commit a Ă©tĂ© effectuĂ© trop tĂŽt ou contient une erreur, mais le travail doit ĂȘtre conservĂ© pour rectification. + +```bash +git reset --soft HEAD~1 +``` + +* `--soft` : Annule le commit dans l'historique mais **conserve les fichiers modifiĂ©s** dans la zone de staging (Index). +* `HEAD~1` : Cible le parent immĂ©diat de la version actuelle. + +--- +## Bibliographie + +* [Documentation officielle (Livre Pro Git)](https://git-scm.com/book/fr/v2) - RĂ©fĂ©rence des commandes. +* [GitHub Docs - Connexion SSH](https://docs.github.com/fr/authentication/connecting-to-github-with-ssh) - ProcĂ©dure dĂ©taillĂ©e par OS. +* [Conventional Commits](https://www.conventionalcommits.org/) - Norme de rĂ©daction des messages de commit. \ No newline at end of file diff --git a/src/content/docs/notions/hyperviseur/synthese-choix-hyperviseur-haute-disponibilite.md b/src/content/docs/notions/hyperviseur/synthese-choix-hyperviseur-haute-disponibilite.md new file mode 100644 index 0000000..93bb3d5 --- /dev/null +++ b/src/content/docs/notions/hyperviseur/synthese-choix-hyperviseur-haute-disponibilite.md @@ -0,0 +1,55 @@ +--- +title: Choix d'un hyperviseur de type 1 et architecture de haute disponibilitĂ© +description: SynthĂšse du choix d'un hyperviseur de type 1 et d'une architecture de haute disponibilitĂ©. Cours BTS SIO - Paul-louis Courier +--- + +## PrĂ©sentation de la problĂ©matique + +Lorsqu'une organisation (comme par exemple la sociĂ©tĂ© IMDEO gĂ©rant le pĂŽle numĂ©rique d'un grand groupe ) renouvelle son infrastructure, elle doit garantir que ses services informatiques fonctionnent sans interruption. Le remplacement d'une ferme de serveurs pose un double enjeu : choisir un systĂšme capable d'hĂ©berger efficacement de multiples serveurs virtuels (comme une centaine de machines Windows et Linux ) et concevoir une architecture matĂ©rielle et logicielle robuste. L'objectif est d'Ă©viter tout point de dĂ©faillance unique (SPOF) afin d'assurer une haute disponibilitĂ© et une continuitĂ© de service. + +## Les solutions + +Le choix d'un hyperviseur de type 1 dĂ©pend des compĂ©tences internes, du budget et des besoins en fonctionnalitĂ©s avancĂ©es. Voici un comparatif des solutions majeures du marchĂ© : + +|**Solution (Hyperviseur)**|**Description et spĂ©cificitĂ©s**| +|---|---| +|**VMware vSphere (ESXi)**|Leader historique. TrĂšs robuste, riche en fonctionnalitĂ©s d'entreprise, mais coĂ»t de licence Ă©levĂ©.| +|**Microsoft Hyper-V**|IntĂ©grĂ© Ă  l'Ă©cosystĂšme Windows Server. IdĂ©al si l'entreprise dispose dĂ©jĂ  de licences Microsoft Datacenter.| +|**Proxmox VE**|Solution Open Source basĂ©e sur Debian (KVM/LXC). Gratuite Ă  l'utilisation, avec un support technique payant en option.| +|**Nutanix (AHV)**|AxĂ© sur l'hyperconvergence (HCI). Simplifie la gestion en fusionnant calcul, stockage et rĂ©seau, mais nĂ©cessite un investissement matĂ©riel spĂ©cifique.| +|**XCP-ng / Xen**|Alternative Open Source performante basĂ©e sur Xen, souvent associĂ©e Ă  l'interface de gestion Xen Orchestra.| + +## Les concepts + +### Hyperviseur de type 1 (Bare-Metal) + +Un hyperviseur de type 1 est un systĂšme d'exploitation allĂ©gĂ© qui s'installe directement sur le matĂ©riel physique du serveur (le _bare-metal_). + +- Il contrĂŽle directement les ressources physiques (CPU, RAM, stockage, rĂ©seau) pour les allouer aux machines virtuelles (VM). +- Contrairement Ă  un hyperviseur de type 2 (comme VirtualBox qui s'installe sur un OS classique comme Windows), le type 1 offre des performances optimales et une latence rĂ©duite, indispensables en production. + +### Haute DisponibilitĂ© (HA - High Availability) + +La Haute DisponibilitĂ© est un mĂ©canisme d'architecture garantissant qu'un service reste accessible mĂȘme en cas de panne d'un composant. + +- **Le principe de cluster :** Les serveurs physiques (nƓuds) sont regroupĂ©s. Si un serveur physique tombe en panne, l'hyperviseur redĂ©marre automatiquement les machines virtuelles affectĂ©es sur un autre serveur sain du cluster. +- **PrĂ©requis :** Pour que cela fonctionne, les nƓuds doivent possĂ©der des caractĂ©ristiques matĂ©rielles suffisantes (ex: 60 cƓurs CPU et 1,5 To de RAM rĂ©partis ) et avoir accĂšs Ă  un espace de stockage commun. + +### RĂ©seau de Stockage (SAN - Storage Area Network) + +Pour assurer la mobilitĂ© des VM entre les serveurs physiques, leurs disques virtuels ne doivent pas ĂȘtre stockĂ©s localement, mais sur un rĂ©seau dĂ©diĂ©. + +- Le SAN est un rĂ©seau de stockage indĂ©pendant Ă  trĂšs haut dĂ©bit qui relie les serveurs Ă  des baies de disques. +- Il permet Ă  tous les hyperviseurs du cluster d'accĂ©der simultanĂ©ment aux mĂȘmes donnĂ©es (les disques virtuels des VM). Ainsi, si un serveur physique meurt, un autre peut reprendre la VM lĂ  oĂč elle s'est arrĂȘtĂ©e. + +### TolĂ©rance aux pannes matĂ©rielles (Redondance) + +Pour assurer la continuitĂ©, chaque composant physique du serveur doit ĂȘtre doublĂ© (redondant) : + +- **Stockage local (OS) :** Utilisation de grappes RAID pour tolĂ©rer la perte d'un disque physique hĂ©bergeant l'hyperviseur. +- **RĂ©seau :** Multiplication des cartes rĂ©seaux (ex: 4 ports 10 Gbits et 4 ports 1 Gbits ) reliĂ©es Ă  des commutateurs diffĂ©rents. +- **Énergie :** Double alimentation Ă©lectrique branchĂ©e sur des onduleurs distincts pour pallier les coupures de courant. + +## Conclusion + +Pour garantir la continuitĂ© de service d'une entreprise, l'installation d'un hyperviseur de type 1 (comme Proxmox, VMware ou Hyper-V ) ne suffit pas Ă  elle seule. Il est impĂ©ratif de concevoir une architecture en cluster adossĂ©e Ă  un stockage partagĂ© (SAN) et de redonder l'ensemble des composants physiques (rĂ©seau, alimentation, disques). C'est cette combinaison matĂ©rielle et logicielle qui permet de construire une infrastructure vĂ©ritablement hautement disponible. \ No newline at end of file diff --git a/src/content/docs/notions/index.md b/src/content/docs/notions/index.md new file mode 100644 index 0000000..26399a2 --- /dev/null +++ b/src/content/docs/notions/index.md @@ -0,0 +1,16 @@ +--- +title: Introduction +description: Exploration des concepts thĂ©oriques et des applications pratiques liĂ©s Ă  l'ingĂ©nierie systĂšme et aux nouvelles technologies. +--- + +## 📚 Notions + +Cette section constitue une base de connaissances dĂ©diĂ©e Ă  la comprĂ©hension approfondie des technologies exploitĂ©es au sein de l'infrastructure. L'objectif est de dĂ©mystifier les paradigmes informatiques modernes en alliant rigueur thĂ©orique et mise en Ɠuvre technique. Vous y retrouverez les principes fondamentaux liĂ©s Ă  l'isolation applicative, Ă  la gestion des ressources, ainsi qu'Ă  l'intĂ©gration de modĂšles d'intelligence artificielle. + +## 📂 Organisation + +L'arborescence de cette documentation est structurĂ©e de maniĂšre thĂ©matique et progressive pour faciliter l'assimilation : + +* **Les concepts thĂ©oriques :** État de l'art, architecture, et mĂ©canismes internes des technologies abordĂ©es. +* **La mise en pratique :** Cas d'usage concrets, exemples de dĂ©ploiement et bonnes pratiques opĂ©rationnelles. +* **Les domaines technologiques :** SĂ©paration logique par grands thĂšmes d'ingĂ©nierie, allant de l'encapsulation des services jusqu'aux outils d'analyse et d'automatisation avancĂ©s. \ No newline at end of file diff --git "a/src/content/docs/notions/r\303\251seau/ipv6.md" "b/src/content/docs/notions/r\303\251seau/ipv6.md" new file mode 100644 index 0000000..cdb316d --- /dev/null +++ "b/src/content/docs/notions/r\303\251seau/ipv6.md" @@ -0,0 +1,61 @@ +--- +title: PrĂ©sentation de l'IPv6 +description: PrĂ©sentation et explication de l'IPv6. +--- + +## PrĂ©sentation gĂ©nĂ©rale + +L'**IPv6** (*Internet Protocol version 6*) est le successeur de l'IPv4. Contrairement Ă  son prĂ©dĂ©cesseur codĂ© sur 32 bits, l'IPv6 est codĂ© sur **128 bits**. Cette longueur permet de passer d'environ 4 milliards d'adresses Ă  3,4×1038 adresses. + +**Format d'Ă©criture :** + +* Notation en **hexadĂ©cimal** (8 blocs de 16 bits sĂ©parĂ©s par des `:`). +* Exemple : `2001:0db8:0000:85a3:0000:0000:ac1f:8001` +* **Simplification :** On peut supprimer les zĂ©ros en tĂȘte (`0db8` -> `db8`) et remplacer une suite de zĂ©ros consĂ©cutifs par `::` (une seule fois par adresse). + +--- + +## La problĂ©matique + +Le dĂ©ploiement de l'IPv6 rĂ©pond Ă  trois enjeux majeurs du rĂ©fĂ©rentiel : + +1. **PĂ©nurie d'adresses IPv4 :** L'Ă©puisement des adresses publiques IPv4 a rendu nĂ©cessaire un espace d'adressage quasi illimitĂ©. +2. **Fin du NAT "obligatoire" :** En IPv4, le NAT est utilisĂ© pour Ă©conomiser les adresses. En IPv6, chaque Ă©quipement peut avoir sa propre IP publique, facilitant les communications de bout en bout (IoT, VoIP). +3. **Optimisation des performances :** L'en-tĂȘte IPv6 est simplifiĂ© pour ĂȘtre traitĂ© plus rapidement par les routeurs. + +--- + +## Les concepts clĂ©s du rĂ©fĂ©rentiel + +### 1. Types d'adresses (Les Scopes) + +Il est crucial de savoir les identifier selon leurs premiers bits : + +* **Unicast Global (GUA) :** DĂ©bute par `2000::/3`. Ce sont les adresses routables sur Internet. +* **Lien Local (Link-Local) :** DĂ©bute par `fe80::/10`. UtilisĂ©e pour le voisinage sur un mĂȘme segment rĂ©seau. +* **Unique Local (ULA) :** DĂ©bute par `fc00::/7`. Équivalent des adresses privĂ©es IPv4 (souvent `fd00::/8`). +* **Multicast :** DĂ©bute par `ff00::/8`. **Note importante :** Le broadcast n'existe pas en IPv6, tout passe par le multicast. + +### 2. Attribution d'adresses + +* **SLAAC (*Stateless Address Autoconfiguration*) :** La machine s'auto-configure sans serveur. Elle reçoit le prĂ©fixe du routeur et gĂ©nĂšre son identifiant d'interface. +* **EUI-64 :** MĂ©thode pour crĂ©er l'identifiant d'interface Ă  partir de l'adresse MAC (insertion de `ff:fe` au milieu et inversion du 7Ăšme bit). +* **DHCPv6 :** +* *Stateless :* Le client prend son IP via SLAAC mais demande au DHCP les serveurs DNS. +* *Stateful :* Le serveur gĂšre et distribue tout (comme en IPv4). + + + +### 3. Le protocole NDP (*Neighbor Discovery Protocol*) + +Il remplace l'ARP de l'IPv4. Il utilise des messages ICMPv6 : + +* **NS / NA (*Neighbor Solicitation / Advertisement*) :** Pour trouver l'adresse MAC d'un voisin. +* **RS / RA (*Router Solicitation / Advertisement*) :** Pour qu'une machine trouve son routeur et obtienne son prĂ©fixe. + +### 4. Coexistence avec l'IPv4 + +Les mĂ©canismes de transition Ă  connaĂźtre : + +* **Dual Stack :** L'Ă©quipement fait tourner IPv4 et IPv6 en mĂȘme temps. +* **Tunneling (6to4) :** On encapsule un paquet IPv6 dans un paquet IPv4. \ No newline at end of file diff --git "a/src/content/docs/notions/r\303\251seau/methode-calcul-vlsm.md" "b/src/content/docs/notions/r\303\251seau/methode-calcul-vlsm.md" new file mode 100644 index 0000000..593f5ab --- /dev/null +++ "b/src/content/docs/notions/r\303\251seau/methode-calcul-vlsm.md" @@ -0,0 +1,94 @@ +--- +title: MĂ©thode de calcul VLSM - BTS SIO +description: PrĂ©sentation et explication du VLSM. +--- + +--- +## PrĂ©sentation du VLSM + +Le **VLSM** (*Variable Length Subnet Mask* ou Masque de Sous-RĂ©seau Ă  Longueur Variable) a Ă©tĂ© introduit en 1993 par l'IETF (notamment via la RFC 1519) pour optimiser l'utilisation des adresses IP. + +Contrairement au dĂ©coupage classique "classful" (Classes A, B, C) qui imposait des tailles fixes et gaspillait Ă©normĂ©ment d'adresses, le VLSM permet d'appliquer un masque diffĂ©rent Ă  chaque sous-rĂ©seau au sein d'un mĂȘme bloc d'adressage. C'est l'outil indispensable pour segmenter un rĂ©seau de maniĂšre chirurgicale. + +## La problĂ©matique rĂ©solue + +Avant le VLSM, si vous aviez besoin de 3 sous-rĂ©seaux, vous deviez utiliser le mĂȘme masque pour tous. + +* **Le problĂšme :** Si un rĂ©seau a besoin de 50 hĂŽtes et un autre de seulement 2, vous Ă©tiez obligĂ© de leur allouer la mĂȘme taille (par exemple 64 adresses). +* **La solution :** Le VLSM permet de "sous-dĂ©couper" un sous-rĂ©seau. On Ă©vite ainsi la pĂ©nurie d'adresses IPv4 en adaptant la taille du contenant (le masque) au contenu (le nombre d'hĂŽtes). + +--- + +## Comment le rĂ©aliser ? + +### Les fondamentaux Ă  maĂźtriser + +* **Adresse RĂ©seau :** C'est l'identifiant du groupe. Elle ne peut pas ĂȘtre attribuĂ©e Ă  une machine. Tous ses bits "hĂŽte" sont Ă  0. +* **Masque de sous-rĂ©seau :** Une suite de 32 bits qui sĂ©pare la partie "RĂ©seau" (les 1) de la partie "HĂŽte" (les 0). +* **CIDR (*Classless Inter-Domain Routing*) :** C'est la notation simplifiĂ©e du masque (ex: `/24`). Le chiffre indique le nombre de bits Ă  1. + +### MĂ©thodologie et Tableaux + +**1. Inventaire des besoins** +| Nom du sous-rĂ©seau | Nombre d'hĂŽtes rĂ©els | +| :--- | :--- | +| Compta | 50 | +| Commercial | 46 | +| Finance | 20 | + +**2. Tableau d'adressage final (TriĂ© par ordre dĂ©croissant)** + +> **Note :** On trie toujours du plus grand besoin au plus petit pour Ă©viter que les plages d'adresses ne se chevauchent. + +| Nom | RĂ©seau | Masque (CIDR) | 1Ăšre @ utile | DerniĂšre @ utile | Diffusion | +| --- | --- | --- | --- | --- | --- | +| **Compta** | 192.168.1.0 | /26 | 192.168.1.1 | 192.168.1.62 | 192.168.1.63 | +| **Commercial** | 192.168.1.64 | /26 | 192.168.1.65 | 192.168.1.126 | 192.168.1.127 | +| **Finance** | 192.168.1.128 | /27 | 192.168.1.129 | 192.168.1.158 | 192.168.1.159 | + +--- + +### Calculer un sous-rĂ©seau (Exemple : Compta - 50 hĂŽtes) + +#### 1. Trouver la puissance de 2 + +On cherche (le nombre de bits hĂŽtes) tel que : + + +* (Trop petit pour 50) +* (C'est bon !) -> **On rĂ©serve 6 bits pour les hĂŽtes.** + +| Valeur | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| **Puissance** | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +#### 2. Calculer le masque CIDR + +Une adresse IPv4 fait 32 bits. Si on utilise 6 bits pour les hĂŽtes, le reste appartient au rĂ©seau : + +* 32 - 6 = 26 +* **RĂ©sultat : /26** + +#### 3. Masque dĂ©cimal pointĂ© + +On passe les 26 premiers bits Ă  1, le reste Ă  0 : +`11111111.11111111.11111111.11000000` + +* Les trois premiers octets valent **255**. +* Le dernier : . +* **RĂ©sultat : 255.255.255.192** + +#### 4. Adresse de Diffusion (Broadcast) + +L'adresse de diffusion est la derniĂšre adresse du bloc. Pour la trouver, on prend l'adresse rĂ©seau et on passe tous les bits "hĂŽtes" (les 6 derniers) Ă  1. + +* RĂ©seau : `... .00000000` (192.168.1.0) +* Diffusion : `... .00111111` (Le `111111` binaire vaut 63 en dĂ©cimal). +* **RĂ©sultat : 192.168.1.63** + +#### 5. Plage d'hĂŽtes utiles + +* **PremiĂšre @ :** Adresse RĂ©seau + 1 = `192.168.1.1` +* **DerniĂšre @ :** Adresse Diffusion - 1 = `192.168.1.62` + +--- \ No newline at end of file diff --git "a/src/content/docs/notions/r\303\251seau/modele-osi.md" "b/src/content/docs/notions/r\303\251seau/modele-osi.md" new file mode 100644 index 0000000..fa9fdc7 --- /dev/null +++ "b/src/content/docs/notions/r\303\251seau/modele-osi.md" @@ -0,0 +1,45 @@ +--- +title: ModĂšle OSI +description: PrĂ©sentation et explication du modĂšle OSI (Open Systems Interconnection) en rĂ©seau informatique. +--- + +## PrĂ©sentation du modĂšle OSI +Le modĂšle OSI (Open Systems Interconnection) est un cadre conceptuel utilisĂ© pour comprendre et standardiser les fonctions d'un systĂšme de communication en rĂ©seau. Il divise les processus de communication en sept couches distinctes, chacune ayant des responsabilitĂ©s spĂ©cifiques. + +## Les 7 couches du modĂšle OSI +1. **Couche Physique (Layer 1)** + - Responsable de la transmission des bits bruts sur un support physique (cĂąbles, fibres optiques, ondes radio). + - Exemples : cĂąbles Ethernet, hubs, rĂ©pĂ©teurs. + +2. **Couche Liaison de DonnĂ©es (Layer 2)** + - GĂšre la communication entre les nƓuds sur un mĂȘme rĂ©seau local. + - Assure la dĂ©tection et la correction des erreurs de transmission. + - Exemples : commutateurs (switches), protocoles Ethernet, PPP. + +3. **Couche RĂ©seau (Layer 3)** + - Responsable de l'acheminement des paquets de donnĂ©es entre diffĂ©rents rĂ©seaux. + - GĂšre l'adressage logique et la sĂ©lection des routes. + - Exemples : routeurs, protocoles IP (IPv4, IPv6). + +4. **Couche Transport (Layer 4)** + - Assure la livraison fiable des donnĂ©es entre les hĂŽtes. + - GĂšre le contrĂŽle de flux, la segmentation et la rĂ©assemblage des donnĂ©es. + - Exemples : TCP, UDP. + +5. **Couche Session (Layer 5)** + - GĂšre les sessions de communication entre les applications. + - Établit, maintient et termine les connexions. + - Exemples : protocoles RPC, NetBIOS. + +6. **Couche PrĂ©sentation (Layer 6)** + - Traduit les donnĂ©es entre le format utilisĂ© par l'application et le format de transmission. + - GĂšre le chiffrement, la compression et la conversion de donnĂ©es. + - Exemples : SSL/TLS, formats de donnĂ©es (JPEG, ASCII). + +7. **Couche Application (Layer 7)** + - Fournit des services de rĂ©seau directement aux applications utilisateur. + - GĂšre les protocoles de haut niveau pour les communications. + - Exemples : HTTP, FTP, SMTP, DNS. + +## Conclusion +Le modĂšle OSI est un outil essentiel pour comprendre comment les donnĂ©es circulent Ă  travers un rĂ©seau. En divisant les fonctions de communication en couches distinctes, il facilite la conception, le dĂ©pannage et l'interopĂ©rabilitĂ© des systĂšmes de rĂ©seau. Chaque couche joue un rĂŽle crucial dans le processus global de communication, assurant que les donnĂ©es sont transmises efficacement et de maniĂšre fiable entre les dispositifs connectĂ©s. Cependant, dans la pratique, le modĂšle OSI est souvent comparĂ© au modĂšle TCP/IP, qui est plus reprĂ©sentatif des protocoles utilisĂ©s sur Internet aujourd'hui. \ No newline at end of file diff --git "a/src/content/docs/notions/r\303\251seau/modele-tcp-ip.md" "b/src/content/docs/notions/r\303\251seau/modele-tcp-ip.md" new file mode 100644 index 0000000..eb123ce --- /dev/null +++ "b/src/content/docs/notions/r\303\251seau/modele-tcp-ip.md" @@ -0,0 +1,32 @@ +--- +title: ModĂšle TCP/IP +description: PrĂ©sentation et explication du modĂšle TCP/IP (Transmission Control Protocol/Internet Protocol) en rĂ©seau informatique. +--- + +## PrĂ©sentation du modĂšle TCP/IP +Le modĂšle TCP/IP (Transmission Control Protocol/Internet Protocol) est un ensemble de protocoles de communication utilisĂ©s pour interconnecter des dispositifs sur Internet et d'autres rĂ©seaux. Contrairement au modĂšle OSI qui comporte sept couches, le modĂšle TCP/IP est composĂ© de quatre couches principales, chacune ayant des fonctions spĂ©cifiques dans le processus de communication. + +## Les 4 couches du modĂšle TCP/IP + +1. **Couche AccĂšs RĂ©seau (Network Access Layer)** + - Responsable de la transmission des donnĂ©es sur le support physique. + - GĂšre les aspects matĂ©riels et les protocoles de liaison de donnĂ©es. + - Exemples : Ethernet, Wi-Fi, ARP. + +2. **Couche Internet (Internet Layer)** + - Responsable de l'acheminement des paquets de donnĂ©es entre les rĂ©seaux. + - GĂšre l'adressage logique et la sĂ©lection des routes. + - Exemples : IP (IPv4, IPv6), ICMP, IGMP. + +3. **Couche Transport (Transport Layer)** + - Assure la livraison fiable des donnĂ©es entre les hĂŽtes. + - GĂšre le contrĂŽle de flux, la segmentation et la rĂ©assemblage des donnĂ©es. + - Exemples : TCP, UDP. + +4. **Couche Application (Application Layer)** + - Fournit des services de rĂ©seau directement aux applications utilisateur. + - GĂšre les protocoles de haut niveau pour les communications. + - Exemples : HTTP, FTP, SMTP, DNS. + +## Conclusion +Le modĂšle TCP/IP est le fondement des communications sur Internet et dans de nombreux rĂ©seaux privĂ©s. En simplifiant les fonctions de communication en quatre couches distinctes, il facilite la conception, le dĂ©ploiement et la gestion des rĂ©seaux. Chaque couche joue un rĂŽle crucial dans le processus global de communication, assurant que les donnĂ©es sont transmises efficacement et de maniĂšre fiable entre les dispositifs connectĂ©s. Le modĂšle TCP/IP est largement utilisĂ© dans le monde rĂ©el et constitue la base des protocoles Internet modernes. \ No newline at end of file diff --git a/src/content/docs/notions/salle-serveur/06-structuration-securisation-salle-serveur.md b/src/content/docs/notions/salle-serveur/06-structuration-securisation-salle-serveur.md new file mode 100644 index 0000000..ce2e4ff --- /dev/null +++ b/src/content/docs/notions/salle-serveur/06-structuration-securisation-salle-serveur.md @@ -0,0 +1,51 @@ +--- +title: Structuration et sĂ©curisation des salles serveur +description: SynthĂšse sur la Structuration et sĂ©curisation des salles serveur. Cours BTS SIO - Paul-louis Courier +--- + +## PrĂ©sentation de la problĂ©matique + +La mise en place d'une salle serveur est une Ă©tape critique dans l'Ă©volution de l'infrastructure d'une entreprise. L'objectif principal est de garantir l'hĂ©bergement et la disponibilitĂ© continue des applications internes et des services. La rĂ©flexion doit porter sur l'emplacement physique optimal, la prĂ©vention rigoureuse des sinistres (dĂ©gĂąts des eaux, incendies, surchauffe, pannes Ă©lectriques) et le contrĂŽle strict des habilitations d'accĂšs. Une mauvaise conception de cette salle expose l'entreprise Ă  une interruption totale de son systĂšme d'information. + +## Les solutions + +| **Enjeu / Risque** | **Solutions Techniques et Organisationnelles** | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------ | +| **Emplacement** | Choix d'une zone hors d'eau (pas de sous-sol), sans passage de conduites d'eau, isolĂ©e des zones de grand passage. | +| **Surchauffe** | Climatisation redondante (N+1), urbanisation en allĂ©es chaudes / allĂ©es froides. | +| **Pannes Ă©lectriques** | Double alimentation des Ă©quipements, onduleurs, groupes Ă©lectrogĂšnes. | +| **Incendie** | DĂ©tecteurs de fumĂ©e Ă  haute sensibilitĂ©, systĂšme d'extinction automatique par gaz inerte. | +| **DĂ©gĂąts des eaux** | Utilisation de faux planchers, installation de cordons dĂ©tecteurs d'eau sous la salle. | +| **Habilitations** | Badges RFID, biomĂ©trie, sas de sĂ©curitĂ©, journalisation des accĂšs. | + +## Les concepts + +### 1. Urbanisation et Climatisation + +L'urbanisation dĂ©signe l'organisation physique et spatiale de la salle. Le choix de l'emplacement dans les bĂątiments permet de limiter les risques extĂ©rieurs (inondations, effondrements). + +- L'installation sur faux plancher permet de sĂ©parer les cĂąblages et de propulser l'air froid sous les baies informatiques. * L'organisation en "couloirs froids" (face avant des serveurs) et "couloirs chauds" (face arriĂšre) Ă©vite le mĂ©lange des flux thermiques, ce qui est indispensable pour lutter contre la surchauffe des Ă©quipements. + +### 2. ContinuitĂ© Électrique + +Pour faire face aux pannes d'Ă©lectricitĂ© et aux risques Ă©lectriques, l'architecture doit Ă©liminer tout point de dĂ©faillance unique (SPOF). + +- Les Ă©quipements nĂ©cessitent une double alimentation reliĂ©e Ă  des circuits distincts. * Les onduleurs prennent le relais instantanĂ©ment en cas de coupure, offrant une autonomie de fonctionnement (par exemple de 15 minutes). Un groupe Ă©lectrogĂšne dĂ©marre ensuite pour les coupures plus longues. + +### 3. Gestion des Habilitations et SĂ©curitĂ© Physique + +La gestion des habilitations consiste Ă  contrĂŽler de maniĂšre stricte qui peut pĂ©nĂ©trer dans cette zone critique. + +- Le principe du moindre privilĂšge s'applique : seuls les techniciens du service "Infrastructures systĂšmes et rĂ©seaux" (comme c'est le cas pour la sociĂ©tĂ© IMDEO) doivent possĂ©der un accĂšs physique. +- La sĂ©curitĂ© s'appuie sur la traçabilitĂ© (savoir qui est entrĂ© et quand) via des systĂšmes de contrĂŽle d'accĂšs Ă©lectroniques (badges, biomĂ©trie). + +### 4. Protection Incendie et DĂ©gĂąts des Eaux + +La protection contre ces sinistres impose des dispositifs spĂ©cifiques pour ne pas dĂ©tĂ©riorer le matĂ©riel informatique en cas d'intervention. + +- En cas d'incendie, on n'utilise jamais d'eau. L'extinction se fait par la libĂ©ration d'un gaz inerte (ex: FM-200, Argonite) qui abaisse le taux d'oxygĂšne pour Ă©touffer les flammes sans endommager la ferme de serveurs. +- Contre les dĂ©gĂąts des eaux, des capteurs d'hygromĂ©trie dĂ©clenchent des alarmes avant mĂȘme que l'eau n'atteigne le matĂ©riel. + +## Conclusion + +Structurer une salle serveur repose sur la redondance et l'anticipation. L'emplacement stratĂ©gique, le contrĂŽle strict du climat (climatisation, flux d'air), la sĂ©curitĂ© Ă©lectrique continue (onduleurs) et une politique rigoureuse des habilitations sont les piliers fondamentaux. Ces Ă©lĂ©ments garantissent la haute disponibilitĂ© de l'infrastructure et la pĂ©rennitĂ© du systĂšme d'information de l'entreprise. \ No newline at end of file diff --git a/src/content/docs/notions/sauvegarde/05-strategie-sauvegarde-machines-virtuelles.md b/src/content/docs/notions/sauvegarde/05-strategie-sauvegarde-machines-virtuelles.md new file mode 100644 index 0000000..88e403d --- /dev/null +++ b/src/content/docs/notions/sauvegarde/05-strategie-sauvegarde-machines-virtuelles.md @@ -0,0 +1,69 @@ +--- +title: StratĂ©gie de sauvegarde des machines virtuelles +description: SynthĂšse sur la stratĂ©gie de sauvegarde des machines virtuelles. Cours BTS SIO - Paul-louis Courier +--- + +## PrĂ©sentation de la problĂ©matique + +La mise en place d'une stratĂ©gie de sauvegarde est cruciale pour pouvoir restaurer les serveurs virtuels et les applications rapidement en cas de sinistre majeur ou d'attaque par ransomware. L'enjeu principal est de garantir la continuitĂ© d'activitĂ© de l'entreprise tout en respectant les bonnes pratiques, la volumĂ©trie des donnĂ©es, et en mobilisant les Ă©quipements adĂ©quats. + +## Les solutions + +| **Solution / MĂ©thode** | **Avantages** | **InconvĂ©nients** | **Cas d'usage type** | +| ----------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | +| **Sauvegarde ComplĂšte** | Restauration simple, rapide et autonome. | TrĂšs gourmande en espace de stockage et trĂšs longue Ă  exĂ©cuter. | Sauvegarde de base rĂ©alisĂ©e tous les mois. | +| **Sauvegarde IncrĂ©mentielle** | Sauvegarde trĂšs rapide, Ă©conomise l'espace de stockage et la bande passante. | Restauration plus complexe (nĂ©cessite la sauvegarde complĂšte initiale plus toutes les incrĂ©mentielles suivantes). | Sauvegarde quotidienne effectuĂ©e toutes les nuits. | +| **Sauvegarde Immuable** | ProtĂšge efficacement les donnĂ©es contre le chiffrement ou la suppression par un ransomware. | Espace de stockage verrouillĂ© et inaltĂ©rable pour une durĂ©e fixe. | SĂ©curisation critique des sauvegardes de production. | + +## Les concepts + +### Les mĂ©thodes de sauvegarde + +Il existe plusieurs approches mathĂ©matiques pour copier les donnĂ©es au fil du temps : + +- **La sauvegarde complĂšte (Full) :** Elle copie l'intĂ©gralitĂ© des donnĂ©es sĂ©lectionnĂ©es Ă  un instant prĂ©cis. +```mermaid +graph TD + A["DonnĂ©es
originales"] --> B["Sauvegarde 01
ComplĂšte"] + A --> C["Sauvegarde 01
ComplĂšte"] + A --> D["Sauvegarde 02
ComplÚte"] +``` +*Schéma de fonctionnement de la sauvegarde complÚte.* + +- **La sauvegarde incrémentielle :** Elle ne sauvegarde que les données qui ont été modifiées depuis la _derniÚre_ sauvegarde effectuée (qu'elle soit complÚte ou incrémentielle). +```mermaid +graph LR + A["Données"] --> B["Sauvegarde
ComplĂšte"] + B --> C["Sauvegarde
Incrémentielle 01"] + C --> D["Sauvegarde
Incrémentielle 02"] +``` +*Schéma de fonctionnement de la sauvegarde incrémentielle.* + +- **La sauvegarde différentielle :** Elle sauvegarde les données modifiées uniquement depuis la derniÚre sauvegarde _complÚte_. +```mermaid +graph LR + A["Données"] --> B["Sauvegarde
ComplĂšte"] + B --> C["Sauvegarde
Différentielle 01"] + B --> D["Sauvegarde
DiffĂ©rentielle 02"] +``` +*SchĂ©ma de fonctionnement de la sauvegarde diffĂ©rentielle* + +### La rĂšgle d'or du 3-2-1 + +Il s'agit de la norme standard en administration systĂšme pour sĂ©curiser les donnĂ©es face Ă  tout type de perte. Elle se dĂ©compose ainsi : + +- **3** : Conserver trois copies des donnĂ©es au total (les donnĂ©es de production + deux sauvegardes). +- **2** : Utiliser deux supports de stockage distincts (par exemple, un serveur dĂ©diĂ© aux sauvegardes et un NAS). +- **1** : Garder une sauvegarde hors ligne ou externalisĂ©e (par exemple, des bandes magnĂ©tiques stockĂ©es dans un coffre-fort ou un NAS placĂ© dans un autre bĂątiment). + +### Les Ă©quipements matĂ©riels + +Pour soutenir cette stratĂ©gie et gĂ©rer des volumĂ©tries importantes (ex: 80 To), plusieurs Ă©quipements rĂ©seaux et de stockage sont nĂ©cessaires: + +- **Serveur de sauvegarde dĂ©diĂ© :** Un serveur physique Ă©quipĂ© d'un logiciel spĂ©cialisĂ© (comme Veeam Backup ou Proxmox Backup Server) pour orchestrer et centraliser les tĂąches de sauvegarde. +- **NAS (Network Attached Storage) :** Une baie de disques intelligente connectĂ©e au rĂ©seau (idĂ©alement en 10 Gbits ou SFP+) permettant le stockage des rĂ©plicas, souvent placĂ©e dans un local distant pour Ă©viter les destructions physiques simultanĂ©es. +- **Lecteur de bande magnĂ©tique (LTO) :** Permet une sauvegarde physique totalement dĂ©connectĂ©e du rĂ©seau (Air-Gap). C'est le dernier rempart indispensable contre les ransomwares. + +## Conclusion + +Une stratĂ©gie de sauvegarde pertinente et moderne s'appuie sur la combinaison de sauvegardes complĂštes rĂ©guliĂšres et d'incrĂ©mentielles frĂ©quentes pour optimiser l'espace. Le respect strict de la rĂšgle du 3-2-1, couplĂ© Ă  des technologies de sauvegardes immuables et Ă  des supports physiques hors ligne comme les bandes magnĂ©tiques, offre la meilleure rĂ©silience possible face aux sinistres et aux cyberattaques. \ No newline at end of file diff --git a/src/content/docs/notions/serveurs-physiques/choix-type-nombre-serveurs-physiques.md b/src/content/docs/notions/serveurs-physiques/choix-type-nombre-serveurs-physiques.md new file mode 100644 index 0000000..dcd3d54 --- /dev/null +++ b/src/content/docs/notions/serveurs-physiques/choix-type-nombre-serveurs-physiques.md @@ -0,0 +1,47 @@ +--- +title: Choix du type et du nombre de serveurs physiques +description: SynthĂšse choix du type et du nombre de serveurs physiques. Cours BTS SIO - Paul-louis Courier +--- + +## PrĂ©sentation de la problĂ©matique + +L'Ă©volution d'une infrastructure nĂ©cessite de choisir du matĂ©riel capable de supporter la charge de travail actuelle tout en anticipant les besoins futurs. La problĂ©matique centrale consiste Ă  dĂ©terminer la forme physique (tour, rack ou lame) et la quantitĂ© de serveurs nĂ©cessaires pour hĂ©berger les services d'une organisation. + +Ce choix ne dĂ©pend pas uniquement de la puissance brute, mais implique des enjeux d'encombrement spatial, de refroidissement, d'Ă©volutivitĂ© et de tolĂ©rance aux pannes. À titre d'exemple, une entreprise devant hĂ©berger une centaine de machines virtuelles impose obligatoirement la mise en place d'une haute disponibilitĂ©. Il faut donc rĂ©partir la charge (par exemple, un minimum de 60 cƓurs physiques et 1,5 To de RAM DDR5 ECC) sur plusieurs machines matĂ©rielles pour Ă©viter un point de dĂ©faillance unique (SPOF). + +## Les solutions + +Le choix du facteur de forme (form factor) dĂ©pend directement de la taille de l'infrastructure et de la salle d'hĂ©bergement. + +|**Type de Serveur**|**Description**|**Avantages**|**InconvĂ©nients**|**Cas d'usage**| +|---|---|---|---|---| +|**Serveur Tour (Tower)**|BoĂźtier indĂ©pendant similaire Ă  un PC de bureau massif.|Silencieux, ne nĂ©cessite pas d'armoire spĂ©cifique (baie), coĂ»t initial faible.|Encombrant, gestion des cĂąbles complexe, faible densitĂ©.|TPE/PME sans salle informatique dĂ©diĂ©e, bureaux isolĂ©s.| +|**Serveur Rack**|Serveur plat conçu pour ĂȘtre empilĂ© dans une armoire standardisĂ©e (mesurĂ© en UnitĂ©s 'U').|Excellente densitĂ©, Ă©volutivitĂ© simple, standardisation, gestion des flux d'air optimisĂ©e.|NĂ©cessite une baie de brassage/serveur, bruyant.|Datacenters, PME/ETI nĂ©cessitant une salle serveur classique.| +|**Serveur Lame (Blade)**|Carte Ă©lectronique (lame) ultra-compacte s'insĂ©rant dans un chĂąssis mutualisant l'alimentation et le rĂ©seau.|DensitĂ© maximale, cĂąblage drastiquement rĂ©duit, administration centralisĂ©e.|CoĂ»t d'entrĂ©e trĂšs Ă©levĂ© (achat du chĂąssis), risque de surchauffe important.|Grandes entreprises, calcul haute performance (HPC), trĂšs grands datacenters.| + +## Les concepts + +### Le dimensionnement (Sizing) et la Haute DisponibilitĂ© (HA) + +Pour rĂ©pondre Ă  la question "Combien de serveurs ?", il faut appliquer le principe de la redondance N+1 (voire N+2). + +Si l'ensemble des services nĂ©cessite une puissance totale X, on ne choisit pas un seul gros serveur, mais plusieurs serveurs plus petits. Si l'un tombe en panne, les autres doivent pouvoir absorber la charge des machines virtuelles redĂ©marrĂ©es. + +- **Calcul de base :** Diviser les besoins globaux (ex: 60 cƓurs, 1,5 To de RAM ) par les capacitĂ©s d'un modĂšle de serveur, et ajouter au moins un nƓud supplĂ©mentaire (N+1) pour garantir la continuitĂ© de service en cas de crash matĂ©riel. + +### La Redondance MatĂ©rielle + +Au-delĂ  du nombre de serveurs, chaque machine physique doit intĂ©grer ses propres sĂ©curitĂ©s pour Ă©viter de s'Ă©teindre Ă  la moindre dĂ©faillance d'un composant. + +- **Alimentation :** L'utilisation d'une double alimentation Ă©lectrique connectĂ©e Ă  des onduleurs distincts est indispensable pour maintenir le service. +- **Stockage local :** Le systĂšme d'exploitation de l'hyperviseur nĂ©cessite une tolĂ©rance aux pannes locale, gĂ©nĂ©ralement gĂ©rĂ©e par un contrĂŽleur RAID matĂ©riel en miroir (RAID 1). + +### L'Administration Hors Bande (Out-of-band management) + +Lorsque l'on dĂ©ploie une flotte de serveurs (en rack ou en lame), on ne branche plus d'Ă©cran ni de clavier directement dessus. On utilise des cartes de gestion Ă  distance intĂ©grĂ©es aux cartes mĂšres. + +- **Notion clĂ© :** Les technologies comme **iDRAC** (Dell) ou **iLO** (HP) permettent d'administrer le serveur au niveau matĂ©riel. On peut allumer, Ă©teindre, configurer le BIOS ou installer un OS Ă  distance, mĂȘme si le serveur est Ă©teint ou que l'OS principal a plantĂ©, grĂące Ă  un port rĂ©seau dĂ©diĂ©. + +## Conclusion + +Le choix du matĂ©riel est le socle de toute infrastructure systĂšme. Pour un environnement de virtualisation d'entreprise exigeant la haute disponibilitĂ©, les serveurs **rack** sont gĂ©nĂ©ralement le compromis idĂ©al entre densitĂ© et coĂ»t, tandis que les **lames** sont rĂ©servĂ©es aux trĂšs grosses volumĂ©tries. Le nombre exact de serveurs physiques dĂ©pend du calcul des ressources totales nĂ©cessaires (CPU/RAM/RĂ©seau) divisĂ© par la capacitĂ© unitaire, auquel on ajoute systĂ©matiquement un ou plusieurs nƓuds de secours (N+1) Ă©quipĂ©s de composants redondĂ©s et d'interfaces de gestion Ă  distance. \ No newline at end of file diff --git a/src/content/docs/notions/stockage/investissement-usage-architectures-san-nas.md b/src/content/docs/notions/stockage/investissement-usage-architectures-san-nas.md new file mode 100644 index 0000000..87a5d9c --- /dev/null +++ b/src/content/docs/notions/stockage/investissement-usage-architectures-san-nas.md @@ -0,0 +1,50 @@ +--- +title: Investissement et usages des architectures SAN et NAS +description: SynthĂšse sur l'investissement et usages des architectures SAN et NAS. Cours BTS SIO - Paul-louis Courier +--- + +--- +## PrĂ©sentation de la problĂ©matique + +Dans le cadre d'une Ă©volution d'infrastructure, comme le remplacement d'une ferme de serveurs de virtualisation, la question du stockage est centrale. L'objectif est de garantir la haute disponibilitĂ©, les performances et la sĂ©curitĂ© des donnĂ©es. Face Ă  des besoins importants (ex: 50 To pour les machines virtuelles et 80 To pour les sauvegardes ), le stockage local (dans le serveur) montre ses limites. Il faut alors se demander : est-il pertinent d’investir dans un SAN ou un NAS? Et pour quels usages prĂ©cis? + +Le choix entre ces deux technologies dĂ©pend de la nature des donnĂ©es Ă  stocker (fichiers vs blocs) et des exigences en matiĂšre de vitesse et de budget. + +## Les solutions + +Voici un comparatif des solutions pour rĂ©pondre Ă  cette problĂ©matique d'infrastructure : + +| **CritĂšre** | **Serveur de stockage NAS** | **RĂ©seau de stockage SAN** | +| ---------------------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| **Type de stockage** | Fichier (File-level) | Bloc (Block-level) | +| **RĂ©seau utilisĂ©** | RĂ©seau IP standard (LAN) | RĂ©seau dĂ©diĂ© haute vitesse (Fibre Channel, iSCSI) | +| **Protocoles** | SMB/CIFS, NFS | FC, iSCSI, FCoE | +| **CoĂ»t & ComplexitĂ©** | ModĂ©rĂ© / Simple Ă  administrer | TrĂšs Ă©levĂ© / Complexe | +| **Usage pertinent (Cas pratique)** | HĂ©bergement de sauvegardes dĂ©portĂ©es. Partage de fichiers utilisateurs. | Stockage mutualisĂ© pour hyperviseurs. HĂ©bergement des disques virtuels (VM). | + +## Les concepts + +### Le NAS (Network Attached Storage) + +Un NAS est un Ă©quipement de stockage connectĂ© au rĂ©seau local (LAN) qui met des fichiers Ă  disposition des utilisateurs et des serveurs. + +- **Fonctionnement :** Il gĂšre lui-mĂȘme son systĂšme de fichiers. Les clients lui demandent un fichier complet, et non des blocs de donnĂ©es brutes. +- **Usage dans une infrastructure :** Il est parfait pour le stockage de masse peu coĂ»teux, le partage de documents et l'archivage. Dans notre contexte, un NAS est l'Ă©quipement idĂ©al pour stocker les rĂ©plicas de sauvegardes dans un bĂątiment distant , afin de respecter la rĂšgle du 3-2-1 (3 copies, 2 supports, 1 hors ligne). + +### Le SAN (Storage Area Network) + +Un SAN n'est pas un simple boĂźtier, c'est un rĂ©seau de stockage dĂ©diĂ© et isolĂ© regroupant des baies de disques. + +- **Fonctionnement :** Contrairement au NAS, le SAN fournit des volumes de stockage bruts ("en mode bloc"). Le serveur (l'hyperviseur) qui s'y connecte le formate avec son propre systĂšme de fichiers (ex: VMFS pour VMware). Le serveur voit l'espace de stockage distant exactement comme si c'Ă©tait un disque dur physique branchĂ© Ă  l'intĂ©rieur de son propre chĂąssis. +- **Usage dans une infrastructure :** Le SAN est indispensable pour la virtualisation Ă  grande Ă©chelle. Il permet de stocker les disques durs des machines virtuelles (les 50 To requis ) sur des disques ultra-rapides (SSD/NVME ou SAS). C'est ce stockage partagĂ© en mode bloc qui permet la haute disponibilitĂ©: si un serveur physique tombe en panne, un autre serveur peut redĂ©marrer la VM instantanĂ©ment, car ils accĂšdent tous au mĂȘme SAN. + +### La Haute DisponibilitĂ© (HA) et la TolĂ©rance aux pannes + +Ces deux concepts sont souvent confondus mais sont complĂ©mentaires dans le stockage : + +- **TolĂ©rance aux pannes (RAID) :** Concerne le matĂ©riel physique. Si un disque dur lĂąche au sein du NAS ou du SAN, les donnĂ©es ne sont pas perdues grĂące Ă  la redondance des autres disques. +- **Haute DisponibilitĂ© (HA) :** Concerne le service. Un SAN disposant d'une double alimentation et de doubles contrĂŽleurs assure que le stockage reste toujours accessible aux hyperviseurs, garantissant une continuitĂ© de service maximale. + +## Conclusion + +L'investissement dans un NAS et un SAN est totalement pertinent, mais pour des usages strictement diffĂ©rents. Le **SAN** est le moteur performant de la production : il stocke les blocs de donnĂ©es des machines virtuelles et assure la haute disponibilitĂ© des hyperviseurs. Le **NAS**, quant Ă  lui, est le coffre-fort capacitaire : il permet d'archiver les donnĂ©es et d'externaliser les sauvegardes Ă  moindre coĂ»t dans un autre bĂątiment pour garantir la reprise d'activitĂ© en cas de sinistre. \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/assets/logo-imdeo.png b/src/content/docs/projets-bts-sio/imdeo/assets/logo-imdeo.png new file mode 100644 index 0000000..6dbddd2 Binary files /dev/null and b/src/content/docs/projets-bts-sio/imdeo/assets/logo-imdeo.png differ diff --git a/src/content/docs/projets-bts-sio/imdeo/index.md b/src/content/docs/projets-bts-sio/imdeo/index.md new file mode 100644 index 0000000..d8c5ed1 --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/index.md @@ -0,0 +1,56 @@ +--- +title: "Contexte" +description: "PrĂ©sentation globale du contexte, de l'environnement et des compĂ©tences mises en Ɠuvre pour IMDEO." +--- + +## Contexte IMDEO + +![Logo IMDEO](./assets/logo-imdeo.png) + +:::note[Informations rapides] +* **Nom de l'entitĂ© :** IMDEO (PĂŽle NumĂ©rique du Groupe HervĂ©) +* **Lieu :** DĂ©ploiement multi-sites (81 implantations pour le groupe) +* **Taille de l'infrastructure :** Grand groupe (3 500 salariĂ©s, 622 M€ de CA) +* **Mon rĂŽle :** Consultant Infrastructure et RĂ©seau +* **PĂ©rimĂštre d'action :** Conception, prototypage, dĂ©ploiement et documentation complĂšte d'infrastructures rĂ©seaux +::: + +## 📋 PrĂ©sentation de la situation + +J'interviens en tant que consultant pour la SSII du Groupe HervĂ© (fictif), plus prĂ©cisĂ©ment au sein d'IMDEO, la sociĂ©tĂ© en charge du PĂŽle NumĂ©rique. Le Groupe HervĂ© est une structure historique et massive comptant 3 500 collaborateurs rĂ©partis sur trois pĂŽles d'expertise majeurs : l'Énergie, l'Industrie et le NumĂ©rique. + +Le besoin initial de ce contexte est de concevoir et d'adapter l'infrastructure rĂ©seau pour soutenir les services informatiques du groupe. L'enjeu est de partir de l'analyse du besoin mĂ©tier pour aller jusqu'au prototypage, aux tests d'acceptation et au dĂ©ploiement technique de la solution, tout en garantissant un haut niveau de documentation. + +## 🎯 CompĂ©tences travaillĂ©es (Épreuve E4) + +Dans le cadre de ce contexte, plusieurs compĂ©tences et sous-compĂ©tences du rĂ©fĂ©rentiel officiel du BTS SIO ont Ă©tĂ© mobilisĂ©es et justifiĂ©es : + +| CompĂ©tence globale | Sous-compĂ©tence mobilisĂ©e | Justification et trace concrĂšte | +| --- | --- | --- | +| **1.4 Travailler en mode projet** | Analyser les objectifs et les modalitĂ©s | Analyse approfondie des besoins du Groupe HervĂ© pour Ă©laborer un dossier de choix technique et dĂ©finir les spĂ©cifications d'une nouvelle architecture rĂ©seau. | +| **1.5 Mettre Ă  disposition un service informatique** | RĂ©aliser les tests d’intĂ©gration et d’acceptation | DĂ©termination, prĂ©paration et exĂ©cution en environnement de simulation des tests nĂ©cessaires pour valider la maquette de la solution avant sa mise en production. | +| **1.5 Mettre Ă  disposition un service informatique** | DĂ©ployer un service | Installation, configuration physique et logique, puis dĂ©ploiement final des Ă©lĂ©ments d'infrastructure retenus pour le rĂ©seau. | + +## đŸ—ïž Environnement technique + +L'Ă©cosystĂšme technique mis en place s'appuie sur des standards avancĂ©s d'architecture rĂ©seau pour garantir la sĂ©curitĂ© et la disponibilitĂ© des services sur un environnement multi-sites. Le travail prĂ©liminaire s'est fortement appuyĂ© sur des logiciels de simulation pour valider les concepts avant tout dĂ©ploiement matĂ©riel. + +### Topologie rĂ©seau + +L'architecture repose sur un plan d'adressage et de nommage rigoureux, intĂ©grant des rĂšgles de routage complexes pour relier les diffĂ©rentes entitĂ©s. Une segmentation stricte des pĂ©rimĂštres rĂ©seau est appliquĂ©e pour isoler les flux des diffĂ©rents pĂŽles et clients du groupe. + +### Services existants + +* **Outils de conception et de simulation :** UtilisĂ©s pour maquetter l'infrastructure rĂ©seau, tester les protocoles de routage et valider les choix techniques. +* **Services d'administration Ă  distance :** DĂ©ploiement d'outils et protocoles sĂ©curisĂ©s pour diagnostiquer les problĂšmes et gĂ©rer les incidents techniques Ă  travers les diffĂ©rentes filiales. + +## 📄 Documentation + +Dans les sous-sections de ce contexte, vous retrouverez des articles thĂ©matiques liĂ©s Ă  ce projet (Virtualisation, Conteneurisation, RĂ©seau, etc.). + +:::note[PrĂ©cision importante] +La documentation prĂ©sente sur ce portfolio a pour unique but de prĂ©senter l'architecture et la logique derriĂšre mes choix techniques. Vous n'y retrouverez pas de documentation d'exploitation pure. + +Pour consulter les procĂ©dures techniques dĂ©taillĂ©es et les documentations d'installation de ce projet, je vous invite Ă  consulter ce lien : +👉 **[Documentation technique - IMDEO](https://ap-bts-sio-louis.github.io/imdeo/)** +::: \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/reseau/nat.mdx b/src/content/docs/projets-bts-sio/imdeo/reseau/nat.mdx new file mode 100644 index 0000000..31f1961 --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/reseau/nat.mdx @@ -0,0 +1,49 @@ +--- +title: "Mise en place du NAT" +description: "Compte-rendu d'intervention et rapport technique sur : Configuration du routage NAT/PAT sur l'infrastructure IMDEO." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention Ă©tait d'interconnecter le rĂ©seau local de l'infrastructure IMDEO Ă  Internet de maniĂšre sĂ©curisĂ©e. Il s'agissait de configurer un routeur Cisco pour partager une adresse IP publique unique avec plusieurs postes internes via la translation d'adresses par port (PAT), tout en appliquant une politique de sĂ©curitĂ© stricte visant Ă  isoler spĂ©cifiquement le VLAN d'administration de tout accĂšs externe. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **DĂ©finition des zones de confiance (Interfaces Inside/Outside) :** + La premiĂšre Ă©tape a consistĂ© Ă  cartographier le flux rĂ©seau sur le routeur. L'interface connectĂ©e au rĂ©seau local a Ă©tĂ© dĂ©clarĂ©e comme zone interne (Inside), tandis que l'interface de sortie vers le Fournisseur d'AccĂšs Internet a Ă©tĂ© dĂ©signĂ©e comme zone publique (Outside). Cela permet au routeur de savoir dans quel sens appliquer la traduction d'adresse. + +2. **ImplĂ©mentation du filtrage rĂ©seau (ACL) :** + Afin de sĂ©curiser l'infrastructure, une liste de contrĂŽle d'accĂšs (Access Control List) standard a Ă©tĂ© rĂ©digĂ©e. L'enjeu stratĂ©gique Ă©tait d'interdire explicitement le routage vers Internet pour le sous-rĂ©seau d'administration (en utilisant le masque gĂ©nĂ©rique inverse), tout en autorisant le trafic provenant des autres sous-rĂ©seaux opĂ©rationnels (comme le VLAN COM). + +3. **Activation de la translation d'adresses (PAT / Overload) :** + Le service de traduction a ensuite Ă©tĂ© activĂ© en liant l'ACL prĂ©cĂ©demment créée Ă  l'interface publique du routeur. L'utilisation du paramĂštre de surcharge (Overload) a Ă©tĂ© configurĂ©e pour activer le PAT (Port Address Translation), permettant ainsi de multiplexer les connexions simultanĂ©es de plusieurs machines internes sur une seule IP publique grĂące Ă  l'attribution de ports distincts. + +4. **Validation et audit des flux :** + La recette de l'intervention a Ă©tĂ© rĂ©alisĂ©e en gĂ©nĂ©rant du trafic ICMP (ping vers un DNS public) depuis un poste client autorisĂ©. L'audit de la table de traduction (`show ip nat translations`) et des statistiques globales a permis de confirmer la crĂ©ation dynamique des sessions NAT et le bon comptage des paquets traduits. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +- Le service d'accĂšs Ă  Internet est opĂ©rationnel pour les rĂ©seaux autorisĂ©s. +- Le VLAN Administration est correctement isolĂ© et bloquĂ© aux frontiĂšres du rĂ©seau interne. +- Le routeur multiplexe correctement les sessions (PAT) en traduisant les IP privĂ©es vers l'IP publique. +::: + +L'utilisation du masque gĂ©nĂ©rique (wildcard mask) lors de la crĂ©ation de l'ACL requiert une attention particuliĂšre, notamment en plaçant les rĂšgles de refus (`deny`) avant les rĂšgles d'autorisation (`permit`) pour garantir l'application correcte de la politique de sĂ©curitĂ©. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **VĂ©rifier les rĂšgles d'utilisation** | ImplĂ©mentation d'une politique de filtrage stricte (ACL) pour interdire l'accĂšs Internet au sous-rĂ©seau d'administration d'IMDEO. | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Configuration et activation du service NAT/PAT sur l'Ă©quipement de routage Cisco pour permettre la connectivitĂ© externe. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | VĂ©rification du trafic gĂ©nĂ©rĂ© (ping) et analyse des tables de traduction dynamiques sur le routeur pour valider le fonctionnement. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/reseau/plan-adressage.mdx b/src/content/docs/projets-bts-sio/imdeo/reseau/plan-adressage.mdx new file mode 100644 index 0000000..bd913bb --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/reseau/plan-adressage.mdx @@ -0,0 +1,46 @@ +--- +title: "CrĂ©ation du plan d'adressage" +description: "Compte-rendu d'intervention et rapport technique sur : Plan d'adressage." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention pour l'infrastructure IMDEO Ă©tait de concevoir une architecture rĂ©seau logique segmentĂ©e, sĂ©curisĂ©e et Ă©volutive. Le besoin initial consistait Ă  isoler les diffĂ©rents flux mĂ©tiers (Ressources Humaines, Direction, ComptabilitĂ©, Serveurs, etc.) pour limiter les domaines de diffusion et prĂ©parer la mise en place d'un routage inter-VLAN sĂ©curisĂ©. L'enjeu technique majeur Ă©tait d'optimiser l'espace d'adressage IPv4 en utilisant la mĂ©thode VLSM (Variable Length Subnet Mask) afin d'attribuer Ă  chaque service un bloc rĂ©seau correspondant exactement Ă  son besoin en nombre d'hĂŽtes, tout en Ă©vitant le gaspillage d'adresses. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, la conception du plan d'adressage a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases analytiques et logiques : + + +1. **Recensement des ressources et dimensionnement (VLSM) :** + Analyse des besoins capacitifs pour chaque service de l'entreprise IMDEO (de 2 hĂŽtes pour le lien FAI Ă  30 hĂŽtes pour les salles de rĂ©union). Le dĂ©coupage logique a Ă©tĂ© rĂ©alisĂ© sur la plage privĂ©e `192.168.1.0` en appliquant des masques de sous-rĂ©seaux Ă  taille variable (allant du `/27` pour les grands services au `/30` pour l'interconnexion FAI), garantissant une allocation stricte et sans chevauchement. + +2. **Segmentation par VLAN et isolation des flux :** + Attribution d'identifiants VLAN uniques (802.1Q) pour chaque dĂ©partement. Une dĂ©marcation claire a Ă©tĂ© Ă©tablie entre les postes clients (VLANs 2, 3, 4, 5, 6, 9, situĂ©s dans les blocs `192.168.x.x`) et l'infrastructure d'hĂ©bergement. Les serveurs ont Ă©tĂ© isolĂ©s dans un VLAN dĂ©diĂ© (VLAN 51) sur une classe d'adressage distincte (`172.16.51.0/24`), facilitant ainsi le futur filtrage applicatif (firewalling) et limitant la surface d'attaque. + +3. **DĂ©finition des limites de plages et des passerelles :** + Pour chaque sous-rĂ©seau, calcul prĂ©cis des adresses rĂ©seau, des adresses de diffusion (broadcast) et des plages d'adresses IP utilisables pour l'attribution statique ou DHCP. Par convention d'architecture, la derniĂšre adresse IP disponible de chaque plage a Ă©tĂ© dĂ©signĂ©e comme passerelle par dĂ©faut (Gateway) pour les Ă©quipements du sous-rĂ©seau (ex: `192.168.1.30` pour le rĂ©seau COM). + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** L'architecture logique est validĂ©e et prĂȘte Ă  ĂȘtre implĂ©mentĂ©e sur les Ă©quipements de niveau 2 et 3. +* **Optimisation IPv4 :** ZĂ©ro chevauchement d'IP et Ă©conomie de l'espace d'adressage grĂące au dĂ©coupage CIDR. +* **SĂ©curitĂ© By-Design :** SĂ©paration stricte des 8 domaines de diffusion (VLANs) facilitant l'application de rĂšgles de pare-feu (Zero Trust local). +* **Documentation :** Matrice de routage et plages d'attribution standardisĂ©es pour les futures configurations DHCP et interfaces virtuelles (SVI). +::: + +L'usage d'un rĂ©seau distinct (`192.168.2.0/27`) pour le service REUNION et d'une classe B (`172.16.51.0/24`) pour les SERVEURS permet d'anticiper une forte scalabilitĂ© et simplifiera la lecture des logs de sĂ©curitĂ© lors des audits de trafic. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **Recenser et identifier les ressources** | Analyse des besoins en nombre d'hĂŽtes pour chaque dĂ©partement d'IMDEO afin de prĂ©parer le dĂ©coupage rĂ©seau. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Application rigoureuse de la mĂ©thode VLSM (RFC 1812) et de la norme d'encapsulation 802.1Q (VLANs) pour la segmentation. | +| **1.4 Travailler en mode projet** | **Analyser les objectifs** | Conception d'une matrice IP rĂ©pondant directement Ă  un besoin d'isolation de sĂ©curitĂ© et de hiĂ©rarchisation des flux d'entreprise. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/reseau/relais-dhcp.mdx b/src/content/docs/projets-bts-sio/imdeo/reseau/relais-dhcp.mdx new file mode 100644 index 0000000..a1eb7dc --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/reseau/relais-dhcp.mdx @@ -0,0 +1,50 @@ +--- +title: "Mise en place du relais DHCP" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place du relais DHCP." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention au sein de l'infrastructure IMDEO est de centraliser la gestion de l'adressage IP. Dans un rĂ©seau segmentĂ©, les requĂȘtes DHCP initiales des clients (Ă©mises en broadcast) sont bloquĂ©es par les limites des sous-rĂ©seaux. La mission consiste Ă  implĂ©menter un agent de relais (IP Helper) sur les Ă©quipements de niveau 3 (commutateurs L3 et routeurs) pour intercepter ces diffusions locales et les relayer en unicast vers un serveur DHCP distant, garantissant ainsi l'obtention d'une configuration rĂ©seau complĂšte pour chaque poste isolĂ©. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **PrĂ©paration de l'environnement de routage :** + Activation du routage global sur les commutateurs de niveau 3 pour autoriser la transmission des paquets inter-VLAN. L'accessibilitĂ© unicast vers le serveur DHCP cible a Ă©tĂ© prĂ©alablement vĂ©rifiĂ©e via des tests de connectivitĂ© ICMP. + +2. **Configuration du relais (IP Helper) sur les interfaces :** + DĂ©ploiement de l'adresse IP du serveur DHCP distant directement sur les interfaces agissant comme passerelle pour les clients. Selon la topologie, cette configuration a Ă©tĂ© appliquĂ©e sur les interfaces virtuelles de VLAN (SVI) pour les commutateurs L3, ou sur les interfaces physiques/sous-interfaces pour les routeurs (architecture Router-on-a-Stick). + +3. **Validation de l'intĂ©gration (Recette) :** + VĂ©rification de la bonne prise en compte du paramĂštre relais sur chaque Ă©quipement rĂ©seau. CĂŽtĂ© client, une demande manuelle de renouvellement de bail (`ipconfig /renew`) a Ă©tĂ© exĂ©cutĂ©e pour s'assurer que le poste obtient une adresse IP valide, son masque et sa passerelle via le serveur distant. + +4. **VĂ©rification centralisĂ©e et sauvegarde :** + ContrĂŽle sur la console du serveur DHCP distant pour confirmer la prĂ©sence du nouveau bail (association IP/MAC). Enfin, sauvegarde des configurations actives dans la NVRAM des Ă©quipements Cisco (`write`) pour figer l'implĂ©mentation. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +* Le routage global est actif et le serveur DHCP distant est joignable depuis les Ă©quipements. +* La fonctionnalitĂ© de relais DHCP est opĂ©rationnelle sur l'ensemble des interfaces ciblĂ©es. +* Les postes clients acquiĂšrent dynamiquement leur configuration rĂ©seau de maniĂšre transparente. +* Les baux sont correctement centralisĂ©s et visibles sur le serveur DHCP de l'infrastructure. +::: + +L'application systĂ©matique des procĂ©dures de recette, allant du test de l'Ă©quipement relais jusqu'Ă  la vĂ©rification des baux cĂŽtĂ© serveur, a permis de garantir une mise en production sans perturbation pour le rĂ©seau local. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | ParamĂ©trage de la fonction de relais DHCP (IP Helper) sur les interfaces passerelles des routeurs et commutateurs L3. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | ExĂ©cution d'un plan de recette complet : vĂ©rification des configurations d'interfaces, pings de validation et renouvellement forcĂ© des baux clients. | +| **1.1 GĂ©rer le patrimoine informatique** | **VĂ©rifier la continuitĂ©** | Sauvegarde des configurations courantes vers la mĂ©moire non-volatile (NVRAM) pour garantir le maintien du service rĂ©seau aprĂšs un Ă©ventuel redĂ©marrage. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/reseau/routage.mdx b/src/content/docs/projets-bts-sio/imdeo/reseau/routage.mdx new file mode 100644 index 0000000..d950b6b --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/reseau/routage.mdx @@ -0,0 +1,43 @@ +--- +title: "Mise en place du routage sur un commutateur et un routeur de niveau 3 cisco" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place du routage sur un commutateur et un routeur de niveau 3 cisco." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +Dans le cadre de la prĂ©paration de l'infrastructure rĂ©seau d'IMDEO, l'objectif de cette intervention Ă©tait d'Ă©tablir l'adressage et d'assurer le routage interne et externe. Il s'agissait de configurer le routage inter-VLAN sur un commutateur de niveau 3 ainsi que sur un routeur Cisco, afin de garantir l'isolation logique des flux (VLANs) tout en permettant la communication contrĂŽlĂ©e entre les diffĂ©rents sous-rĂ©seaux et l'accĂšs vers l'extĂ©rieur (WAN). + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **Activation du routage et dĂ©finition des passerelles internes (Commutateur L3) :** + Activation du moteur de routage IP global du commutateur. Les VLANs de production (ex: COM) ont Ă©tĂ© dĂ©clarĂ©s et associĂ©s Ă  des interfaces virtuelles de routage (SVI). Chaque interface virtuelle a reçu une configuration IP pour agir en tant que passerelle par dĂ©faut pour son rĂ©seau cible. + +2. **Configuration de l'encapsulation et des liaisons Trunk inter-Ă©quipements :** + SĂ©curisation des liens inter-commutateurs en forçant le mode Trunk et en restreignant le trafic aux seuls VLANs nĂ©cessaires. CĂŽtĂ© routeur (approche "Router-on-a-Stick"), le port physique a Ă©tĂ© activĂ© pour supporter des sous-interfaces logiques. Chacune a Ă©tĂ© configurĂ©e avec le standard d'encapsulation 802.1Q pour traiter spĂ©cifiquement les trames taguĂ©es de chaque VLAN. + +3. **Interconnexion WAN et dĂ©finition des routes de dernier recours :** + Attribution de l'adressage IP sur l'interface de sortie WAN du routeur. Enfin, des routes statiques par dĂ©faut (`0.0.0.0 0.0.0.0`) ont Ă©tĂ© dĂ©ployĂ©es en cascade : le commutateur L3 redirige le trafic inconnu vers le routeur interne, et le routeur transfĂšre ce flux sortant vers l'Ă©quipement du fournisseur d'accĂšs Ă  Internet (FAI). + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +- Le routage inter-VLAN est pleinement opĂ©rationnel via les SVI et les sous-interfaces du routeur. +- La segmentation rĂ©seau est respectĂ©e grĂące au filtrage 802.1Q sur les liens agrĂ©gĂ©s. +- Les requĂȘtes sortantes (Internet/WAN) sont correctement acheminĂ©es par le biais des routes par dĂ©faut statiques. +::: + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | ImplĂ©mentation du standard d'encapsulation IEEE 802.1Q sur les sous-interfaces et les liens Trunk pour la gestion des trames rĂ©seau taguĂ©es. | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | DĂ©ploiement et configuration des services de routage IP (statique et inter-VLAN) sur les Ă©quipements d'infrastructure Cisco (Commutateur L3 et Routeur). | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/reseau/ssh.mdx b/src/content/docs/projets-bts-sio/imdeo/reseau/ssh.mdx new file mode 100644 index 0000000..fefbba4 --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/reseau/ssh.mdx @@ -0,0 +1,51 @@ +--- +title: "Mise en place de la connexion SSH sur le commutateur et le routeur" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place de la connexion SSH sur le commutateur et le routeur." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +Dans le cadre de la prĂ©paration de l'infrastructure rĂ©seau pour le projet IMDEO, il Ă©tait impĂ©ratif de sĂ©curiser l'administration distante des Ă©quipements actifs (routeurs et commutateurs). L'objectif de cette intervention est de bannir les protocoles en clair, nativement vulnĂ©rables (comme Telnet), au profit du protocole chiffrĂ© SSH (Secure Shell). Le rĂ©sultat attendu est un accĂšs distant robuste, authentifiĂ© localement et chiffrĂ© selon les standards de sĂ©curitĂ© actuels. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **DĂ©finition de l'identitĂ© de l'Ă©quipement :** + L'activation du protocole SSH requiert au prĂ©alable la configuration stricte d'un nom d'hĂŽte (hostname) et d'un nom de domaine. Cette association logique est indispensable pour permettre Ă  l'Ă©quipement rĂ©seau de gĂ©nĂ©rer ultĂ©rieurement son propre certificat matĂ©riel. + +2. **GĂ©nĂ©ration du matĂ©riel cryptographique :** + Pour assurer la confidentialitĂ© totale des Ă©changes, une paire de clĂ©s asymĂ©triques (publique/privĂ©e) a Ă©tĂ© gĂ©nĂ©rĂ©e. L'algorithme RSA a Ă©tĂ© sĂ©lectionnĂ© avec une taille de clĂ© de 2048 bits, respectant ainsi les recommandations de sĂ©curitĂ© de l'industrie pour contrer les attaques par force brute. + +3. **SĂ©curisation des accĂšs virtuels (VTY) et habilitations :** + Un compte d'administration local a Ă©tĂ© créé avec les privilĂšges maximaux (niveau 15), en s'assurant que son mot de passe soit stockĂ© sous forme hachĂ©e dans la configuration. Les lignes d'accĂšs Ă  distance (VTY) ont ensuite Ă©tĂ© durcies : obligation de s'authentifier sur la base locale, forçage de la version 2 du protocole SSH, et blocage explicite de tout autre protocole entrant non chiffrĂ©. La configuration a ensuite Ă©tĂ© validĂ©e et enregistrĂ©e en mĂ©moire non volatile (NVRAM). + +4. **Validation et tests d'intĂ©gration :** + Le statut du dĂ©mon SSH a Ă©tĂ© contrĂŽlĂ© directement sur l'Ă©quipement actif pour confirmer l'activation exclusive de la version 2.0. Un test de connectivitĂ© rĂ©el a ensuite Ă©tĂ© opĂ©rĂ© depuis le terminal d'un poste client de la maquette afin de valider l'authentification et l'Ă©tablissement du tunnel sĂ©curisĂ©. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +- Le service SSHv2 est pleinement actif et opĂ©rationnel sur l'Ă©quipement rĂ©seau. +- L'accĂšs d'administration non sĂ©curisĂ© (Telnet) est dĂ©finitivement bloquĂ©. +- L'authentification distante exige dĂ©sormais un compte local chiffrĂ© Ă  hauts privilĂšges. +- La configuration est sauvegardĂ©e et survivra aux redĂ©marrages de l'Ă©quipement. +::: + +L'intervention s'est dĂ©roulĂ©e sans anomalie. L'implĂ©mentation d'une clĂ© RSA de 2048 bits couplĂ©e Ă  la dĂ©sactivation de SSHv1 garantit une surface d'attaque rĂ©duite et un niveau de sĂ©curitĂ© optimal pour les futures opĂ©rations de maintenance DevOps sur le rĂ©seau IMDEO. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations** | CrĂ©ation d'un compte administrateur local avec privilĂšge maximal (niveau 15) et mot de passe chiffrĂ© pour encadrer et restreindre l'accĂšs Ă  l'Ă©quipement. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Application des standards de cryptographie actuels en gĂ©nĂ©rant des clĂ©s RSA de 2048 bits et en forçant l'utilisation exclusive de SSH version 2. | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Configuration, sĂ©curisation et activation du service de connexion distante SSH sur les lignes virtuelles (VTY) de l'Ă©quipement. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | ContrĂŽle de l'Ă©tat du service cryptographique et validation de la connectivitĂ© via l'ouverture d'une session SSH depuis un poste client. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/reseau/vlan-vtp.mdx b/src/content/docs/projets-bts-sio/imdeo/reseau/vlan-vtp.mdx new file mode 100644 index 0000000..5040b32 --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/reseau/vlan-vtp.mdx @@ -0,0 +1,48 @@ +--- +title: "Distribution automatique des vlans (VTP)" +description: "Compte-rendu d'intervention et rapport technique sur : Distribution automatique des vlans (VTP)." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention au sein de l'infrastructure du rĂ©seau IMDEO Ă©tait d'automatiser et de centraliser la gestion des VLANs. Afin de rĂ©duire la charge administrative et de prĂ©venir les erreurs de configuration manuelle, le protocole VTP (VLAN Trunking Protocol) a Ă©tĂ© dĂ©ployĂ©. L'enjeu technique consistait Ă  configurer un commutateur maĂźtre (serveur) capable de propager de maniĂšre dynamique et sĂ©curisĂ©e sa base de donnĂ©es de VLANs vers les commutateurs pĂ©riphĂ©riques (clients). + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **PrĂ©paration et configuration du serveur VTP :** + Le commutateur principal a Ă©tĂ© dĂ©fini avec le rĂŽle de serveur VTP. Le domaine d'administration ("IMDEO") et une clĂ© d'authentification ont Ă©tĂ© configurĂ©s pour sĂ©curiser la topologie. Les VLANs nĂ©cessaires Ă  l'infrastructure ont Ă©tĂ© créés sur cet Ă©quipement central, et les ports d'interconnexion ont Ă©tĂ© paramĂ©trĂ©s en mode agrĂ©gation (Trunk) pour autoriser la diffusion des trames. + +2. **DĂ©ploiement des clients VTP :** + Les commutateurs secondaires ont Ă©tĂ© raccordĂ©s au serveur via des liens Trunk. Ils ont ensuite Ă©tĂ© configurĂ©s en mode "Client" et intĂ©grĂ©s au domaine VTP en renseignant les mĂȘmes paramĂštres de sĂ©curitĂ©, ce qui leur a permis de synchroniser automatiquement leur base de donnĂ©es locale. + +3. **SĂ©curisation de la rĂ©vision VTP :** + Afin de prĂ©venir tout Ă©crasement accidentel de la base VLAN (dĂ» Ă  l'insertion d'un switch avec un numĂ©ro de rĂ©vision supĂ©rieur), une procĂ©dure de rĂ©initialisation a Ă©tĂ© documentĂ©e et validĂ©e. Elle consiste Ă  basculer temporairement un Ă©quipement en mode "Transparent" ou Ă  modifier son domaine pour forcer la remise Ă  zĂ©ro de son compteur de rĂ©vision. + +4. **Tests et validation (Recette) :** + La bonne propagation rĂ©seau a Ă©tĂ© vĂ©rifiĂ©e en consultant l'Ă©tat de synchronisation des clients. Un test d'injection a Ă©tĂ© rĂ©alisĂ© en crĂ©ant un rĂ©seau virtuel de test (VLAN 999) sur le serveur, confirmant sa redĂ©scente instantanĂ©e sur l'ensemble de l'infrastructure. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +- Le commutateur serveur centralise parfaitement la base VLAN. +- Les liaisons Trunk acheminent correctement les trames de gestion. +- Les commutateurs clients rĂ©cupĂšrent dynamiquement l'intĂ©gralitĂ© des VLANs sans configuration manuelle locale. +- Le rĂ©seau est protĂ©gĂ© contre les anomalies de synchronisation grĂące Ă  la maĂźtrise des numĂ©ros de rĂ©vision. +::: + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | ImplĂ©mentation du protocole standard VTP (niveau 2 OSI) pour uniformiser l'architecture de commutation logicielle. | +| **1.2 RĂ©pondre aux incidents / assistance** | **Traiter les demandes (rĂ©seau/systĂšme/applicatif)** | Anticipation et rĂ©solution d'incidents de rĂ©vision VTP via une procĂ©dure de rĂ©initialisation sĂ©curisĂ©e (mode transparent). | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Configuration du rĂŽle serveur VTP, paramĂ©trage des trunks, et automatisation de la distribution des rĂ©seaux logiques vers les Ă©quipements clients. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/systeme/kea-dhcp.mdx b/src/content/docs/projets-bts-sio/imdeo/systeme/kea-dhcp.mdx new file mode 100644 index 0000000..eaaac39 --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/systeme/kea-dhcp.mdx @@ -0,0 +1,51 @@ +--- +title: "Mise en place de Kea DHCP sur Debian 13" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place de Kea DHCP sur Debian 13." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention Ă©tait de moderniser et de sĂ©curiser le service d'attribution des adresses IP au sein de l'infrastructure en dĂ©ployant un serveur Kea DHCP (le successeur moderne et modulaire d'ISC DHCP) sur un environnement Debian 13. Le besoin initial consistait Ă  segmenter le rĂ©seau en plusieurs sous-rĂ©seaux distincts (Commercial, Ressources Humaines, Direction, ComptabilitĂ©) tout en assurant une haute disponibilitĂ© du service (tolĂ©rance aux pannes) via une architecture redondĂ©e s'appuyant sur des relais DHCP au niveau du routeur. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **PrĂ©paration du systĂšme et installation du service :** + Avant tout dĂ©ploiement, le systĂšme d'exploitation Debian a Ă©tĂ© intĂ©gralement mis Ă  jour pour garantir la sĂ©curitĂ© et la stabilitĂ© de l'environnement. Le paquet spĂ©cifique `kea-dhcp4-server`, qui gĂšre le protocole IPv4, a ensuite Ă©tĂ© installĂ© et son Ă©tat d'exĂ©cution initial a Ă©tĂ© vĂ©rifiĂ© auprĂšs du gestionnaire de services du systĂšme. + +2. **SĂ©curisation et configuration des Ă©tendues DHCP :** + ConformĂ©ment aux bonnes pratiques d'administration, une sauvegarde du fichier de configuration par dĂ©faut a Ă©tĂ© rĂ©alisĂ©e (crĂ©ation d'un fichier `.bak`) avant toute modification. Le fichier de configuration, structurĂ© en JSON, a ensuite Ă©tĂ© Ă©ditĂ© de maniĂšre sĂ©curisĂ©e pour dĂ©clarer l'interface d'Ă©coute et dĂ©finir les diffĂ©rents sous-rĂ©seaux (Commercial, RH, DIR, COMPTA). Pour chaque segment, des plages d'adresses spĂ©cifiques, des serveurs DNS et des passerelles par dĂ©faut ont Ă©tĂ© rigoureusement attribuĂ©s. + +3. **Validation syntaxique et application :** + Afin d'Ă©viter toute interruption de service due Ă  une erreur de frappe, la syntaxe du nouveau fichier de configuration a Ă©tĂ© testĂ©e Ă  blanc avec un outil de diagnostic intĂ©grĂ©, en utilisant les droits de l'utilisateur de service restreint (`_kea`). Une fois la validation confirmĂ©e, le service a Ă©tĂ© redĂ©marrĂ© pour appliquer la nouvelle politique de distribution rĂ©seau. + +4. **ImplĂ©mentation du relais DHCP et tests de Haute DisponibilitĂ© (HA) :** + Le routeur cƓur de rĂ©seau (Cisco) a Ă©tĂ© configurĂ© avec des agents de relais (`ip helper-address`) pour intercepter les requĂȘtes de diffusion des clients et les transfĂ©rer en unicast vers nos deux serveurs Kea DHCP. Des tests poussĂ©s ont Ă©tĂ© rĂ©alisĂ©s depuis un poste client Windows (libĂ©ration et renouvellement des baux IP) pour simuler la panne totale du serveur principal et confirmer que le serveur secondaire prenait le relais instantanĂ©ment, garantissant ainsi la continuitĂ© d'activitĂ©. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** L'infrastructure DHCP est dĂ©sormais pleinement opĂ©rationnelle et rĂ©siliente. +* Les postes clients obtiennent dynamiquement leurs configurations rĂ©seau (IP, DNS, Passerelle) en fonction de leur sous-rĂ©seau d'appartenance. +* Le relais DHCP sur le routeur Cisco intercepte et redirige correctement le trafic. +* Le mĂ©canisme de tolĂ©rance aux pannes a Ă©tĂ© validĂ© : la perte du serveur DHCP principal n'impacte pas l'attribution des baux grĂące Ă  la prise de relais du serveur secondaire. +::: + +L'utilisation de la vĂ©rification syntaxique avant le redĂ©marrage du service s'est rĂ©vĂ©lĂ©e ĂȘtre une Ă©tape cruciale pour maintenir la stabilitĂ© de l'infrastructure lors du paramĂ©trage intensif du format JSON, Ă©vitant ainsi un crash silencieux du dĂ©mon. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Installation du paquet `kea-dhcp4-server` sur Debian 13 et paramĂ©trage des Ă©tendues rĂ©seau (Commercial, RH, DIR, COMPTA) via la configuration JSON. | +| **1.1 GĂ©rer le patrimoine informatique** | **GĂ©rer des sauvegardes** | CrĂ©ation d'une sauvegarde prĂ©ventive du fichier de configuration initial (`kea-dhcp4.conf.bak`) avant d'appliquer les nouvelles rĂšgles. | +| **1.1 GĂ©rer le patrimoine informatique** | **VĂ©rifier la continuitĂ©** | Planification et validation de la haute disponibilitĂ© en simulant l'arrĂȘt du serveur principal pour vĂ©rifier la prise de relais par le serveur secondaire. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | ExĂ©cution du plan de recette : vĂ©rification des relais (`ip helper-address`) sur le routeur Cisco et tests de renouvellement de bail (`ipconfig /renew`) sur un poste client. | +| **1.1 GĂ©rer le patrimoine informatique** | **VĂ©rifier les rĂšgles d'utilisation** | Utilisation de l'outil de diagnostic intĂ©grĂ© (`-t`) et d'un utilisateur de service restreint (`-u _kea`) pour valider la configuration avant application. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/systeme/lvm.mdx b/src/content/docs/projets-bts-sio/imdeo/systeme/lvm.mdx new file mode 100644 index 0000000..58903a3 --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/systeme/lvm.mdx @@ -0,0 +1,49 @@ +--- +title: "Partitionnement de LVM sur Debian 13" +description: "Compte-rendu d'intervention et rapport technique sur : Partitionnement de LVM sur Debian 13." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +Dans le cadre du dĂ©ploiement de l'infrastructure IMDEO, l'objectif de cette intervention Ă©tait d'installer un serveur Debian 13 en abandonnant le partitionnement classique au profit de la technologie LVM (Logical Volume Manager). Ce choix d'architecture de stockage rĂ©pond Ă  un besoin critique de flexibilitĂ©, de sĂ©curitĂ© et d'Ă©volutivitĂ©. L'enjeu principal consistait Ă  isoler logiquement les diffĂ©rentes arborescences du systĂšme (comme `/var` pour les journaux ou `/home` pour les donnĂ©es utilisateurs) afin de garantir qu'une saturation d'espace sur un service n'entraĂźne pas le crash global du serveur, tout en permettant de redimensionner les disques "Ă  chaud" Ă  l'avenir. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s lors de l'installation de l'OS : + + +1. **PrĂ©paration de la partition d'amorçage (/boot) hors LVM :** + Le chargeur d'amorçage (GRUB) nĂ©cessitant un accĂšs direct au noyau Linux avant de pouvoir charger les modules complexes de LVM, une partition primaire standard a Ă©tĂ© créée en tout dĂ©but de disque. FormatĂ©e en `ext4` avec 1 Go d'espace, elle garantit un stockage suffisant pour conserver sereinement plusieurs versions du noyau au fil des mises Ă  jour de sĂ©curitĂ©. + +2. **CrĂ©ation du conteneur physique et du groupe de volumes (VG) :** + L'espace libre restant sur le disque physique a Ă©tĂ© initialisĂ© puis agrĂ©gĂ© pour former un Groupe de Volumes (VG) nommĂ© `vg_imdeo`. Ce groupe agit comme un vaste "rĂ©servoir" abstrait du matĂ©riel. Si le serveur vient Ă  manquer de place Ă  l'avenir, il suffira d'ajouter un nouveau disque physique dans ce mĂȘme rĂ©servoir. + +3. **DĂ©coupage des volumes logiques (LV) et isolation des donnĂ©es :** + À partir du rĂ©servoir global, plusieurs Volumes Logiques (LV) ont Ă©tĂ© taillĂ©s sur mesure pour segmenter le systĂšme : `lvroot` pour la racine de l'OS, `lvhome` pour les profils utilisateurs, `lvvar` pour la variabilitĂ© (logs, bases de donnĂ©es) et `lvswap` pour la mĂ©moire virtuelle. Cette Ă©tape est la clĂ© de voĂ»te de la rĂ©silience du systĂšme. + +4. **Formatage (BTRFS) et dĂ©finition des points de montage :** + Chaque volume logique a ensuite Ă©tĂ© formatĂ© et liĂ© Ă  un dossier systĂšme. Le systĂšme de fichiers moderne `btrfs` a Ă©tĂ© sĂ©lectionnĂ© pour les partitions de donnĂ©es. Ce choix technique stratĂ©gique permet de bĂ©nĂ©ficier du redimensionnement dynamique Ă  chaud et prĂ©pare le terrain pour une gestion optimisĂ©e des snapshots. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** L'installation du systĂšme Debian 13 s'est achevĂ©e avec une structure de stockage rĂ©siliente et opĂ©rationnelle. +- Le serveur dĂ©marre correctement de maniĂšre autonome grĂące Ă  l'isolation de la partition `/boot`. +- L'intĂ©gritĂ© du systĂšme est protĂ©gĂ©e : la saturation potentielle des journaux (`/var`) ou des donnĂ©es utilisateurs (`/home`) ne peut plus bloquer la partition racine (`/`). +- L'infrastructure est hautement Ă©volutive : l'espace de stockage peut dĂ©sormais ĂȘtre Ă©tendu dynamiquement en production grĂące Ă  la combinaison de LVM et BTRFS. +::: + +L'attention devra dĂ©sormais se porter sur le monitoring de ces volumes. L'utilisation future de snapshots (bien que trĂšs pratique pour les retours en arriĂšre rapides) nĂ©cessitera une vigilance particuliĂšre sur le remplissage du groupe de volumes pour ne pas dĂ©grader les performances d'Ă©criture (I/O). + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **VĂ©rifier la continuitĂ©** | Isolation logique des points de montage critiques (`/var`, `/home`) pour prĂ©venir tout dĂ©ni de service (crash systĂšme) liĂ© Ă  une saturation de l'espace de stockage. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | ImplĂ©mentation des standards de partitionnement serveur actuels (LVM, ext4 pour l'amorçage, BTRFS pour les donnĂ©es) favorisant la sĂ©curitĂ© et la maintenabilitĂ©. | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | DĂ©ploiement, configuration de l'architecture de stockage sous-jacente et installation du systĂšme d'exploitation Debian 13 pour l'infrastructure IMDEO. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/systeme/securisation-grub-debian.mdx b/src/content/docs/projets-bts-sio/imdeo/systeme/securisation-grub-debian.mdx new file mode 100644 index 0000000..e819653 --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/systeme/securisation-grub-debian.mdx @@ -0,0 +1,50 @@ +--- +title: "SĂ©curisation du bootloader GRUB 2" +description: "Compte-rendu d'intervention et rapport technique sur : SĂ©curisation du bootloader GRUB 2." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'accĂšs physique ou console Ă  un serveur non sĂ©curisĂ© reprĂ©sente une vulnĂ©rabilitĂ© critique. Par dĂ©faut, le chargeur d'amorçage GRUB 2 permet Ă  n'importe quel utilisateur d'interrompre la sĂ©quence de dĂ©marrage pour injecter des paramĂštres noyau (comme `init=/bin/sh`). Cette faille permet d'obtenir un accĂšs `root` en lecture-Ă©criture et de compromettre l'intĂ©gralitĂ© du systĂšme, notamment en rĂ©initialisant les mots de passe Ă  l'insu des administrateurs[cite: 1]. L'objectif de cette intervention est de verrouiller l'Ă©dition du GRUB par une authentification forte, tout en garantissant une expĂ©rience de saisie fiable (clavier AZERTY) pour les opĂ©rations de maintenance. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **Audit et dĂ©monstration de la compromission initiale :** + Avant de sĂ©curiser le systĂšme, une simulation d'attaque a Ă©tĂ© rĂ©alisĂ©e en modifiant les paramĂštres d'amorçage au vol. L'ajout de l'instruction de lancement d'un shell direct a permis de monter le volume racine de la distribution, dĂ©montrant la facilitĂ© avec laquelle un attaquant peut usurper des droits d'administration et modifier des mots de passe systĂšme. + +2. **IntĂ©gration de la disposition clavier AZERTY :** + GRUB utilisant nativement une disposition QWERTY, il Ă©tait impĂ©ratif de configurer le clavier en français (AZERTY) pour Ă©viter toute erreur lors de la saisie future du mot de passe complexe. Un fichier de disposition spĂ©cifique a Ă©tĂ© compilĂ© Ă  partir de la cartographie systĂšme, puis injectĂ© dans les modules de configuration personnalisĂ©e de GRUB. + +3. **Mise en place des habilitations et du chiffrement :** + Afin de ne jamais stocker de secret en clair, une empreinte cryptographique robuste (condensat PBKDF2) a Ă©tĂ© gĂ©nĂ©rĂ©e via l'outil dĂ©diĂ©. Un compte superutilisateur exclusif Ă  l'amorçage (`adminsio`) a ensuite Ă©tĂ© dĂ©clarĂ© dans les fichiers de configuration, en l'associant directement Ă  ce hash. + +4. **DĂ©ploiement des rĂšgles et validation :** + Une exception (directive `--unrestricted`) a Ă©tĂ© implĂ©mentĂ©e dans les scripts de gĂ©nĂ©ration pour s'assurer que la machine dĂ©marre son systĂšme d'exploitation normalement sans demander d'identifiants. Le verrouillage ne s'applique ainsi qu'en cas de tentative d'Ă©dition. Les configurations ont ensuite Ă©tĂ© compilĂ©es et appliquĂ©es avant un redĂ©marrage de test. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +- Le serveur est dĂ©sormais protĂ©gĂ© contre l'altĂ©ration physique de ses paramĂštres de dĂ©marrage. +- Toute tentative d'Ă©dition de la sĂ©quence de boot (touche `e`) dĂ©clenche une demande d'authentification exigeant les identifiants du compte `adminsio`. +- Le module clavier AZERTY est correctement chargĂ©, garantissant une saisie fiable du mot de passe. +- Le dĂ©marrage standard des services n'est pas bloquĂ©, prĂ©servant la continuitĂ© de service. +::: + +L'intervention a exigĂ© une attention particuliĂšre quant Ă  l'ordre d'exĂ©cution des scripts : le module gĂ©rant la disposition du clavier a dĂ» ĂȘtre scrupuleusement appelĂ© *avant* la dĂ©claration de l'authentification PBKDF2 pour garantir que le clavier physique soit correctement reconnu au moment de la frappe. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations** | CrĂ©ation du compte superutilisateur GRUB (`adminsio`) et intĂ©gration d'un mot de passe hachĂ© (PBKDF2) pour restreindre l'Ă©dition des paramĂštres d'amorçage. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Application des bonnes pratiques de sĂ©curitĂ© systĂšmes en gĂ©nĂ©rant un condensat cryptographique plutĂŽt qu'un stockage de mot de passe en clair. | +| **1.1 GĂ©rer le patrimoine informatique** | **VĂ©rifier les rĂšgles d'utilisation** | Simulation d'une faille de sĂ©curitĂ© physique (Ă©lĂ©vation de privilĂšges via `init=/bin/sh`) pour justifier, vĂ©rifier et valider le durcissement du serveur. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/imdeo/systeme/ssh-debian.mdx b/src/content/docs/projets-bts-sio/imdeo/systeme/ssh-debian.mdx new file mode 100644 index 0000000..6eebcd5 --- /dev/null +++ b/src/content/docs/projets-bts-sio/imdeo/systeme/ssh-debian.mdx @@ -0,0 +1,50 @@ +--- +title: "Mise en place du SSH sur Debian 13" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place du SSH sur Debian 13." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention Ă©tait d'Ă©tablir un canal de communication sĂ©curisĂ© pour l'administration distante d'un serveur Debian 13. Le besoin initial consistait Ă  abandonner l'authentification traditionnelle par mot de passe, particuliĂšrement vulnĂ©rable aux attaques par force brute, au profit d'une authentification forte reposant sur la cryptographie asymĂ©trique. Il s'agissait Ă©galement de durcir la configuration du service pour rĂ©pondre aux standards de sĂ©curitĂ© actuels des infrastructures (DevSecOps). + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **DĂ©ploiement et initialisation du service :** + Installation du paquet `openssh-server` afin de dĂ©ployer le dĂ©mon permettant d'Ă©couter les requĂȘtes de connexion entrantes. Le statut du service a ensuite Ă©tĂ© vĂ©rifiĂ© pour s'assurer de son bon fonctionnement en arriĂšre-plan. + +2. **Mise en place de l'authentification par clĂ©s asymĂ©triques :** + GĂ©nĂ©ration d'une paire de clĂ©s cryptographiques sur le poste client en utilisant l'algorithme Ed25519, reconnu pour sa robustesse et ses performances. La clĂ© publique a ensuite Ă©tĂ© injectĂ©e de maniĂšre sĂ©curisĂ©e sur le serveur dans le fichier des clĂ©s autorisĂ©es, liant ainsi cryptographiquement le poste client au serveur. + +3. **Durcissement (Hardening) de la configuration SSH :** + Modification du fichier de configuration principal (`sshd_config`) pour verrouiller les accĂšs. L'authentification par mot de passe a Ă©tĂ© intĂ©gralement dĂ©sactivĂ©e, et la connexion directe via le compte super-utilisateur (`root`) a Ă©tĂ© formellement interdite, forçant ainsi l'Ă©lĂ©vation de privilĂšges tracĂ©e. + +4. **Gestion des identitĂ©s et prĂ©vention des attaques MITM :** + Application du principe TOFU (Trust On First Use) par la vĂ©rification manuelle de l'empreinte cryptographique (fingerprint) du serveur. Les commandes de gestion du fichier `known_hosts` ont Ă©tĂ© maĂźtrisĂ©es pour purger ou valider les identitĂ©s des hĂŽtes distants en cas de rĂ©installation, Ă©vitant ainsi les usurpations d'identitĂ©. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +* Le dĂ©mon SSH est pleinement opĂ©rationnel et Ă©coute les requĂȘtes entrantes. +* L'authentification ne s'effectue plus que par prĂ©sentation d'une clĂ© privĂ©e Ed25519 chiffrĂ©e. +* Les tentatives de connexion par mot de passe ou via le compte `root` sont rejetĂ©es par le serveur. +* Les empreintes cryptographiques sont gĂ©rĂ©es proprement, garantissant l'intĂ©gritĂ© de la connexion. +::: + +L'intervention a permis d'Ă©lever significativement le niveau de sĂ©curitĂ© du serveur. Bien que le changement de port par dĂ©faut (22) n'apporte pas de sĂ©curitĂ© cryptographique supplĂ©mentaire, il a Ă©tĂ© notĂ© comme une piste d'amĂ©lioration de "confort" pour rĂ©duire la pollution des journaux d'Ă©vĂ©nements (logs) face aux scanners automatisĂ©s. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Installation, activation et vĂ©rification du dĂ©mon `openssh-server` sur le systĂšme Debian 13 pour rendre l'infrastructure administrable Ă  distance. | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations** | CrĂ©ation d'identitĂ©s cryptographiques (clĂ©s Ed25519) et configuration du mĂ©canisme d'autorisation (`authorized_keys`) pour n'autoriser que les terminaux approuvĂ©s. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Durcissement du fichier de configuration (`sshd_config`) selon les bonnes pratiques de sĂ©curitĂ© : interdiction du login root, suppression des mots de passe et gestion des empreintes (Principe TOFU). | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/index.md b/src/content/docs/projets-bts-sio/index.md new file mode 100644 index 0000000..a77c63d --- /dev/null +++ b/src/content/docs/projets-bts-sio/index.md @@ -0,0 +1,16 @@ +--- +title: Introduction +description: PrĂ©sentation des infrastructures et solutions techniques rĂ©alisĂ©es dans le cadre du cursus acadĂ©mique et professionnel. +--- + +## 📖 Projets BTS SIO + +Cette section documente les diffĂ©rentes rĂ©alisations techniques menĂ©es lors de ma formation professionnalisante. L'objectif est de dĂ©montrer l'application des bonnes pratiques d'administration systĂšme et d'ingĂ©nierie de fiabilitĂ© (SRE) sur des cas d'usage d'entreprise. Vous y retrouverez l'analyse dĂ©taillĂ©e des cahiers des charges, les justifications des architectures dĂ©ployĂ©es via des registres de dĂ©cisions (ADR), ainsi que la modĂ©lisation des topologies rĂ©seau et des flux associĂ©s Ă  chaque environnement simulĂ© ou rĂ©el. + +## 📂 Organisation + +L'arborescence de cette documentation est structurĂ©e par projet afin d'isoler chaque contexte mĂ©tier et technique : + +* **Cadrage :** Analyse des besoins initiaux, contraintes opĂ©rationnelles et dĂ©finition des pĂ©rimĂštres d'intervention. +* **Choix d'architecture :** Registres de dĂ©cisions dĂ©taillant la rĂ©flexion technique, la sĂ©curitĂ© et les alternatives envisagĂ©es pour les couches de virtualisation et de services. +* **Conception :** SchĂ©mas logiques, modĂ©lisation des flux rĂ©seau et cartographie globale des dĂ©ploiements. \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/millenuits/assets/logo_millenuits.png b/src/content/docs/projets-bts-sio/millenuits/assets/logo_millenuits.png new file mode 100644 index 0000000..ea027a0 Binary files /dev/null and b/src/content/docs/projets-bts-sio/millenuits/assets/logo_millenuits.png differ diff --git a/src/content/docs/projets-bts-sio/millenuits/espace-developpement/assets/schema-reseau-espace-developpement.png b/src/content/docs/projets-bts-sio/millenuits/espace-developpement/assets/schema-reseau-espace-developpement.png new file mode 100644 index 0000000..8c8e2ee Binary files /dev/null and b/src/content/docs/projets-bts-sio/millenuits/espace-developpement/assets/schema-reseau-espace-developpement.png differ diff --git a/src/content/docs/projets-bts-sio/millenuits/espace-developpement/assets/schema-workflow-espace-developpement.png b/src/content/docs/projets-bts-sio/millenuits/espace-developpement/assets/schema-workflow-espace-developpement.png new file mode 100644 index 0000000..6fef9e3 Binary files /dev/null and b/src/content/docs/projets-bts-sio/millenuits/espace-developpement/assets/schema-workflow-espace-developpement.png differ diff --git a/src/content/docs/projets-bts-sio/millenuits/espace-developpement/environnement-test-conteneur-docker.mdx b/src/content/docs/projets-bts-sio/millenuits/espace-developpement/environnement-test-conteneur-docker.mdx new file mode 100644 index 0000000..60eed23 --- /dev/null +++ b/src/content/docs/projets-bts-sio/millenuits/espace-developpement/environnement-test-conteneur-docker.mdx @@ -0,0 +1,70 @@ +--- +title: "Mise en place d’un espace de dĂ©veloppement" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place d’un espace de dĂ©veloppement." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +Le besoin initial consistait Ă  fournir aux dĂ©veloppeurs un environnement de test autonome, reproductible et strictement isolĂ© du rĂ©seau de production. Historiquement confrontĂ©s aux conflits de dĂ©pendances locaux, l'objectif Ă©tait de normaliser leurs dĂ©ploiements grĂące Ă  la conteneurisation. Cette intervention vise Ă  implĂ©menter une infrastructure Docker sĂ©curisĂ©e au sein d'une DMZ, administrable via une interface web restreinte (approche Zero-Shell) et propulsĂ©e par un flux de travail GitOps continu, tout en fournissant un support de formation adaptĂ©. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **PrĂ©paration et sĂ©curisation du socle d'hĂ©bergement :** + Installation du moteur Docker sur le serveur de prĂ©-production. Une attention critique a Ă©tĂ© portĂ©e au durcissement (hardening) du dĂ©mon en dĂ©sactivant l'acquisition de nouveaux privilĂšges pour les processus conteneurisĂ©s, en configurant la rotation automatique des journaux d'Ă©vĂ©nements et en activant l'isolation des espaces de noms (mapping de l'utilisateur root vers un compte restreint). + +2. **DĂ©ploiement des services d'orchestration et de versionning :** + Instanciation des solutions Gitea (forge logicielle) et Portainer (interface d'administration Docker) via des fichiers de configuration orientĂ©s Infrastructure as Code. Les mots de passe et donnĂ©es sensibles des bases de donnĂ©es associĂ©es n'ont pas Ă©tĂ© inscrits en clair, mais stockĂ©s dans un coffre-fort d'Ă©quipe puis injectĂ©s dynamiquement sous forme de variables d'environnement. + +3. **Configuration du contrĂŽle d'accĂšs et du flux GitOps :** + Mise en Ɠuvre du principe de moindre privilĂšge via Portainer. Une Ă©quipe logique a Ă©tĂ© créée pour restreindre les dĂ©veloppeurs Ă  un environnement de dĂ©ploiement spĂ©cifique, sans aucun accĂšs d'administration global ou SSH au serveur physique. Le flux de travail a Ă©tĂ© automatisĂ© pour que les conteneurs se dĂ©ploient directement en lisant les configurations stockĂ©es sur les dĂ©pĂŽts Gitea. + +4. **Validation de l'isolation rĂ©seau (DMZ) et tests d'intĂ©gration :** + VĂ©rification de l'Ă©tanchĂ©itĂ© du rĂ©seau via l'application d'une liste de contrĂŽle d'accĂšs (ACL) stricte sur l'Ă©quipement de routage, bloquant tout trafic initiĂ© depuis le sous-rĂ©seau de test vers la production. ExĂ©cution d'un plan de recette validant le rejet des paquets ICMP non autorisĂ©s et l'accessibilitĂ© des services web dĂ©ployĂ©s. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** L'espace de dĂ©veloppement est pleinement opĂ©rationnel et rĂ©pond aux exigences de sĂ©curitĂ©. +- Les requĂȘtes rĂ©seau depuis la DMZ vers la production sont correctement bloquĂ©es par les rĂšgles de filtrage. +- Les dĂ©veloppeurs dĂ©ploient leurs architectures en autonomie via l'interface web de Portainer, sans exposition directe du systĂšme d'exploitation hĂŽte. +- Le flux de dĂ©ploiement automatisĂ© (GitOps) lit avec succĂšs les dĂ©pĂŽts Gitea pour instancier les services. +- Le module de formation pĂ©dagogique expliquant l'architecture en micro-services est finalisĂ© et prĂȘt Ă  ĂȘtre dispensĂ©. +::: + +### 🔗 Annexes + +![SchĂ©ma rĂ©seau - Espace de dĂ©veloppement](./assets/schema-reseau-espace-developpement.png) +*SchĂ©ma rĂ©seau - Espace de dĂ©veloppement* + +![SchĂ©ma - Workflow espace de dĂ©veloppement](./assets/schema-workflow-espace-developpement.png) +*SchĂ©ma - Workflow espace de dĂ©veloppement* + +Le workflow se dĂ©roule en cinq Ă©tapes, entiĂšrement au sein du serveur MN21 isolĂ© en DMZ : + +1. **Le dĂ©veloppeur pousse son code** depuis son poste vers le dĂ©pĂŽt Git distant hĂ©bergĂ© sur **Gitea** (conteneur Docker sur MN21). Gitea joue le rĂŽle de source de vĂ©ritĂ© pour le code source et les fichiers de configuration d'infrastructure (`docker-compose.yml`). + +2. **Le dĂ©veloppeur se connecte Ă  Portainer** via son navigateur web sur l'interface HTTPS (port 9443). Son accĂšs est restreint Ă  l'environnement de dĂ©veloppement qui lui a Ă©tĂ© dĂ©lĂ©guĂ© — il ne peut pas accĂ©der aux paramĂštres globaux du serveur. + +3. **Le dĂ©veloppeur crĂ©e une Stack dans Portainer** en pointant vers son dĂ©pĂŽt Gitea. Portainer utilise la mĂ©thode "Repository" : il lit directement le `docker-compose.yml` stockĂ© dans le dĂ©pĂŽt Git, ce qui garantit que le dĂ©ploiement est toujours synchronisĂ© avec le code versionnĂ©. + +4. **Portainer rĂ©cupĂšre la configuration** en allant lire le fichier `docker-compose.yml` depuis le dĂ©pĂŽt Gitea. Cette Ă©tape constitue le cƓur de l'approche **GitOps** : l'infrastructure est dĂ©crite sous forme de code, versionnable et auditable. + +5. **Portainer instancie les conteneurs** de l'application Ă  tester (service web, base de donnĂ©es, etc.) sur le moteur Docker de MN21. Le conteneur applicatif est alors accessible depuis le sous-rĂ©seau dĂ©veloppeurs. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations** | Configuration du contrĂŽle d'accĂšs (RBAC) sur Portainer pour dĂ©lĂ©guer la gestion d'un environnement restreint aux dĂ©veloppeurs. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Durcissement sĂ©curitaire du moteur Docker en appliquant l'isolation des privilĂšges (User Namespace Remapping). | +| **1.4 Travailler en mode projet** | **Analyser les objectifs** | RĂ©daction d'un document de dĂ©cision d'architecture (ADR) justifiant le choix de l'interface graphique face aux contraintes Zero-Shell. | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Instanciation des plateformes d'infrastructure (Gitea, base de donnĂ©es PostgreSQL, Portainer) via des manifestes d'orchestration. | +| **1.5 Mettre Ă  disposition un service** | **Accompagner les utilisateurs** | CrĂ©ation d'un support pĂ©dagogique expliquant les enjeux de la conteneurisation pour faciliter l'adoption de l'outil par les Ă©quipes. | \ No newline at end of file diff --git "a/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/borne-wifi.mdx" "b/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/borne-wifi.mdx" new file mode 100644 index 0000000..44c5b26 --- /dev/null +++ "b/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/borne-wifi.mdx" @@ -0,0 +1,46 @@ +--- +title: "Mise en place de l'AP" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place de l'AP et segmentation du rĂ©seau Wi-Fi." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention consiste Ă  intĂ©grer une borne d'accĂšs Wi-Fi (AP) au sein de l'infrastructure rĂ©seau de l'environnement "MILLENUITS". Le besoin technique principal est d'assurer une sĂ©grĂ©gation stricte des flux de donnĂ©es entre un rĂ©seau "Commercial" et un rĂ©seau "InvitĂ©". Cela implique la mise en place d'un routage inter-VLAN sur les Ă©quipements de cƓur de rĂ©seau (routeur et commutateur), suivie du provisionnement de la borne avec une adresse de management statique et du dĂ©ploiement sĂ©curisĂ© du SSID invitĂ©. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **PrĂ©paration de l'infrastructure de routage et commutation :** + La premiĂšre Ă©tape a consistĂ© Ă  prĂ©parer la couche rĂ©seau. Une sous-interface logique a Ă©tĂ© créée sur le routeur avec une encapsulation 802.1Q pour gĂ©rer le trafic du VLAN 15 (InvitĂ©), en lui attribuant l'adresse IP de passerelle et en configurant le NAT. CĂŽtĂ© commutateur, les VLANs ont Ă©tĂ© dĂ©clarĂ©s dans la base de donnĂ©es, le port de liaison montante a Ă©tĂ© configurĂ© en mode Trunk pour autoriser le trafic tagguĂ©, et les ports d'accĂšs dĂ©diĂ©s Ă  la borne et aux autres Ă©quipements ont Ă©tĂ© affectĂ©s statiquement aux bons VLANs. + +2. **AccĂšs initial et adressage de management de la borne :** + La borne Ă©tant dans sa configuration d'usine, un poste d'administration a Ă©tĂ© temporairement configurĂ© avec un adressage IP statique dans le mĂȘme sous-rĂ©seau (192.168.0.0/24) pour accĂ©der Ă  son interface web. L'Ă©quipement a ensuite Ă©tĂ© intĂ©grĂ© au plan d'adressage de l'infrastructure en lui attribuant une adresse IP de management statique (172.40.2.129) associĂ©e Ă  un masque /28, et en renseignant la passerelle configurĂ©e prĂ©cĂ©demment sur le routeur. + +3. **DĂ©ploiement du rĂ©seau sans-fil (SSID) et tests de validation :** + Le mode Point d'AccĂšs a Ă©tĂ© activĂ© pour diffuser le rĂ©seau sans-fil nommĂ© "INVITE Louis". Ce rĂ©seau a Ă©tĂ© sĂ©curisĂ© via le protocole d'authentification WPA-Personal avec une clĂ© prĂ©-partagĂ©e. AprĂšs l'application de la configuration et le redĂ©marrage de l'Ă©quipement, la connectivitĂ© a Ă©tĂ© validĂ©e par un test de joignabilitĂ© ICMP (ping) depuis le rĂ©seau local et par la vĂ©rification de la diffusion effective du SSID sur un terminal mobile. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** L'infrastructure rĂ©seau et la borne Wi-Fi sont totalement opĂ©rationnelles. +* Le routage inter-VLAN et la segmentation (VLAN 11 et VLAN 15) sont fonctionnels. +* L'interface de management de la borne est accessible via son adresse IP statique. +* Le SSID invitĂ© est correctement diffusĂ©, sĂ©curisĂ© et prĂȘt Ă  accueillir les connexions des utilisateurs. +::: + +L'utilisation d'un masque de sous-rĂ©seau restrictif (/28, limitant Ă  14 hĂŽtes) sur le segment de management garantit une optimisation de l'espace d'adressage et rĂ©duit la surface d'exposition du sous-rĂ©seau d'infrastructure, respectant ainsi les bonnes pratiques de sĂ©curitĂ© (DevSecOps). Il sera impĂ©ratif de modifier les identifiants par dĂ©faut de la borne lors d'une prochaine phase de durcissement. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations / VĂ©rifier les rĂšgles d'utilisation** | ImplĂ©mentation d'une segmentation logique stricte via les VLANs pour isoler le trafic invitĂ© du rĂ©seau commercial, et sĂ©curisation de l'accĂšs sans-fil via le protocole WPA-Personal. | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Configuration matĂ©rielle et logicielle des interfaces rĂ©seaux (routeur Cisco, switch) et provisionnement complet de la borne Wi-Fi autonome pour diffuser le nouveau SSID. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | Validation de la continuitĂ© de service post-dĂ©ploiement via des requĂȘtes ICMP sur la nouvelle passerelle de management et vĂ©rification visuelle de l'association au rĂ©seau Wi-Fi diffusĂ©. | \ No newline at end of file diff --git "a/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/infrastructure-reseau.mdx" "b/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/infrastructure-reseau.mdx" new file mode 100644 index 0000000..e5b3978 --- /dev/null +++ "b/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/infrastructure-reseau.mdx" @@ -0,0 +1,55 @@ +--- +title: "Mise en place de l'infrastructure rĂ©seau" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place de l'infrastructure rĂ©seau." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention est de concevoir, dĂ©ployer et sĂ©curiser l'infrastructure rĂ©seau de l'environnement MilleNuits. Le besoin initial imposait la segmentation logique des diffĂ©rents services (VLANs), la garantie d'un routage fluide entre ces rĂ©seaux (Router-on-a-stick), ainsi que l'accĂšs sĂ©curisĂ© vers l'extĂ©rieur (NAT). En complĂ©ment, la mission intĂšgre la sĂ©curisation des accĂšs administratifs (SSH, VLAN Management) et la mise en Ɠuvre d'une politique de sauvegarde centralisĂ©e et versionnĂ©e des configurations matĂ©rielles. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **Conception et prototypage du cƓur de rĂ©seau :** + L'adressage IP a Ă©tĂ© structurĂ© selon un plan de nommage strict. L'isolation des flux a Ă©tĂ© rĂ©alisĂ©e en crĂ©ant des VLANs dĂ©diĂ©s par service (Production, Administratif, Logistique, etc.) sur les commutateurs d'accĂšs et le commutateur cƓur de rĂ©seau. Les liaisons entre ces Ă©quipements ont Ă©tĂ© configurĂ©es en mode Trunk (802.1Q) pour transporter l'ensemble des rĂ©seaux virtuels. + +2. **Configuration du routage et de l'accĂšs WAN :** + Le routeur principal a Ă©tĂ© paramĂ©trĂ© avec des sous-interfaces logiques servant de passerelles par dĂ©faut pour chaque VLAN. Pour permettre l'accĂšs Ă  Internet, le protocole NAT (Network Address Translation) en mode surcharge (PAT) a Ă©tĂ© configurĂ© sur les routeurs, accompagnĂ© de listes de contrĂŽle d'accĂšs (ACL) dĂ©finissant les rĂ©seaux autorisĂ©s Ă  communiquer vers l'extĂ©rieur. + +3. **SĂ©curisation des accĂšs administratifs :** + Les protocoles non chiffrĂ©s (Telnet) ont Ă©tĂ© bloquĂ©s au profit du protocole SSH. Cette Ă©tape a impliquĂ© la crĂ©ation d'un domaine local, la gĂ©nĂ©ration de clĂ©s asymĂ©triques RSA (2048 bits) et la dĂ©finition de comptes administrateurs locaux fortement chiffrĂ©s. L'administration a Ă©tĂ© isolĂ©e dans un rĂ©seau dĂ©diĂ© (VLAN 99 Management). + +4. **Mise en place de la stratĂ©gie de sauvegarde :** + Une centralisation des configurations a Ă©tĂ© dĂ©ployĂ©e via un serveur TFTP local. Les configurations actives (`running-config`) des routeurs et les bases de donnĂ©es VLAN (`vlan.dat`) des commutateurs ont Ă©tĂ© extraites, standardisĂ©es par renommage, puis archivĂ©es et versionnĂ©es sur le dĂ©pĂŽt Git de l'infrastructure pour assurer la traçabilitĂ©. + +5. **Tests d'intĂ©gration et recette :** + Une validation rigoureuse a Ă©tĂ© menĂ©e selon un cahier de recette technique : vĂ©rification physique des liens, tests de connectivitĂ© ICMP sur les passerelles locales, validation des couches rĂ©seau et applicative vers l'extĂ©rieur (Ping et requĂȘtes HTTP via cURL), et contrĂŽle des tables de translation NAT sur le routeur. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs : L'infrastructure est pleinement opĂ©rationnelle, sĂ©curisĂ©e et sauvegardĂ©e.** +- Les flux des diffĂ©rents services sont correctement isolĂ©s par VLANs. +- Le routage inter-VLAN et l'accĂšs Ă  Internet via la translation d'adresses (NAT) sont fonctionnels. +- Les accĂšs aux Ă©quipements sont chiffrĂ©s (SSH) et restreints. +- L'historique des configurations est assurĂ© par le couplage TFTP/Git. +::: + +L'utilisation du protocole TFTP pour les sauvegardes impliquant un transfert en clair, le processus a Ă©tĂ© strictement cantonnĂ© au VLAN de management localement pour annuler tout risque d'interception sur le rĂ©seau public. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | ImplĂ©mentation du routage inter-VLAN, configuration des commutateurs (Access/Trunk) et paramĂ©trage de la translation d'adresses (NAT) sur les routeurs Cisco. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | ExĂ©cution du plan de recette rĂ©seau : vĂ©rification des liens physiques, tests ICMP locaux/distants et requĂȘtes cURL pour valider l'accĂšs Internet L7. | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations** | Isolement des flux d'administration (VLAN 99) et sĂ©curisation des accĂšs CLI via le dĂ©ploiement du protocole SSH (clĂ©s RSA 2048 bits, comptes locaux). | +| **1.1 GĂ©rer le patrimoine informatique** | **GĂ©rer des sauvegardes** | Extraction des configurations (`running-config` et `vlan.dat`) via TFTP et mise en place d'un versioning continu sur le dĂ©pĂŽt Git du projet. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Application stricte du plan d'adressage IP et de la convention de nommage dĂ©finie pour l'entreprise MilleNuits lors de l'archivage. | \ No newline at end of file diff --git "a/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/plan-adressage.mdx" "b/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/plan-adressage.mdx" new file mode 100644 index 0000000..14184c7 --- /dev/null +++ "b/src/content/docs/projets-bts-sio/millenuits/gestion-infrastructure-r\303\251seau/plan-adressage.mdx" @@ -0,0 +1,43 @@ +--- +title: "CrĂ©ation du plan d'adressage" +description: "Compte-rendu d'intervention et rapport technique sur : CrĂ©ation du plan d'adressage." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention pour l'infrastructure de l'entreprise MILLENUITS Ă©tait de concevoir un plan d'adressage IPv4 structurĂ©, Ă©volutif et sĂ©curisĂ©. Avant le dĂ©ploiement physique ou logique des Ă©quipements, il Ă©tait impĂ©ratif de cartographier les besoins capacitaires de chaque service (Production, Administratif, Logistique, etc.) en anticipant la croissance du parc informatique. Le rĂ©sultat attendu est un document de rĂ©fĂ©rence dĂ©finissant le dĂ©coupage rĂ©seau par VLAN, optimisant l'espace d'adressage et posant les bases du futur routage inter-VLAN. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **Recueil et analyse des besoins capacitaires :** + L'intervention a dĂ©butĂ© par un audit des besoins en hĂŽtes pour chaque dĂ©partement de l'entreprise. Afin de garantir la pĂ©rennitĂ© de l'infrastructure et d'Ă©viter un redĂ©coupage Ă  court terme, une marge de sĂ©curitĂ© de 20 % a Ă©tĂ© appliquĂ©e systĂ©matiquement sur le nombre d'hĂŽtes requis par zone (ex: 110 hĂŽtes rĂ©els en Production provisionnĂ©s pour 132). + +2. **DĂ©coupage de l'espace d'adressage (mĂ©thodologie VLSM) :** + Pour optimiser l'utilisation des adresses IP et limiter les domaines de diffusion (broadcast), une logique de sous-rĂ©seaux Ă  taille variable (VLSM) a Ă©tĂ© appliquĂ©e sur la plage rĂ©seau de base (172.40.x.x). Les masques CIDR ont Ă©tĂ© ajustĂ©s dynamiquement du `/24` au `/28` en fonction de la taille requise par chaque segment, allant des rĂ©seaux denses (Production) aux rĂ©seaux restreints (Management, InvitĂ©). + +3. **Attribution logique des VLANs et convention de nommage :** + Chaque sous-rĂ©seau calculĂ© a Ă©tĂ© strictement isolĂ© dans un VLAN dĂ©diĂ© (VLAN 10 Ă  99). Une rĂšgle d'architecture standardisĂ©e a Ă©tĂ© dĂ©finie et documentĂ©e : la passerelle par dĂ©faut (Gateway) de chaque segment correspond systĂ©matiquement Ă  la derniĂšre adresse IP disponible du sous-rĂ©seau, facilitant ainsi la lisibilitĂ© et la maintenance future par les Ă©quipes d'administration. + + +## ✅ Bilan et RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** Le plan d'adressage est totalement finalisĂ©, documentĂ© et valide. L'espace IP est optimisĂ© sans gaspillage, et la segmentation par VLAN garantit une isolation logique prĂȘte Ă  ĂȘtre implĂ©mentĂ©e sur les Ă©quipements de niveau 2 et 3 (Switchs/Routeurs/Firewalls). +::: + +La principale exigence technique reposait sur la rigueur du calcul binaire pour Ă©viter tout chevauchement (overlap) entre les sous-rĂ©seaux, tout en respectant l'intĂ©gration des marges de croissance. L'utilisation d'une mĂ©thodologie mathĂ©matique stricte a permis de valider techniquement chaque plage allouĂ©e. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.1 GĂ©rer le patrimoine informatique** | **Recenser et identifier les ressources** | Audit des besoins matĂ©riels par dĂ©partement pour MILLENUITS et calcul du nombre d'hĂŽtes nĂ©cessaires avec intĂ©gration d'une marge d'Ă©volution de 20 %. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Application des standards d'adressage IPv4 et de la mĂ©thode de calcul VLSM pour dĂ©finir les masques CIDR, les adresses de diffusion et segmenter logiquement le rĂ©seau via des VLANs. | +| **1.4 Travailler en mode projet** | **Analyser les objectifs** | Traduction des besoins organisationnels de l'entreprise en contraintes techniques afin de produire une architecture rĂ©seau robuste et Ă©volutive. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/millenuits/gestion-parc-informatique/documentation-mkdocs-github.mdx b/src/content/docs/projets-bts-sio/millenuits/gestion-parc-informatique/documentation-mkdocs-github.mdx new file mode 100644 index 0000000..372d5ef --- /dev/null +++ b/src/content/docs/projets-bts-sio/millenuits/gestion-parc-informatique/documentation-mkdocs-github.mdx @@ -0,0 +1,47 @@ +--- +title: "CrĂ©ation d'une documentation avec MkDocs et GitHub Pages" +description: "Compte-rendu d'intervention et rapport technique sur : CrĂ©ation d'une documentation avec MkDocs et GitHub Pages." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention est de concevoir et de dĂ©ployer une plateforme de documentation technique centralisĂ©e pour le projet Millenuits. Le besoin initial rĂ©sidait dans la nĂ©cessitĂ© de structurer les procĂ©dures rĂ©seau et systĂšme, tout en automatisant la publication pour garantir une information toujours Ă  jour. La solution retenue s'appuie sur le gĂ©nĂ©rateur de site statique MkDocs (via le thĂšme Material) et la mise en place d'une chaĂźne d'intĂ©gration et de dĂ©ploiement continus (CI/CD) automatisĂ©e avec GitHub Actions vers l'hĂ©bergement GitHub Pages. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **Initialisation de l'environnement de dĂ©veloppement MkDocs :** + L'environnement local a Ă©tĂ© prĂ©parĂ© en installant les dĂ©pendances Python requises, notamment le framework MkDocs et son thĂšme visuel Material. L'architecture du projet de documentation a ensuite Ă©tĂ© gĂ©nĂ©rĂ©e et paramĂ©trĂ©e via le fichier `mkdocs.yaml` pour dĂ©finir l'identitĂ© du site, la langue de l'interface et l'activation du moteur de recherche interne. + +2. **ImplĂ©mentation du pipeline d'automatisation (CI/CD) :** + Un workflow GitHub Actions a Ă©tĂ© dĂ©clarĂ© Ă  la racine du dĂ©pĂŽt (`publish.yaml`). Ce pipeline a Ă©tĂ© configurĂ© pour se dĂ©clencher automatiquement lors de chaque `push` sur la branche principale. Il provisionne un conteneur d'exĂ©cution Linux, installe les prĂ©requis Python, compile les sources Markdown en HTML statique, puis force le dĂ©ploiement sur la branche de production (`gh-pages`). + +3. **Tests d'intĂ©gration et recette fonctionnelle :** + Le dĂ©ploiement a fait l'objet d'une validation en trois Ă©tapes. D'abord, un serveur de dĂ©veloppement local a confirmĂ© la bonne gĂ©nĂ©ration du site. Ensuite, l'exĂ©cution fluide du pipeline d'intĂ©gration a Ă©tĂ© contrĂŽlĂ©e sur l'interface GitHub. Enfin, une recette fonctionnelle en production a permis de valider l'accessibilitĂ© HTTP (Code 200), le routage des images, l'efficacitĂ© du moteur de recherche et le comportement responsive de l'interface web. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +- La documentation technique est opĂ©rationnelle et accessible publiquement via l'URL GitHub Pages. +- La publication est totalement automatisĂ©e : la mise Ă  jour des fichiers sources dĂ©clenche un dĂ©ploiement sans intervention manuelle. +- L'expĂ©rience utilisateur est validĂ©e, incluant une navigation fluide, une recherche pertinente et un affichage adaptĂ© aux terminaux mobiles. +- L'arborescence du dĂ©pĂŽt sĂ©pare logiquement la configuration d'infrastructure, les scripts d'automatisation et les sources Markdown. +::: + +Une vigilance particuliĂšre a Ă©tĂ© requise lors de la dĂ©claration des chemins de ressources statiques (images) afin d'assurer leur rendu simultanĂ© sur le dĂ©pĂŽt Git brut et sur la version web compilĂ©e. Par ailleurs, la stricte indentation exigĂ©e par le format YAML a nĂ©cessitĂ© une rigueur d'Ă©criture lors de la conception du workflow CI/CD. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | CrĂ©ation et paramĂ©trage d'un pipeline CI/CD (GitHub Actions) pour automatiser la compilation et le dĂ©ploiement continu du site statique MkDocs vers GitHub Pages. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | Conception et exĂ©cution d'une fiche recette en trois phases (locale, pipeline, production) pour certifier l'accessibilitĂ© et les fonctionnalitĂ©s du site (recherche, responsivitĂ©, intĂ©gritĂ© des mĂ©dias). | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Structuration des procĂ©dures techniques au standard Markdown et utilisation rigoureuse du format YAML pour la configuration du gĂ©nĂ©rateur de site et du script d'automatisation. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/millenuits/gestion-parc-informatique/solution-ticketing.mdx b/src/content/docs/projets-bts-sio/millenuits/gestion-parc-informatique/solution-ticketing.mdx new file mode 100644 index 0000000..4b8e8b7 --- /dev/null +++ b/src/content/docs/projets-bts-sio/millenuits/gestion-parc-informatique/solution-ticketing.mdx @@ -0,0 +1,56 @@ +--- +title: "Mise en place d'une solution de ticketing" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place d'une solution de ticketing." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +L'objectif de cette intervention Ă©tait de doter l'entreprise Millenuits d'une solution centralisĂ©e de gestion des services informatiques (ITSM) et de support utilisateur (Helpdesk). Face Ă  la nĂ©cessitĂ© de structurer les demandes des 167 collaborateurs rĂ©partis sur deux sites gĂ©ographiques distincts, il fallait dĂ©ployer un outil permettant la dĂ©claration d'incidents, le suivi par une Ă©quipe de trois techniciens, et l'Ă©dition de statistiques pour la Direction des SystĂšmes d'Information (DSI). L'outil devait ĂȘtre gratuit, hĂ©bergeable sur une infrastructure Linux existante et intĂ©grer une base de connaissances. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **Étude des besoins et choix technologique :** + Analyse du cahier des charges et comparaison des solutions du marchĂ© (GLPI, osTicket, Zammad, Hesk). GLPI a Ă©tĂ© retenu pour son adĂ©quation parfaite avec les prĂ©requis : gratuitĂ©, respect des bonnes pratiques ITIL, richesse du module de ticketing et intĂ©gration d'une base de connaissances. + +2. **PrĂ©paration du socle d'hĂ©bergement (LAMP) :** + Mise en place de l'environnement serveur sur une distribution Debian. Cette Ă©tape a consistĂ© Ă  installer et durcir un serveur web Apache2 couplĂ© Ă  PHP 8.4-FPM pour des performances optimales. Une base de donnĂ©es MariaDB a Ă©tĂ© dĂ©ployĂ©e, sĂ©curisĂ©e (dĂ©sactivation des accĂšs distants root, suppression des bases de test) et dotĂ©e d'un utilisateur restreint dĂ©diĂ© Ă  l'application. + +3. **DĂ©ploiement et sĂ©curisation de GLPI :** + TĂ©lĂ©chargement et extraction de l'archive officielle de GLPI dans le rĂ©pertoire web. Une attention particuliĂšre a Ă©tĂ© portĂ©e Ă  la sĂ©curitĂ© : ajustement des droits systĂšme (`www-data`), dĂ©placement des rĂ©pertoires sensibles (`config`, `files`, `log`) en dehors de la racine publique d'Apache, et configuration du VirtualHost avec réécriture d'URL. + +4. **ParamĂ©trage mĂ©tier et logique d'entreprise :** + Post-installation, l'environnement a Ă©tĂ© cloisonnĂ©. L'arborescence gĂ©ographique a Ă©tĂ© recréée (Sites de BaugĂ© et JouĂ©-LĂšs-Tours), et les habilitations ont Ă©tĂ© structurĂ©es via des profils stricts : *Self-Service* (utilisateurs), *Technician* (support) et *Super-Admin* (DSI). Les catĂ©gories d'incidents ITIL et les niveaux de service (SLA) ont Ă©galement Ă©tĂ© dĂ©finis. + +5. **Phase de recette et validation :** + ExĂ©cution d'un plan de tests complet pour valider chaque parcours utilisateur. Les tests ont inclus la vĂ©rification des blocages de sĂ©curitĂ© (comptes par dĂ©faut dĂ©sactivĂ©s), la crĂ©ation d'un ticket par un utilisateur standard, sa prise en charge et sa rĂ©solution par un technicien, ainsi que la gĂ©nĂ©ration de rapports statistiques par l'administrateur. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** La plateforme de ticketing GLPI est aujourd'hui pleinement opĂ©rationnelle et sĂ©curisĂ©e. +* Les collaborateurs disposent d'un portail intuitif pour dĂ©clarer leurs incidents. +* Les techniciens peuvent s'attribuer, documenter et rĂ©soudre les tickets efficacement. +* La base de connaissances est active pour capitaliser sur les rĂ©solutions. +* La DSI dispose de ses tableaux de bord pour le suivi des performances (SLA). +::: + +La principale complexitĂ© a rĂ©sidĂ© dans le durcissement post-installation (dĂ©placement des dossiers racines et modification des directives PHP `cookie_httponly`), qui a nĂ©cessitĂ© la crĂ©ation de fichiers de liens symboliques internes (`downstream.php`) pour maintenir l'intĂ©gritĂ© de l'application tout en garantissant un haut niveau de sĂ©curitĂ© web. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.4 Travailler en mode projet** | **Analyser les objectifs** | Étude comparative des solutions du marchĂ© (GLPI vs Zammad/osTicket) pour rĂ©pondre prĂ©cisĂ©ment au cahier des charges de la DSI de Millenuits. | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Installation, configuration du socle serveur LAMP (Apache, PHP-FPM, MariaDB) et dĂ©ploiement de l'applicatif GLPI. | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations** | CrĂ©ation des entitĂ©s gĂ©ographiques, gestion des rĂŽles (Self-Service, Technicien, Admin) et application du principe de moindre privilĂšge. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | ImplĂ©mentation des catĂ©gories de tickets, des rĂšgles d'attribution et des SLA selon les bonnes pratiques du rĂ©fĂ©rentiel ITIL. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | ExĂ©cution d'une fiche de recette stricte (tests de vulnĂ©rabilitĂ© basiques, simulation de bout en bout d'un ticket utilisateur vers technicien). | +| **1.5 Mettre Ă  disposition un service** | **Accompagner les utilisateurs** | RĂ©daction des guides d'utilisation contextualisĂ©s (Profil Utilisateur, Helpdesk, Administrateur) pour faciliter la prise en main de la plateforme. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/millenuits/index.md b/src/content/docs/projets-bts-sio/millenuits/index.md new file mode 100644 index 0000000..e2712cc --- /dev/null +++ b/src/content/docs/projets-bts-sio/millenuits/index.md @@ -0,0 +1,60 @@ +--- +title: "Contexte" +description: "PrĂ©sentation globale du contexte, de l'environnement et des compĂ©tences mises en Ɠuvre pour Millenuits." +--- + +## Contexte Millenuits + +![Logo Millenuits](./assets/logo_millenuits.png) + +--- + +:::note[Informations rapides] +* **Nom de l'entitĂ© :** Millenuits +* **Lieu :** BaugĂ©-en-Anjou (SiĂšge historique) & JouĂ©-LĂšs-Tours (Site logistique) +* **Taille de l'infrastructure :** PME industrielle de 167 employĂ©s rĂ©partis sur deux sites distants. +* **Mon rĂŽle :** Administrateur SystĂšme et RĂ©seau, profil DevSecOps +* **PĂ©rimĂštre d'action :** Audit de l'existant, refonte de l'architecture rĂ©seau (segmentation), sĂ©curisation du systĂšme d'information et modernisation de la gestion du parc. +::: + +## 📋 PrĂ©sentation de la situation + +Millenuits est une PME française florissante, leader sur le marchĂ© de la production de couettes et d'oreillers avec plus de 35 000 piĂšces produites par jour. L'entreprise est divisĂ©e en deux entitĂ©s gĂ©ographiques : un site historique Ă  BaugĂ©-en-Anjou qui regroupe l'administration, la direction et la production, et un site logistique distant Ă  JouĂ©-LĂšs-Tours pour la gestion des stocks et expĂ©ditions. Le systĂšme d'information soutient l'activitĂ© de 167 collaborateurs et repose sur une petite Ă©quipe informatique interne. + +Le dĂ©fi principal de ce contexte rĂ©side dans la vĂ©tustĂ© technique et organisationnelle de l'infrastructure de dĂ©part. Le rĂ©seau est un grand rĂ©seau plat (sans segmentation), la gestion de parc se fait via un simple tableur, et la sĂ©curitĂ© des postes de travail repose sur des antivirus isolĂ©s. L'objectif est d'apporter une vision d'ingĂ©nierie moderne et DevSecOps : cartographier, segmenter, sĂ©curiser les accĂšs (notamment pour les postes en libre-service de la production) et fiabiliser la liaison inter-sites, tout en garantissant une haute disponibilitĂ© pour le PGI (Open ERP) qui est le cƓur de l'entreprise. + +## 🎯 CompĂ©tences travaillĂ©es (Épreuve E4) + +Dans le cadre de ce contexte, plusieurs compĂ©tences et sous-compĂ©tences du rĂ©fĂ©rentiel officiel du BTS SIO ont Ă©tĂ© mobilisĂ©es et justifiĂ©es : + +| CompĂ©tence globale | Sous-compĂ©tence mobilisĂ©e | Justification et trace concrĂšte | +| --- | --- | --- | +| **1.1 GĂ©rer le patrimoine informatique** | Recenser et identifier les ressources numĂ©riques | Audit complet de l'infrastructure pour remplacer la gestion obsolĂšte par tableur et cartographier les Ă©quipements rĂ©seau rĂ©partis sur les deux sites gĂ©ographiques. | +| **1.1 GĂ©rer le patrimoine informatique** | Mettre en place et vĂ©rifier les niveaux d’habilitation | Restructuration et durcissement des droits via l'Active Directory pour sĂ©curiser de maniĂšre granulaire l'accĂšs aux postes de production en libre-service et au PGI. | +| **1.4 Travailler en mode projet** | Analyser les objectifs et les modalitĂ©s | Étude des besoins de refonte de l'architecture (fin du rĂ©seau plat) et dĂ©finition des Ă©tapes pour intĂ©grer des pratiques d'administration modernes sans interrompre la chaĂźne de production. | + +## đŸ—ïž Environnement technique + +L'Ă©cosystĂšme technologique de dĂ©part repose sur une architecture Windows Server traditionnelle, centralisĂ©e dans une salle climatisĂ©e sur le site de BaugĂ©-en-Anjou. L'enjeu est de transformer cette base classique en un environnement robuste, segmentĂ© et prĂȘt Ă  accueillir des processus d'administration automatisĂ©s. + +### Topologie rĂ©seau + +À l'origine, l'entreprise fonctionne sur un rĂ©seau local unique et non segmentĂ© (adressage en `192.168.110.0/24`), mĂ©langeant tous les services (RH, compta, vente, serveurs). L'accĂšs Ă  Internet est fourni via une box ADSL classique avec une adresse IP publique fixe (`45.17.25.3`). Le site logistique distant y est rattachĂ© au moyen d'une liaison privĂ©e dĂ©diĂ©e. Seul le rĂ©seau industriel des automates est physiquement isolĂ©. + +### Services existants + +* **Serveur MN01 (Windows Server) :** CƓur de l'infrastructure rĂ©seau. Il hĂ©berge les rĂŽles critiques AD (Active Directory), DNS, DHCP et centralise le partage de fichiers communs. +* **Serveur MN02 :** Serveur dĂ©diĂ© Ă  la messagerie d'entreprise (format prenom.nom@millenuits.com). +* **Serveur MN03 (Open ERP) :** Serveur hĂ©bergeant le Progiciel de Gestion IntĂ©grĂ©, vital pour la comptabilitĂ©, les achats, les ventes et la gestion en temps rĂ©el des stocks entre les deux sites. + +## 📄 Documentation + +Dans les sous-sections de ce contexte, vous retrouverez des articles thĂ©matiques liĂ©s Ă  ce projet (Virtualisation, Conteneurisation, RĂ©seau, etc.). + +:::note[PrĂ©cision importante] +La documentation prĂ©sente sur ce portfolio a pour unique but de prĂ©senter l'architecture et la logique derriĂšre mes choix techniques. Vous n'y retrouverez pas de documentation d'exploitation pure. + +Pour consulter les procĂ©dures techniques dĂ©taillĂ©es et les documentations d'installation de ce projet, je vous invite Ă  consulter ce lien : + +👉 **[Documentation technique - Millenuits](https://ap-bts-sio-louis.github.io/millenuits/)** +::: \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/millenuits/services-ad-dhcp/active-directory-dns-windows-server.mdx b/src/content/docs/projets-bts-sio/millenuits/services-ad-dhcp/active-directory-dns-windows-server.mdx new file mode 100644 index 0000000..a8bc33e --- /dev/null +++ b/src/content/docs/projets-bts-sio/millenuits/services-ad-dhcp/active-directory-dns-windows-server.mdx @@ -0,0 +1,51 @@ +--- +title: "Mise en place de l'active directory et du DNS sur Windows Server" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place de l'active directory et du DHCP sur Windows Server." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +Dans le cadre du projet d'infrastructure MILLENUITS, l'objectif de cette intervention Ă©tait de dĂ©ployer et de sĂ©curiser les services d'annuaire et de rĂ©solution de noms, cƓurs stratĂ©giques du systĂšme d'information. Le besoin initial consistait Ă  provisionner un nouveau contrĂŽleur de domaine (Windows Server 2019) capable de centraliser les identitĂ©s, de gĂ©rer les habilitations rĂ©seau et de structurer logiquement les ressources de l'entreprise. Le rĂ©sultat attendu est un environnement Active Directory (AD DS) et DNS pleinement fonctionnel, hautement sĂ©curisĂ©, et prĂȘt Ă  accueillir les postes clients ainsi que les stratĂ©gies de groupe (GPO) des diffĂ©rents services. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **Provisionnement et configuration systĂšme (Nutanix) :** + CrĂ©ation de la machine virtuelle sur le cluster Nutanix avec intĂ©gration des pilotes matĂ©riels spĂ©cifiques (VirtIO) pour garantir la reconnaissance des disques et des interfaces. Le systĂšme a Ă©tĂ© configurĂ© avec un adressage IP statique sur le VLAN dĂ©diĂ©, suivi de la sĂ©curisation du compte Administrateur local (gĂ©nĂ©ration d'un mot de passe fort via un coffre-fort) et de l'activation du bureau Ă  distance (RDP) pour l'administration continue. + +2. **DĂ©ploiement et sĂ©curisation des services AD DS et DNS :** + AprĂšs avoir standardisĂ© le nom d'hĂŽte du serveur (MN01), les binaires des services de domaine Active Directory ont Ă©tĂ© installĂ©s. Le serveur a ensuite Ă©tĂ© promu en tant que premier contrĂŽleur de domaine de la forĂȘt. Le service DNS, installĂ© conjointement, a fait l'objet d'un durcissement de sĂ©curitĂ© : la rĂ©cursivitĂ© a Ă©tĂ© totalement dĂ©sactivĂ©e via PowerShell afin de rĂ©duire la surface d'attaque et d'empĂȘcher les tentatives d'empoisonnement de cache ou d'amplification DNS. + +3. **Structuration logique et crĂ©ation des habilitations :** + Afin de prĂ©parer l'application du principe de moindre privilĂšge, l'annuaire a Ă©tĂ© organisĂ© en UnitĂ©s d'Organisation (UO) correspondant aux dĂ©partements de l'entreprise, avec une protection active contre la suppression accidentelle. Des comptes utilisateurs standardisĂ©s (convention de nommage stricte) ont Ă©tĂ© créés, intĂ©grant une politique de sĂ©curitĂ© forçant le renouvellement du mot de passe dĂšs la premiĂšre connexion. + +4. **Recette technique et tests d'intĂ©gration :** + Une validation exhaustive de l'infrastructure a Ă©tĂ© menĂ©e. Les flux rĂ©seaux ont Ă©tĂ© testĂ©s (routage local, NAT, accĂšs WAN). La santĂ© de l'Active Directory a Ă©tĂ© auditĂ©e via des diagnostics natifs vĂ©rifiant l'intĂ©gritĂ© de la base NTDS et la connectivitĂ©. Enfin, la rĂ©solution DNS interne et le processus d'intĂ©gration d'un poste client au domaine ont Ă©tĂ© validĂ©s avec succĂšs. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** L'infrastructure d'identitĂ© et de rĂ©solution est opĂ©rationnelle et validĂ©e en production. +* La VM Windows Server est stable sur l'hyperviseur Nutanix avec une connectivitĂ© rĂ©seau conforme. +* L'annuaire Active Directory est dĂ©ployĂ©, structurĂ© par services (UOs), et accepte les connexions des utilisateurs avec des habilitations sĂ©curisĂ©es. +* Le service DNS fait autoritĂ© sur la zone locale et sa rĂ©cursivitĂ© est verrouillĂ©e contre les menaces externes. +* Les tests de diagnostic du contrĂŽleur de domaine n'affichent aucune erreur critique. +::: + +Sur le plan technique, l'avertissement classique de dĂ©lĂ©gation DNS lors de la promotion du domaine racine a Ă©tĂ© correctement identifiĂ© comme un comportement normal et ignorĂ© en toute sĂ©curitĂ©. De plus, l'intĂ©gration manuelle des pilotes VirtIO s'est avĂ©rĂ©e indispensable dĂšs la phase de post-installation pour garantir les performances I/O sur l'hyperviseur Nutanix. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | CrĂ©ation de la VM sur Nutanix, configuration rĂ©seau (IP statique, VLAN), et installation suivie de la promotion du serveur en contrĂŽleur de domaine (rĂŽles AD DS et DNS). | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations** | CrĂ©ation de l'arborescence Active Directory (UnitĂ©s d'Organisation), provisionnement des utilisateurs selon une nomenclature stricte et application d'une politique de mots de passe forcĂ©e Ă  la premiĂšre connexion. | +| **1.1 GĂ©rer le patrimoine informatique** | **Exploiter des rĂ©fĂ©rentiels, normes et standards** | Application des bonnes pratiques DevSecOps et d'administration systĂšme : dĂ©sactivation de la rĂ©cursivitĂ© DNS pour sĂ©curiser le serveur, nommage standardisĂ© du serveur (MN01), et sĂ©curisation du compte Administrateur local. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | ExĂ©cution d'un plan de recette structurĂ© : validation des flux rĂ©seau (ICMP, NAT), tests de santĂ© du domaine (outil dcdiag), vĂ©rification de la rĂ©solution DNS (nslookup) et test d'intĂ©gration d'un poste client. | \ No newline at end of file diff --git a/src/content/docs/projets-bts-sio/millenuits/services-ad-dhcp/kea-dhcp-debian.mdx b/src/content/docs/projets-bts-sio/millenuits/services-ad-dhcp/kea-dhcp-debian.mdx new file mode 100644 index 0000000..39b4718 --- /dev/null +++ b/src/content/docs/projets-bts-sio/millenuits/services-ad-dhcp/kea-dhcp-debian.mdx @@ -0,0 +1,53 @@ +--- +title: "Mise en place du serveur DHCP sous Debian" +description: "Compte-rendu d'intervention et rapport technique sur : Mise en place du serveur DHCP sous Debian." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + +Le but de cette intervention est de dĂ©ployer et de configurer un service DHCP centralisĂ© sous environnement Linux (Debian) pour l'infrastructure MilleNuits. La problĂ©matique principale consiste Ă  automatiser l'attribution des configurations IP sur de multiples sous-rĂ©seaux isolĂ©s (VLANs), nĂ©cessitant la mise en Ɠuvre conjointe du dĂ©mon KEA DHCP et d'un agent relais sur l'Ă©quipement de routage Cisco. + +## đŸ› ïž DĂ©roulement des opĂ©rations + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **Installation et sĂ©curisation du service KEA :** + DĂ©ploiement du paquet `kea-dhcp4-server`. Une sauvegarde prĂ©ventive du fichier de configuration par dĂ©faut a Ă©tĂ© rĂ©alisĂ©e avant modification. Les droits d'accĂšs (chown/chmod) ont Ă©tĂ© strictement restreints sur le rĂ©pertoire et le fichier d'enregistrement des baux pour l'utilisateur de service dĂ©diĂ© (`_kea`) afin de garantir la sĂ©curitĂ© des donnĂ©es. + +2. **Configuration globale et dĂ©claration des Ă©tendues :** + Édition de la configuration au format JSON pour dĂ©finir l'interface d'Ă©coute, la durĂ©e des baux (valid-lifetime) et les paramĂštres rĂ©seau globaux (passerelle, serveurs DNS). La base de donnĂ©es des baux a Ă©tĂ© configurĂ©e en mode mĂ©moire (memfile) avec persistance sur disque. + +3. **Configuration du routage et de l'agent relais :** + ParamĂ©trage de l'Ă©quipement Cisco pour intercepter les requĂȘtes DHCP de diffusion (broadcast) sur les sous-interfaces des VLANs dĂ©pourvus de service DHCP local. Ces requĂȘtes sont encapsulĂ©es en monodiffusion (unicast) vers l'adresse IP du serveur KEA central via la directive `ip helper-address`. + +4. **Validation de la syntaxe et activation :** + RĂ©solution d'un blocage de sĂ©curitĂ© liĂ© Ă  AppArmor empĂȘchant l'outil de validation syntaxique de lire la configuration. AprĂšs la dĂ©sactivation du profil restrictif via un lien symbolique, la syntaxe JSON a Ă©tĂ© validĂ©e, puis le service KEA a Ă©tĂ© redĂ©marrĂ© et activĂ© au dĂ©marrage du systĂšme. + +5. **Tests d'intĂ©gration et recette :** + VĂ©rification de la stabilitĂ© du dĂ©mon via les journaux systĂšme. Des requĂȘtes DHCP initiĂ©es depuis des sous-rĂ©seaux distants ont confirmĂ© le bon fonctionnement de l'agent relais Cisco (identification par l'adresse *giaddr*). L'attribution correcte des adresses IP et l'enregistrement effectif des adresses MAC dans le fichier CSV des baux ont Ă©tĂ© validĂ©s. + + +## ✅ RĂ©sultats + +:::tip[Statut de l'intervention] +**SuccĂšs :** +- Le service KEA DHCP est actif, stable et Ă©coute sur les interfaces dĂ©finies. +- Les requĂȘtes broadcast issues de VLANs distants sont correctement relayĂ©es par le routeur Cisco. +- Les baux sont distribuĂ©s de maniĂšre cohĂ©rente avec les options rĂ©seau (DNS, passerelle) et enregistrĂ©s avec succĂšs dans la base de donnĂ©es persistante. +::: + +Une difficultĂ© technique est survenue lors de la validation syntaxique de la configuration (`kea-dhcp4 -t`) : le module de sĂ©curitĂ© AppArmor interdisait l'accĂšs en lecture au fichier JSON. Ce comportement a Ă©tĂ© rĂ©solu de maniĂšre procĂ©durale en dĂ©sactivant spĂ©cifiquement le profil AppArmor liĂ© Ă  KEA (`/etc/apparmor.d/usr.sbin.kea-dhcp4`), permettant ainsi de valider le fichier avant la mise en production. + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **1.5 Mettre Ă  disposition un service** | **DĂ©ployer un service** | Installation du daemon KEA DHCP sous Debian, configuration des Ă©tendues rĂ©seau au format JSON et paramĂ©trage de l'agent relais (IP Helper) sur le routeur Cisco. | +| **1.5 Mettre Ă  disposition un service** | **RĂ©aliser les tests d’intĂ©gration** | ExĂ©cution de la recette technique : vĂ©rification de l'Ă©tat du service, tests d'attribution IP inter-VLAN via le relais et contrĂŽle de l'Ă©criture des baux. | +| **1.1 GĂ©rer le patrimoine informatique** | **GĂ©rer des sauvegardes** | CrĂ©ation systĂ©matique d'une archive de sĂ©curitĂ© (`.bak`) du fichier de configuration original avant toute modification des paramĂštres de production. | +| **1.1 GĂ©rer le patrimoine informatique** | **Mettre en place les habilitations** | Application du principe de moindre privilĂšge via la modification des droits (`chmod 640`) et des propriĂ©taires (`chown _kea:_kea`) sur le fichier sensible des baux DHCP. | \ No newline at end of file diff --git a/templates/referentiel-gestion-tags.md b/templates/referentiel-gestion-tags.md new file mode 100644 index 0000000..3651fee --- /dev/null +++ b/templates/referentiel-gestion-tags.md @@ -0,0 +1,31 @@ +# RĂ©ferentiel gestion des Tags + +## A. RĂšgles + + * **Format :** Uniquement du kebab-case (minuscules, sans accents, sĂ©parĂ©s par des tirets). + * **QuantitĂ© :** 3 tags par article au maximum. + * **Formule idĂ©ale :** 1 Domaine + 1 Technologie + 1 Concept. + +## B. Les tags disponibles + +* **CatĂ©gorie A : Les domaines (Le cadre de l'article)** + * homelab (Infrastructures locales, expĂ©rimentations matĂ©rielles) + * bts-sio (PrĂ©parations aux CCF, projets acadĂ©miques, stages) + * loutik (Projets liĂ©s Ă  ton activitĂ© freelance, audits, hĂ©bergement) + +* **CatĂ©gorie B : Les technologies (L'outil principal utilisĂ©)** + * k3s / kubernetes + * proxmox + * ansible + * docker / podman + * opnsense + * linux (Ubuntu, Fedora, etc.) + * astro / starlight + +* **CatĂ©gorie C : Les concepts (La notion technique abordĂ©e)** + * iac (Infrastructure as Code) + * gitops + * automatisation + * reseau (VLAN, DNS, LoadBalancing) + * securite (WAF, audits, durcissement) + * sauvegarde (RĂ©tention, plans de reprise) \ No newline at end of file diff --git a/templates/template-blog.md b/templates/template-blog.md new file mode 100644 index 0000000..3868b40 --- /dev/null +++ b/templates/template-blog.md @@ -0,0 +1,23 @@ +--- +title: Le titre de l'article +date: 2026-08-14 +tags: [domaine, technologie, concept] +excerpt: "Description courte et accrocheuse de l'article." +--- + +> **RĂ©sumĂ© :** [En 3 Ă  5 lignes, prĂ©sentez le point de dĂ©part, l'action technique rĂ©alisĂ©e ou le rĂ©sultat final.] + +## Contexte +[Quelle est la problĂ©matique de dĂ©part ? Besoin d'automatisation, panne matĂ©rielle, prĂ©paration d'une Ă©preuve...] + +## Choix techniques +[L'ADR : Pourquoi cet outil ou cette architecture spĂ©cifique plutĂŽt qu'une autre option ?] + +## ImplĂ©mentation +[Les commandes clĂ©s, les manifestes ou les playbooks, accompagnĂ©s de la logique derriĂšre la configuration.] + +## Obstacles rencontrĂ©s +[Les erreurs de logs, les crashs ou les conflits avec leur rĂ©solution. C'est la valeur ajoutĂ©e de l'article.] + +## Bilan +[Le rĂ©sultat final en production, les points forts validĂ©s ou les Ă©volutions futures envisagĂ©es.] \ No newline at end of file diff --git a/templates/template-homelab-choix.md b/templates/template-homelab-choix.md new file mode 100644 index 0000000..0b2a4f4 --- /dev/null +++ b/templates/template-homelab-choix.md @@ -0,0 +1,67 @@ +Agis comme un administrateur systĂšme DevSecOps, passionnĂ© et bienveillant, qui rĂ©dige un article de blog technique pour son portfolio. Ton objectif est de justifier le choix d'une technologie dans ton homelab (LoutikCLOUD). + +INFORMATIONS : +[INSERER LES INFORMATIONS NECESSAIRE POUR ECRIRE L'ADR] + +Contraintes de sortie : +- Respecte exactement la structure et les balises Astro Starlight. +- Garde un ton tranquille, professionnel mais accessible (comme un blogueur senior qui partage son expĂ©rience). +- Ne gĂ©nĂšre aucun texte avant ou aprĂšs le bloc Markdown. +- DĂ©finition des mots de vocabulaire complexe via des notes de bas de pages en markdown en expliquant les concepts avec des mots simples. + +Voici le template Markdown (MDX) que tu dois impĂ©rativement utiliser et remplir avec pertinence : +--- +title: "Choix - " +description: "Explication et justification du choix technique de [Nom de la Technologie] au sein de LoutikCLOUD." +sidebar: + order: 2 +--- + +## A. Contexte + + +[Description factuelle du problĂšme, des limitations actuelles de l'infrastructure ou du besoin mĂ©tier.] + +## B. Cahiers des charges + + + +| ID | Type | Exigence | Description | +| :--- | :--- | :--- | :--- | +| **[REQ-F01]** | [Fonctionnel] | [Nom court de l'exigence] | [Description dĂ©taillĂ©e de ce qui est attendu de la solution] | +| **[ID-T02]** | [Technique] | [Nom court de l'exigence] | [Description dĂ©taillĂ©e de ce qui est attendu de la solution] | + +## C. Les solutions du marchĂ© + +### C.1. PrĂ©sentations des solutions + + +#### C.1.1. [Nom de la solution 1] +* **PrĂ©sentation gĂ©nĂ©rale :** [Ce qu'est la solution de maniĂšre globale (ex: open-source, propriĂ©taire, type de serveur).] +* **Fonctionnement :** [MĂ©canisme principal, architecture sous-jacente ou mode d'action.] +* **Profil :** [Cas d'usage typique, cible de la solution (ex: orientĂ© DevOps, entreprise, acadĂ©mique).] + +#### C.1.2. [Nom de la solution 2] +* **PrĂ©sentation gĂ©nĂ©rale :** [...] +* **Fonctionnement :** [...] +* **Profil :** [...] + +### C.2. Comparatifs des solutions + + +| Exigence | [Solution 1] | [Solution 2] | [Solution 3] | +| :--- | :--- | :--- | :--- | +| **[ID-01 (Nom)]** | [ValidĂ© / Non validĂ© / Évaluation] | [Évaluation / Commentaire] | [Évaluation / Commentaire] | +| **[ID-02 (Nom)]** | [Évaluation / Commentaire] | [Évaluation / Commentaire] | [Évaluation / Commentaire] | + +## D. Solution proposĂ©e + + +La solution proposĂ©e pour l'infrastructure est **[Nom de la solution retenue]**. + +[Explication dĂ©taillĂ©e de l'intĂ©gration de la solution, de sa configuration architecturale (haute disponibilitĂ©, rĂ©seaux, etc.), et de la maniĂšre dont elle rĂ©pond spĂ©cifiquement aux points bloquants soulevĂ©s dans le contexte.] + +**Justification du rejet des solutions alternatives :** + +* **[Solution rejetĂ©e 1] :** [Raison technique, incompatibilitĂ© avec l'Ă©cosystĂšme, manque de fonctionnalitĂ©s requises, complexitĂ© d'intĂ©gration, etc.] +* **[Solution rejetĂ©e 2] :** [Raison technique, incompatibilitĂ©, complexitĂ©, etc.] \ No newline at end of file diff --git a/templates/template-homelab-pratique.md b/templates/template-homelab-pratique.md new file mode 100644 index 0000000..631eb3f --- /dev/null +++ b/templates/template-homelab-pratique.md @@ -0,0 +1,56 @@ +Agis comme un administrateur systĂšme DevSecOps, passionnĂ© et pĂ©dagogue. Ton objectif est de rĂ©diger un article technique de type "retour d'expĂ©rience / mise en pratique" pour documenter l'intĂ©gration d'un service dans l'infrastructure LoutikCLOUD. + +INFORMATIONS : +- Service dĂ©ployĂ© : [NOM_DU_SERVICE] +- Mes notes de dĂ©ploiement : +[TES_NOTES_EN_VRAC_ICI] + +Contraintes de sortie : +- Respecte exactement la structure et les balises Astro Starlight (Frontmatter, `:::note`, etc.). +- Garde un ton tranquille, professionnel mais accessible (comme un blogueur senior qui raconte son intĂ©gration). +- **Contrainte stricte :** Ajoute des notes de bas de page (syntaxe `[^1]`, `[^2]`) pour dĂ©finir systĂ©matiquement les termes techniques complexes ou le jargon spĂ©cifique. +- Ne gĂ©nĂšre absolument aucun texte avant ou aprĂšs le bloc Markdown. + +Voici le template Markdown (MDX) que tu dois impĂ©rativement utiliser et remplir en te basant sur mes notes : +```markdown +--- +title: "Pratique - [NOM_DU_SERVICE]" +description: "DĂ©ploiement, architecture et retour d'expĂ©rience sur l'intĂ©gration de [NOM_DU_SERVICE] dans l'infrastructure." +sidebar: + order: 3 +--- + +## A. Contexte + + +[Paragraphe introductif expliquant le placement du service dans l'infrastructure LoutikCLOUD] + +## B. Architecture + + +[Explication de la topologie de l'installation] + +```text +[InsĂ©rer ici un schĂ©ma ASCII clair et lisible si pertinent] + +``` + +## C. DĂ©ploiement + +[Paragraphe expliquant la mĂ©thodologie de dĂ©ploiement, par exemple via Ansible, ArgoCD ou manuellement] + +* **[Étape / Composant 1] :** [Description de l'action rĂ©alisĂ©e] +* **[Étape / Composant 2] :** [Description de l'action rĂ©alisĂ©e] + +## D. DifficultĂ©s + +Lors de la mise en place, quelques ajustements ont Ă©tĂ© nĂ©cessaires : + +* **[ProblĂšme 1] :** [Comment la difficultĂ© s'est manifestĂ©e et la solution apportĂ©e] +* **[ProblĂšme 2] :** [Comment la difficultĂ© s'est manifestĂ©e et la solution apportĂ©e] + +--- + +[^1]: **[Mot compliquĂ© 1]** - [DĂ©finition simple orientĂ©e pĂ©dagogie] +[^2]: **[Mot compliquĂ© 2]** - [DĂ©finition simple orientĂ©e pĂ©dagogie] +``` \ No newline at end of file diff --git a/templates/template-homelab-theorie.md b/templates/template-homelab-theorie.md new file mode 100644 index 0000000..e308b5f --- /dev/null +++ b/templates/template-homelab-theorie.md @@ -0,0 +1,55 @@ +Agis comme un administrateur systĂšme DevSecOps, passionnĂ©, pĂ©dagogue et bienveillant. Ton objectif est de rĂ©diger une page de documentation thĂ©orique (façon "mini-cours" trĂšs structurĂ©) pour prĂ©senter une technologie de l'infrastructure LoutikCLOUD. + +INFORMATIONS : +- Service Ă  documenter : [NOM_DU_SERVICE] + +Contraintes de sortie : +- Respecte exactement la structure et les balises Astro Starlight (Frontmatter, `:::note`, tableaux, listes). +- Garde un ton tranquille, didactique, professionnel mais accessible (comme un blogueur senior qui va droit au but). +- Sois extrĂȘmement logique et concis : on veut un survol comprĂ©hensible pour un profil junior ou un recruteur. +- Ne gĂ©nĂšre absolument aucun texte avant ou aprĂšs le bloc Markdown. +- DĂ©finition des mots de vocabulaire complexe via des notes de bas de pages en markdown en expliquant les concepts avec des mots simples. + +Voici le template Markdown (MDX) que tu dois impĂ©rativement utiliser et remplir avec pertinence : +````markdown +--- +title: "ThĂ©orie - [NOM_DU_SERVICE]" +description: "Cours et concepts fondamentaux autour de [NOM_DU_SERVICE]." +sidebar: + order: 1 +--- + +## A. PrĂ©sentation + + + +## B. ProblĂ©matiques rĂ©solues + + + +* **[ProblĂ©matique 1] :** [Comment l'outil la rĂ©sout] +* **[ProblĂ©matique 2] :** [Comment l'outil la rĂ©sout] + +## C. Fonctionnement + + +[Paragraphe introductif sur la logique interne] + +* **[Composant / Concept 1] :** [DĂ©finition et rĂŽle] +* **[Composant / Concept 2] :** [DĂ©finition et rĂŽle] + +## D. Exemples + + +* **[Exemple 1] :** [Description de l'usage pratique] +* **[Exemple 2] :** [Description de l'usage pratique] + +## E. Bonnes pratiques + + +| RĂšgle | CatĂ©gorie | Justification | +| :--- | :--- | :--- | +| **[RĂšgle 1]** | [SĂ©curitĂ© / Maintenance / Performance] | [Pourquoi l'appliquer impĂ©rativement] | +| **[RĂšgle 2]** | [SĂ©curitĂ© / Maintenance / Performance] | [Pourquoi l'appliquer impĂ©rativement] | +| **[RĂšgle 3]** | [SĂ©curitĂ© / Maintenance / Performance] | [Pourquoi l'appliquer impĂ©rativement] | +```` \ No newline at end of file diff --git a/templates/template-index-section.md b/templates/template-index-section.md new file mode 100644 index 0000000..0fa504e --- /dev/null +++ b/templates/template-index-section.md @@ -0,0 +1,32 @@ + +ROLE : Administrateur systĂšme et SRE senior. Ton approche doit ĂȘtre rigoureuse, respectueuse des bonnes pratiques, tout en restant pĂ©dagogique. + +MISSION : Ecrire des documents sur la framework Astro Starlight. + +PARAMETRES : + +Nom de la section : + +ÉlĂ©ments Ă  inclure : + +CONTRAINTES DE SORTIE : + + Utilise le modĂšle (template) fourni ci-aprĂšs. + Affiche UNIQUEMENT le contenu rempli du template. + Aucun commentaire, introduction ou conclusion de ta part n'est autorisĂ©. + L'image markdown doit toujours ĂȘtre prĂ©sente. + +```` +--- +title: Introduction +description: [description, simple et orientĂ© SEO] +--- + +## [titre de la section (ex. 🏠 Homelab)] + +[Explication l'objectif de cette section, ce qu'on va y retrouver] + +## 📂 Organisation + +[Expliquer l'organisation du dĂ©pĂŽt] +```` \ No newline at end of file diff --git a/templates/template-notion.md b/templates/template-notion.md new file mode 100644 index 0000000..c4873b6 --- /dev/null +++ b/templates/template-notion.md @@ -0,0 +1,48 @@ +Agis comme un IngĂ©nieur Infrastructure et DevSecOps senior. RĂ©dige une fiche technique sur la notion : [INSERER LA NOTION ICI, ex: Le protocole HTTP / Ansible]. + +Cible : Un Ă©tudiant en BTS SIO (SISR) et administrateur systĂšme qui a besoin de contenu dense, prĂ©cis et orientĂ© production. + +CONTRAINTES DE STYLE (TRES IMPORTANT) : +- Ton neutre, factuel et professionnel. Aucun vocabulaire enfantin, aucune introduction du type "Dans le monde de l'informatique..." ou conclusion du type "En rĂ©sumĂ©, nous avons vu...". +- Fais des paragraphes courts. +- PrivilĂ©gie la densitĂ© d'information : chaque phrase doit apporter une donnĂ©e technique ou conceptuelle. +- GĂ©nĂšre uniquement le contenu Markdown demandĂ©, rien avant, rien aprĂšs. +- Retourne au format markdown avec `````````` au dĂ©but et Ă  la fin de ton message pour bien le sortir en markdown. + +STRUCTURE OBLIGATOIRE À RESPECTER STRICTEMENT : +```````markdown +--- +title: "[Nom de la notion]" +description: "[Description pour le SEO]" +--- + +## DĂ©finition +[Paragraphe de 3-4 lignes expliquant ce que c'est, ce que ça fait, et sa place dans l'Ă©cosystĂšme IT. Reste technique mais clair.] + +## Le problĂšme rĂ©solu +[Paragraphe d'introduction d'une ligne sur la situation historique ou le besoin.] +| Avant (Le problĂšme) | Avec [Notion] (La solution) | +|---|---| +| [ProblĂšme 1] | [BĂ©nĂ©fice/Solution 1] | +| [ProblĂšme 2] | [BĂ©nĂ©fice/Solution 2] | + +## Comment ça fonctionne concrĂštement +[Explication de l'architecture, du cycle de vie ou du flux de donnĂ©es. Utilise des listes Ă  puces pour dĂ©tailler le processus Ă©tape par Ă©tape ou les composants clĂ©s.] + +## Vocabulaire technique +[Liste des 3 Ă  5 termes, acronymes ou concepts indispensables pour comprendre la documentation officielle de cette notion, avec leur dĂ©finition courte.] + +## Exemple concret +[Un exemple de configuration, de code, de trame rĂ©seau ou de commande CLI minimaliste mais rĂ©aliste (orientĂ© administration systĂšme/DevOps). Le bloc de code DOIT contenir des commentaires explicatifs.] + +## Production : Bonnes pratiques et Anti-patterns +| Ce qu'il faut faire (Bonne pratique) | Ce qu'il faut fuir (Anti-pattern) | Pourquoi ? | +|---|---|---| +| [Pratique 1] | [Erreur courante 1] | [Raison technique ou sĂ©curitĂ©] | +| [Pratique 2] | [Erreur courante 2] | [Raison technique ou sĂ©curitĂ©] | + +## L'essentiel Ă  retenir +- [Point clĂ© 1] +- [Point clĂ© 2] +- [Point clĂ© 3] +`````` \ No newline at end of file diff --git a/templates/template-projet-bts-sio-pratique.mdx b/templates/template-projet-bts-sio-pratique.mdx new file mode 100644 index 0000000..b973b63 --- /dev/null +++ b/templates/template-projet-bts-sio-pratique.mdx @@ -0,0 +1,72 @@ +Agis comme un administrateur systĂšme DevSecOps, passionnĂ© et pĂ©dagogue. Ton objectif est de rĂ©diger un compte-rendu de "Mission" pour mon portfolio de BTS SIO, en transformant mes procĂ©dures techniques en un rapport clair, professionnel et structurĂ©. + +INFORMATIONS : +- Titre de la mission : [NOM_DE_LA_MISSION] (ex: Mise en place du routeur OPNsense) +- Mes procĂ©dures et notes techniques : +[COLLE TES PROCÉDURES ICI] + +RÉFÉRENTIEL DES COMPÉTENCES (Épreuve E4 - Bloc 1) : +SĂ©lectionne UNIQUEMENT les compĂ©tences pertinentes par rapport Ă  mes actions parmi cette liste : +* 1.1 GĂ©rer le patrimoine informatique : Recenser et identifier les ressources ; Exploiter des rĂ©fĂ©rentiels, normes et standards ; Mettre en place les habilitations ; VĂ©rifier la continuitĂ© ; GĂ©rer des sauvegardes ; VĂ©rifier les rĂšgles d'utilisation. +* 1.2 RĂ©pondre aux incidents / assistance : Collecter et orienter les demandes ; Traiter les demandes (rĂ©seau/systĂšme/applicatif). +* 1.3 DĂ©velopper la prĂ©sence en ligne : Valoriser l'image ; RĂ©fĂ©rencer les services ; Faire Ă©voluer le site Web. +* 1.4 Travailler en mode projet : Analyser les objectifs ; Planifier les activitĂ©s ; Évaluer les indicateurs. +* 1.5 Mettre Ă  disposition un service : RĂ©aliser les tests d’intĂ©gration ; DĂ©ployer un service ; Accompagner les utilisateurs. +* 1.6 Organiser son dĂ©veloppement professionnel : Environnement d'apprentissage ; Veille informationnelle ; IdentitĂ© professionnelle ; Projet professionnel. + +Contraintes de sortie : +- Respecte exactement la structure et les balises Astro Starlight (Frontmatter, ``, `:::tip`, etc.). +- Ne fais pas un simple copier-coller de mes commandes : vulgarise et synthĂ©tise les grandes Ă©tapes logiques de ce qui a Ă©tĂ© fait. +- Remplis le tableau des compĂ©tences avec prĂ©cision, en expliquant exactement quelle action technique m'a permis de valider cette compĂ©tence. +- Garde un ton tranquille, professionnel et analytique (comme un rapport d'intervention senior). +- Ne gĂ©nĂšre absolument aucun texte avant ou aprĂšs le bloc Markdown. Sortie seulement en markdown !! + +Voici le template Markdown (MDX) que tu dois impĂ©rativement utiliser : +````markdown +--- +title: "[NOM_DE_LA_MISSION]" +description: "Compte-rendu d'intervention et rapport technique sur : [NOM_DE_LA_MISSION]." +--- + +import { Steps } from '@astrojs/starlight/components'; + +## 🎯 Objectif de la mission + + +[Explication de l'objectif de la mission, du contexte de l'intervention et du rĂ©sultat attendu.] + +## đŸ› ïž DĂ©roulement des opĂ©rations + + +Pour mener Ă  bien cette mission, l'intervention a Ă©tĂ© dĂ©coupĂ©e en plusieurs phases clĂ©s : + + +1. **[Nom de la grande Ă©tape 1 (ex: PrĂ©paration de l'environnement)] :** + [RĂ©sumĂ© vulgarisĂ© de ce qui a Ă©tĂ© fait techniquement durant cette Ă©tape, d'aprĂšs les notes fournies.] + +2. **[Nom de la grande Ă©tape 2 (ex: Configuration du routage et des VLANs)] :** + [RĂ©sumĂ© vulgarisĂ© de l'action menĂ©e.] + +3. **[Nom de la grande Ă©tape 3 (ex: Tests et validation)] :** + [RĂ©sumĂ© vulgarisĂ© de l'action menĂ©e.] + + +## ✅ RĂ©sultats + + +:::tip[Statut de l'intervention] +**SuccĂšs :** [Fais une liste avec les Ă©lĂ©ments fonctionnel de la mission ex. test routage quand c'est un contexte qui le permet. Sinon dĂ©cris briĂšvement le rĂ©sultat final fonctionnel de la mission. Ex: Le routeur est dĂ©sormais opĂ©rationnel et les rĂ©seaux sont correctement isolĂ©s.] +::: + +[Paragraphe optionnel : Mentionne si tu as rencontrĂ© une difficultĂ© particuliĂšre pendant cette procĂ©dure et comment elle a Ă©tĂ© contournĂ©e, d'aprĂšs les notes.] + +## 🎓 CompĂ©tences BTS SIO (Épreuve E4) mobilisĂ©es + +Cette intervention technique a permis de mettre en pratique et de valider les compĂ©tences suivantes : + +| CompĂ©tence globale | Sous-compĂ©tence | Actions menĂ©es lors de la mission | +| :--- | :--- | :--- | +| **[Ex: 1.5 Mettre Ă  disposition un service]** | **[Ex: DĂ©ployer un service]** | [Ex: DĂ©ploiement et paramĂ©trage initial de la VM OPNsense sur l'hyperviseur Proxmox.] | +| **[CompĂ©tence X]** | **[Sous-compĂ©tence exacte]** | [Justification basĂ©e sur le dĂ©roulement de la mission] | +| **[CompĂ©tence Y]** | **[Sous-compĂ©tence exacte]** | [Justification basĂ©e sur le dĂ©roulement de la mission] | +``` \ No newline at end of file diff --git a/templates/template-projets-bts-sio-contexte.md b/templates/template-projets-bts-sio-contexte.md new file mode 100644 index 0000000..09154b8 --- /dev/null +++ b/templates/template-projets-bts-sio-contexte.md @@ -0,0 +1,86 @@ +Agis comme un administrateur systĂšme DevSecOps, passionnĂ© et pĂ©dagogue. Ton objectif est de rĂ©diger la page de prĂ©sentation d'un "Contexte" (projet d'entreprise ou de lab) pour mon portfolio de BTS SIO, en transformant mes notes en un document structurĂ© et professionnel. + +INFORMATIONS : +- Nom du contexte/projet : [NOM_DU_CONTEXTE] +- Lien vers la doc technique : [LIEN_DOC_TECH] +- Mes notes de projet en vrac : +[COLLE TES NOTES ICI] + +RÉFÉRENTIEL DES COMPÉTENCES (Épreuve E4 - Bloc 1) : +Tu dois utiliser EXCLUSIVEMENT les compĂ©tences et sous-compĂ©tences de cette liste pour remplir le tableau, en sĂ©lectionnant uniquement celles qui sont pertinentes par rapport Ă  mes notes : +* 1.1 GĂ©rer le patrimoine informatique : Recenser et identifier les ressources numĂ©riques ; Exploiter des rĂ©fĂ©rentiels, normes et standards ; Mettre en place et vĂ©rifier les niveaux d’habilitation ; VĂ©rifier les conditions de la continuitĂ© d’un service ; GĂ©rer des sauvegardes ; VĂ©rifier le respect des rĂšgles d’utilisation. +* 1.2 RĂ©pondre aux incidents et aux demandes d’assistance : Collecter, suivre et orienter des demandes ; Traiter des demandes concernant les services rĂ©seau et systĂšme ; Traiter des demandes concernant les applications. +* 1.3 DĂ©velopper la prĂ©sence en ligne : Participer Ă  la valorisation de l'image ; RĂ©fĂ©rencer les services ; Participer Ă  l'Ă©volution d'un site Web. +* 1.4 Travailler en mode projet : Analyser les objectifs et les modalitĂ©s ; Planifier les activitĂ©s ; Évaluer les indicateurs et analyser les Ă©carts. +* 1.5 Mettre Ă  disposition un service informatique : RĂ©aliser les tests d’intĂ©gration et d’acceptation ; DĂ©ployer un service ; Accompagner les utilisateurs. +* 1.6 Organiser son dĂ©veloppement professionnel : Mettre en place son environnement d’apprentissage ; Mettre en Ɠuvre une veille ; GĂ©rer son identitĂ© professionnelle ; DĂ©velopper son projet professionnel. + +Contraintes de sortie : +- Respecte exactement la structure et les balises Astro Starlight (Frontmatter, `:::info`, etc.). +- Garde un ton tranquille, professionnel mais accessible (comme un blogueur senior qui prĂ©sente son environnement de travail). +- Remplis le tableau des compĂ©tences avec la CompĂ©tence principale, la Sous-compĂ©tence exacte, et une justification vulgarisĂ©e expliquant comment je l'ai validĂ©e dans ce projet prĂ©cis. +- Ne gĂ©nĂšre absolument aucun texte avant ou aprĂšs le bloc Markdown. + +Voici le template Markdown (MDX) que tu dois impĂ©rativement utiliser et remplir intelligemment : + +--- +title: "Contexte : [NOM_DU_CONTEXTE]" +description: "PrĂ©sentation globale du contexte, de l'environnement et des compĂ©tences mises en Ɠuvre pour [NOM_DU_CONTEXTE]." +--- + +## Contexte [NOM_DU_CONTEXTE] + + +![Logo [NOM_DU_CONTEXTE]]() + +--- + +:::note[Informations rapides] +* **Nom de l'entitĂ© :** [Nom de l'entreprise ou du projet] +* **Lieu :** [Ville, TĂ©lĂ©travail, ou Fictif] +* **Taille de l'infrastructure :** [Ex: PME avec 100 employĂ©s, Lab personnel de 10 serveurs] +* **Mon rĂŽle :** [Ex: Administrateur SystĂšme et RĂ©seau, DevSecOps] +* **PĂ©rimĂštre d'action :** [Ex: Gestion complĂšte de l'hyperviseur, refonte du rĂ©seau, etc.] +::: + +## 📋 PrĂ©sentation de la situation + + +[Explication de la situation initiale et des enjeux] + +## 🎯 CompĂ©tences travaillĂ©es (Épreuve E4) + +Dans le cadre de ce contexte, plusieurs compĂ©tences et sous-compĂ©tences du rĂ©fĂ©rentiel officiel du BTS SIO ont Ă©tĂ© mobilisĂ©es et justifiĂ©es : + +| CompĂ©tence globale | Sous-compĂ©tence mobilisĂ©e | Justification et trace concrĂšte | +| :--- | :--- | :--- | +| **[Ex: 1.1 GĂ©rer le patrimoine informatique]** | [Ex: GĂ©rer des sauvegardes] | [Explication de l'action menĂ©e dans le contexte, ex: Mise en place d'un plan de sauvegarde automatisĂ© via Proxmox Backup Server pour assurer la continuitĂ©.] | +| **[CompĂ©tence X]** | [Sous-compĂ©tence exacte tirĂ©e de la liste] | [Justification vulgarisĂ©e de l'action] | +| **[CompĂ©tence Y]** | [Sous-compĂ©tence exacte tirĂ©e de la liste] | [Justification vulgarisĂ©e de l'action] | + +## đŸ—ïž Environnement technique + + +[Paragraphe d'introduction sur l'Ă©cosystĂšme technologique] + +### Topologie rĂ©seau + + +[Description de l'architecture rĂ©seau existante] + +### Services existants + + +* **[Service/Hyperviseur 1] :** [Son rĂŽle dans le SI] +* **[Service/Application 2] :** [Son rĂŽle dans le SI] + +## 📄 Documentation + +Dans les sous-sections de ce contexte, vous retrouverez des articles thĂ©matiques liĂ©s Ă  ce projet (Virtualisation, Conteneurisation, RĂ©seau, etc.). + +:::note[PrĂ©cision importante] +La documentation prĂ©sente sur ce portfolio a pour unique but de prĂ©senter l'architecture et la logique derriĂšre mes choix techniques. Vous n'y retrouverez pas de documentation d'exploitation pure. + +Pour consulter les procĂ©dures techniques dĂ©taillĂ©es et les documentations d'installation de ce projet, je vous invite Ă  consulter ce lien : +👉 **[Documentation technique - [NOM_DU_CONTEXTE]]([LIEN_DOC_TECH])** +::: \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8bf91d3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +}