[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CST7HE7A2XY99QSBEWRQHZ":3,"skill-same:01M6CST7HE7A2XY99QSBEWRQHZ":147},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":108,"commitSha":109,"skillDirectory":110,"forksCount":14,"repositoryHtmlUrl":111,"repositoryHomepageUrl":6,"ownerHtmlUrl":112,"ownerBlogUrl":6,"ownerDescription":113,"updatedAt":114,"install":115,"resources":128},"01M6CST7HE7A2XY99QSBEWRQHZ","volontariapp-deploy-gitops",null,"Playbook opérationnel pas-à-pas pour faire évoluer l'infrastructure Kubernetes dans le repo deploy (ArgoCD) : création de manifests durcis PSA Restricted, NetworkPolicies default-deny et validation kustomize.","Volontariapp\u002Fmeta","Volontariapp","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F263673138?v=4","01M6CSQNQ3J4SKRWA512JQF5K7","VolontariApp","01M6CSQNV8T2M4PR5A22DKHY5T",0,"MIT","https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-deploy-gitops\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000011","运维与部署","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"deploy","volontariapp deploy gitops","gitops","apps","manifests","dbs","cluster","image","manifest","kustomize","kubernetes","default","k3s","deployment","repo","README","ms","databases","api-gateway","gateway","2026-11-10 10:12:30",false,"---\nname: volontariapp-deploy-gitops\ndescription: \"Playbook opérationnel pas-à-pas pour faire évoluer l'infrastructure Kubernetes dans le repo deploy (ArgoCD) : création de manifests durcis PSA Restricted, NetworkPolicies default-deny et validation kustomize.\"\ntype: Playbook\ntitle: \"Déploiement GitOps (Cluster K3s & ArgoCD)\"\ntags: [deploy, kubernetes, argocd, security, gitops, playbook]\nstatus: stable\npaths:\n  - \"deploy\u002Fapps\u002F**\"\n  - \"deploy\u002Finfrastructure\u002F**\"\nmesh_keys:\n  - deploy\u002F\n  - argocd\n  - networkpolic\n  - kustomiz\n  - initcontainer\ngenerated:\n  by: claude-code\u002Fclaude-opus-5-5\n  at: \"2026-10-06T08:54:04Z\"\nverified:\n  - by: claude-code\u002Fagent\n    at: \"2026-11-07T15:34:32Z\"\n    digest: b48ec88eaae14f12\n  - by: claude-code\u002Fagent\n    at: \"2026-11-07T15:49:18Z\"\n    digest: 588cb1216d7d8ddb\n  - by: claude-code\u002Fagent\n    at: \"2026-11-07T15:55:35Z\"\n    digest: f42f7925a6ae2c42\n  - by: claude-code\u002Fagent\n    at: \"2026-11-07T18:48:20Z\"\n    digest: 874988c93538ded7\n  - by: claude-code\u002Fagent\n    at: \"2026-11-08T12:27:59Z\"\n    digest: 00f4f0c84bd2a17a\nsources:\n  - id: c4\n    resource: docs\u002Farchitecture\u002FC4-Deployment-And-Infrastructure.md\n    title: \"C4 Deployment & Infrastructure\"\n  - id: readme\n    resource: deploy\u002FREADME.md\n    title: deploy\u002FREADME.md\n  - id: base-user\n    resource: deploy\u002Fapps\u002Fbase\u002Fms-user\u002Fdeployment.yaml\n    title: \"Manifest ms-user (initContainer, securityContext, image sha)\"\n  - id: gw\n    resource: deploy\u002Fapps\u002Fbase\u002Fapi-gateway\u002Fdeployment.yaml\n    title: Manifest api-gateway (NODE_ENV)\n  - id: dbs\n    resource: deploy\u002Finfrastructure\u002Fdatabases\n    title: Bases de données par service\n---\n\n# Playbook : Déploiement GitOps (Cluster K3s & ArgoCD)\n\nLe dépôt `deploy` est l'unique source de vérité de l'infrastructure du cluster K3s : ArgoCD (pattern app-of-apps) synchronise automatiquement ce que contient la branche `main`[^c4]. \n\n> [!CAUTION]\n> **RÈGLE CRITIQUE : ZÉRO `kubectl apply` MANUEL EN PRODUCTION.**\n> Toute modification d'infrastructure, de dimensionnement, de secret ou de réseau passe impérativement par une Pull Request sur le dépôt `deploy`.\n\nLa documentation détaillée de la structure du cluster, des bases de données et des ressources est disponible dans [Spécifications Cluster](\u002Fvolontariapp-deploy-gitops\u002Freferences\u002Fcluster-specifications.md).\n\n---\n\n## Trigger\n\nCe playbook doit être exécuté dès qu'une tâche nécessite :\n- Le déploiement d'un nouveau microservice, runner outbox, worker BullMQ ou post-processor.\n- L'ajustement des variables d'environnement, requests\u002Flimits ou sondes de liveness\u002Freadiness.\n- L'ouverture d'un nouveau flux réseau inter-services ou vers une base de données.\n- La mise à jour d'un manifest dans `deploy\u002Fapps\u002Fbase\u002F` ou de l'overlay de production `deploy\u002Fapps\u002Foverlays\u002Fprod\u002F`.\n\n---\n\n## Procédure A : Création ou Mise à Jour d'un Manifest (`deployment.yaml`)\n\nEmplacement : `deploy\u002Fapps\u002Fbase\u002F\u003Cservice>\u002F`.\n\nChaque conteneur déployé doit se conformer strictement au profil **PSA Restricted** (Pod Security Standards) et intégrer l'attente active de ses dépendances :\n\n### 1. Template de Durcissement PSA Restricted (Obligatoire) :\n```yaml\nspec:\n  template:\n    spec:\n      securityContext:\n        runAsNonRoot: true\n        seccompProfile:\n          type: RuntimeDefault\n      containers:\n        - name: service\n          image: ghcr.io\u002Fvolontariapp\u002F\u003Cservice>:sha-\u003Ccourt> # Épinglé par commit sha\n          securityContext:\n            allowPrivilegeEscalation: false\n            readOnlyRootFilesystem: true\n            capabilities:\n              drop: [\"ALL\"]\n          volumeMounts:\n            - name: tmp\n              mountPath: \u002Ftmp\n      volumes:\n        - name: tmp\n          emptyDir: {}\n```\n\n### 2. InitContainer d'Attente de Dépendance (Base de Données \u002F Redis) :\nPour éviter les crash-loops au démarrage du pod, injecter l'initContainer standard bouclant sur la disponibilité réseau de la base[^base-user] :\n```yaml\n      initContainers:\n        - name: wait-for-db\n          image: busybox:1.28\n          command:\n            - sh\n            - -c\n            - \"until nc -zv \u003Cservice>-db-postgresql 5432; do echo waiting for db; sleep 2; done\"\n          resources:\n            requests:\n              cpu: 32m\n              memory: 64Mi\n            limits:\n              cpu: 32m\n              memory: 64Mi\n          securityContext:\n            allowPrivilegeEscalation: false\n            readOnlyRootFilesystem: true\n            capabilities:\n              drop: [\"ALL\"]\n```\n\n### 3. Règles d'Attribution des Ressources & Réseau :\n- **Requests \u002F Limits**[^readme] :\n  - Microservices `ms-*` : Requests 100m \u002F 128Mi, Limits 500m \u002F 256Mi.\n  - Runners outbox \u002F post-processors : Requests 50m \u002F 64Mi, Limits 200m \u002F 128Mi.\n  - Workers Node.js : Request 96Mi, Limit 192Mi (ne jamais fixer 96Mi de limit : risque d'OOMKill avéré).\n- **Limits CPU x4 (2026-11-07)** : toutes les `limits.cpu` (apps, runners, bases Postgres, Redis, Neo4j) ont été multipliées par 4 ; les `requests` sont inchangées car le cluster n'a qu'un nœud de 8 CPU (requests à 44 %). Les valeurs ci-dessus pour les limits CPU sont donc à lire x4. Le quota `prod` passe à `limits.cpu: 56` (l'api-gateway à 6 pods en prend 12 à elle seule) et `limits.memory: 28Gi` (à 18Gi, un redémarrage du nœud bloquait la création de `redis-master` : les anciens pods `Unknown` comptent dans le quota, `kubectl describe statefulset` montre alors `exceeded quota`) ; il faut le recalculer à chaque ajout de composant ou de `maxReplicas`. Monter les requests suppose d'abord d'ajouter un nœud worker K3s.\n- **Broker d'Outbox (`REDIS_HOST`)** :\n  - Tout `deployment-outbox.yaml` doit pointer vers `redis-master` (broker global du mesh). Ne jamais brancher une outbox sur un Redis local isolé.\n\n### 4. Haute disponibilité et autoscaling (modèle : `api-gateway`) :\n- Le load balancer est Traefik (Ingress vers le Service ClusterIP, répartition sur les endpoints des pods prêts) : aucun LB à ajouter.\n- `deploy\u002Fapps\u002Fbase\u002Fapi-gateway\u002F` : pas de champ `replicas` dans le Deployment (l'HPA le pilote, sinon ArgoCD se bat avec lui), `hpa.yaml` (min 3, max 6, CPU 70 % seul, scale-down stabilisé 300 s ; pas de métrique mémoire : elle se mesure contre la request et dépasse toujours 80 % sur un pod Node, d'où un scale-up parasite jusqu'au quota), `pdb.yaml` (`minAvailable: 2`), `readinessProbe`, `preStop: sleep 10`, `maxUnavailable: 0` et `topologySpreadConstraints` par nœud.\n- Le `ResourceQuota` de `prod` (requests CPU 6, limits CPU 10) plafonne `maxReplicas` : recalculer avant d'augmenter.\n- L'HPA exige metrics-server (livré par défaut avec K3s) et des `requests` sur chaque conteneur.\n\n---\n\n## Procédure B : Définition des NetworkPolicies (Réseau Default-Deny)\n\nLe cluster applique une politique de **Default-Deny** : tout trafic ingress\u002Fegress non explicitement déclaré est bloqué par défaut, même au sein du même namespace Kubernetes.\n\nPour ouvrir un flux réseau vers un microservice ou une base :\n\n1. Créer ou éditer la `NetworkPolicy` sous `deploy\u002Finfrastructure\u002Fsecurity\u002Fnetwork-policies\u002F\u003Cservice>-netpol.yaml` :\n```yaml\napiVersion: networking.k8s.io\u002Fv1\nkind: NetworkPolicy\nmetadata:\n  name: \u003Cservice>-network-policy\n  namespace: default\nspec:\n  podSelector:\n    matchLabels:\n      app: \u003Cservice>\n  policyTypes:\n    - Ingress\n    - Egress\n  ingress:\n    # Autoriser les appels gRPC entrants depuis l'API Gateway ou un autre service autorisé\n    - from:\n        - podSelector:\n            matchLabels:\n              app: api-gateway\n      ports:\n        - protocol: TCP\n          port: 3000\n  egress:\n    # Autoriser la connexion vers sa base PostgreSQL dédiée\n    - to:\n        - podSelector:\n            matchLabels:\n              app: \u003Cservice>-db-postgresql\n      ports:\n        - protocol: TCP\n          port: 5432\n    # Autoriser la résolution DNS du cluster\n    - to:\n        - namespaceSelector: {}\n          podSelector:\n            matchLabels:\n              k8s-app: kube-dns\n      ports:\n        - protocol: UDP\n          port: 53\n```\n\n---\n\n## Procédure C : Validation Kustomize & Synchronisation ArgoCD\n\n1. **Déclarer les manifests dans `kustomization.yaml` :**\n   - Base locale : vérifier `deploy\u002Fapps\u002Fbase\u002F\u003Cservice>\u002Fkustomization.yaml`.\n   - Overlay de production : déclarer le nouveau service ou le secret scellé dans `deploy\u002Fapps\u002Foverlays\u002Fprod\u002Fkustomization.yaml`.\n\n2. **Validation Dry-Run locale :**\n   Exécuter Kustomize localement pour valider la syntaxe et l'interpolation :\n   ```bash\n   kubectl kustomize deploy\u002Fapps\u002Foverlays\u002Fprod\u002F > \u002Fdev\u002Fnull\n   ```\n\n3. **Livraison GitOps :**\n   - Commiter sur une branche de feature du dépôt `deploy`.\n   - Ouvrir la Pull Request. Dès le merge sur `main`, ArgoCD synchronise l'état en moins de 3 minutes.\n   - Contrôler le déploiement sur la console ArgoCD : statut Synced et santé Healthy.\n\n---\n\n## Checklist d'Assurance Qualité & Critères d'Arrêt\n\nAvant de valider une modification d'infrastructure :\n- [ ] Aucun `Secret` en clair ni mot de passe dans les manifests Git (utiliser exclusivement `SealedSecret`).\n- [ ] Profil PSA Restricted intégralement respecté (`readOnlyRootFilesystem: true`, `\u002Ftmp` en `emptyDir`).\n- [ ] InitContainer `busybox:1.28` configuré pour attendre la disponibilité du port DB.\n- [ ] Requests et limits définies pour chaque conteneur.\n- [ ] NetworkPolicy déclarée pour tout nouveau flux réseau.\n- [ ] `deployment-outbox.yaml` configuré avec `REDIS_HOST: redis-master`.\n- [ ] `kubectl kustomize` compile sans aucune erreur de syntaxe.\n\n[^c4]: C4 Deployment & Infrastructure\n[^readme]: deploy\u002FREADME.md\n[^base-user]: Manifest ms-user (initContainer, securityContext, image sha)\n[^gw]: Manifest api-gateway (NODE_ENV)\n[^dbs]: Bases de données par service\n","# Playbook : Déploiement GitOps (Cluster K3s & ArgoCD)\n\nLe dépôt `deploy` est l'unique source de vérité de l'infrastructure du cluster K3s : ArgoCD (pattern app-of-apps) synchronise automatiquement ce que contient la branche `main`[^c4]. \n\n> [!CAUTION]\n> **RÈGLE CRITIQUE : ZÉRO `kubectl apply` MANUEL EN PRODUCTION.**\n> Toute modification d'infrastructure, de dimensionnement, de secret ou de réseau passe impérativement par une Pull Request sur le dépôt `deploy`.\n\nLa documentation détaillée de la structure du cluster, des bases de données et des ressources est disponible dans [Spécifications Cluster](\u002Fvolontariapp-deploy-gitops\u002Freferences\u002Fcluster-specifications.md).\n\n---\n\n## Trigger\n\nCe playbook doit être exécuté dès qu'une tâche nécessite :\n- Le déploiement d'un nouveau microservice, runner outbox, worker BullMQ ou post-processor.\n- L'ajustement des variables d'environnement, requests\u002Flimits ou sondes de liveness\u002Freadiness.\n- L'ouverture d'un nouveau flux réseau inter-services ou vers une base de données.\n- La mise à jour d'un manifest dans `deploy\u002Fapps\u002Fbase\u002F` ou de l'overlay de production `deploy\u002Fapps\u002Foverlays\u002Fprod\u002F`.\n\n---\n\n## Procédure A : Création ou Mise à Jour d'un Manifest (`deployment.yaml`)\n\nEmplacement : `deploy\u002Fapps\u002Fbase\u002F\u003Cservice>\u002F`.\n\nChaque conteneur déployé doit se conformer strictement au profil **PSA Restricted** (Pod Security Standards) et intégrer l'attente active de ses dépendances :\n\n### 1. Template de Durcissement PSA Restricted (Obligatoire) :\n```yaml\nspec:\n  template:\n    spec:\n      securityContext:\n        runAsNonRoot: true\n        seccompProfile:\n          type: RuntimeDefault\n      containers:\n        - name: service\n          image: ghcr.io\u002Fvolontariapp\u002F\u003Cservice>:sha-\u003Ccourt> # Épinglé par commit sha\n          securityContext:\n            allowPrivilegeEscalation: false\n            readOnlyRootFilesystem: true\n            capabilities:\n              drop: [\"ALL\"]\n          volumeMounts:\n            - name: tmp\n              mountPath: \u002Ftmp\n      volumes:\n        - name: tmp\n          emptyDir: {}\n```\n\n### 2. InitContainer d'Attente de Dépendance (Base de Données \u002F Redis) :\nPour éviter les crash-loops au démarrage du pod, injecter l'initContainer standard bouclant sur la disponibilité réseau de la base[^base-user] :\n```yaml\n      initContainers:\n        - name: wait-for-db\n          image: busybox:1.28\n          command:\n            - sh\n            - -c\n            - \"until nc -zv \u003Cservice>-db-postgresql 5432; do echo waiting for db; sleep 2; done\"\n          resources:\n            requests:\n              cpu: 32m\n              memory: 64Mi\n            limits:\n              cpu: 32m\n              memory: 64Mi\n          securityContext:\n            allowPrivilegeEscalation: false\n            readOnlyRootFilesystem: true\n            capabilities:\n              drop: [\"ALL\"]\n```\n\n### 3. Règles d'Attribution des Ressources & Réseau :\n- **Requests \u002F Limits**[^readme] :\n  - Microservices `ms-*` : Requests 100m \u002F 128Mi, Limits 500m \u002F 256Mi.\n  - Runners outbox \u002F post-processors : Requests 50m \u002F 64Mi, Limits 200m \u002F 128Mi.\n  - Workers Node.js : Request 96Mi, Limit 192Mi (ne jamais fixer 96Mi de limit : risque d'OOMKill avéré).\n- **Limits CPU x4 (2026-11-07)** : toutes les `limits.cpu` (apps, runners, bases Postgres, Redis, Neo4j) ont été multipliées par 4 ; les `requests` sont inchangées car le cluster n'a qu'un nœud de 8 CPU (requests à 44 %). Les valeurs ci-dessus pour les limits CPU sont donc à lire x4. Le quota `prod` passe à `limits.cpu: 56` (l'api-gateway à 6 pods en prend 12 à elle seule) et `limits.memory: 28Gi` (à 18Gi, un redémarrage du nœud bloquait la création de `redis-master` : les anciens pods `Unknown` comptent dans le quota, `kubectl describe statefulset` montre alors `exceeded quota`) ; il faut le recalculer à chaque ajout de composant ou de `maxReplicas`. Monter les requests suppose d'abord d'ajouter un nœud worker K3s.\n- **Broker d'Outbox (`REDIS_HOST`)** :\n  - Tout `deployment-outbox.yaml` doit pointer vers `redis-master` (broker global du mesh). Ne jamais brancher une outbox sur un Redis local isolé.\n\n### 4. Haute disponibilité et autoscaling (modèle : `api-gateway`) :\n- Le load balancer est Traefik (Ingress vers le Service ClusterIP, répartition sur les endpoints des pods prêts) : aucun LB à ajouter.\n- `deploy\u002Fapps\u002Fbase\u002Fapi-gateway\u002F` : pas de champ `replicas` dans le Deployment (l'HPA le pilote, sinon ArgoCD se bat avec lui), `hpa.yaml` (min 3, max 6, CPU 70 % seul, scale-down stabilisé 300 s ; pas de métrique mémoire : elle se mesure contre la request et dépasse toujours 80 % sur un pod Node, d'où un scale-up parasite jusqu'au quota), `pdb.yaml` (`minAvailable: 2`), `readinessProbe`, `preStop: sleep 10`, `maxUnavailable: 0` et `topologySpreadConstraints` par nœud.\n- Le `ResourceQuota` de `prod` (requests CPU 6, limits CPU 10) plafonne `maxReplicas` : recalculer avant d'augmenter.\n- L'HPA exige metrics-server (livré par défaut avec K3s) et des `requests` sur chaque conteneur.\n\n---\n\n## Procédure B : Définition des NetworkPolicies (Réseau Default-Deny)\n\nLe cluster applique une politique de **Default-Deny** : tout trafic ingress\u002Fegress non explicitement déclaré est bloqué par défaut, même au sein du même namespace Kubernetes.\n\nPour ouvrir un flux réseau vers un microservice ou une base :\n\n1. Créer ou éditer la `NetworkPolicy` sous `deploy\u002Finfrastructure\u002Fsecurity\u002Fnetwork-policies\u002F\u003Cservice>-netpol.yaml` :\n```yaml\napiVersion: networking.k8s.io\u002Fv1\nkind: NetworkPolicy\nmetadata:\n  name: \u003Cservice>-network-policy\n  namespace: default\nspec:\n  podSelector:\n    matchLabels:\n      app: \u003Cservice>\n  policyTypes:\n    - Ingress\n    - Egress\n  ingress:\n    # Autoriser les appels gRPC entrants depuis l'API Gateway ou un autre service autorisé\n    - from:\n        - podSelector:\n            matchLabels:\n              app: api-gateway\n      ports:\n        - protocol: TCP\n          port: 3000\n  egress:\n    # Autoriser la connexion vers sa base PostgreSQL dédiée\n    - to:\n        - podSelector:\n            matchLabels:\n              app: \u003Cservice>-db-postgresql\n      ports:\n        - protocol: TCP\n          port: 5432\n    # Autoriser la résolution DNS du cluster\n    - to:\n        - namespaceSelector: {}\n          podSelector:\n            matchLabels:\n              k8s-app: kube-dns\n      ports:\n        - protocol: UDP\n          port: 53\n```\n\n---\n\n## Procédure C : Validation Kustomize & Synchronisation ArgoCD\n\n1. **Déclarer les manifests dans `kustomization.yaml` :**\n   - Base locale : vérifier `deploy\u002Fapps\u002Fbase\u002F\u003Cservice>\u002Fkustomization.yaml`.\n   - Overlay de production : déclarer le nouveau service ou le secret scellé dans `deploy\u002Fapps\u002Foverlays\u002Fprod\u002Fkustomization.yaml`.\n\n2. **Validation Dry-Run locale :**\n   Exécuter Kustomize localement pour valider la syntaxe et l'interpolation :\n   ```bash\n   kubectl kustomize deploy\u002Fapps\u002Foverlays\u002Fprod\u002F > \u002Fdev\u002Fnull\n   ```\n\n3. **Livraison GitOps :**\n   - Commiter sur une branche de feature du dépôt `deploy`.\n   - Ouvrir la Pull Request. Dès le merge sur `main`, ArgoCD synchronise l'état en moins de 3 minutes.\n   - Contrôler le déploiement sur la console ArgoCD : statut Synced et santé Healthy.\n\n---\n\n## Checklist d'Assurance Qualité & Critères d'Arrêt\n\nAvant de valider une modification d'infrastructure :\n- [ ] Aucun `Secret` en clair ni mot de passe dans les manifests Git (utiliser exclusivement `SealedSecret`).\n- [ ] Profil PSA Restricted intégralement respecté (`readOnlyRootFilesystem: true`, `\u002Ftmp` en `emptyDir`).\n- [ ] InitContainer `busybox:1.28` configuré pour attendre la disponibilité du port DB.\n- [ ] Requests et limits définies pour chaque conteneur.\n- [ ] NetworkPolicy déclarée pour tout nouveau flux réseau.\n- [ ] `deployment-outbox.yaml` configuré avec `REDIS_HOST: redis-master`.\n- [ ] `kubectl kustomize` compile sans aucune erreur de syntaxe.\n\n[^c4]: C4 Deployment & Infrastructure\n[^readme]: deploy\u002FREADME.md\n[^base-user]: Manifest ms-user (initContainer, securityContext, image sha)\n[^gw]: Manifest api-gateway (NODE_ENV)\n[^dbs]: Bases de données par service\n",{"name":5,"tags":54,"type":58,"paths":59,"title":62,"status":63,"sources":64,"verified":83,"generated":100,"mesh_keys":103,"description":7},[29,39,55,56,31,57],"argocd","security","playbook","Playbook",[60,61],"deploy\u002Fapps\u002F**","deploy\u002Finfrastructure\u002F**","Déploiement GitOps (Cluster K3s & ArgoCD)","stable",[65,69,72,76,80],{"id":66,"title":67,"resource":68},"c4","C4 Deployment & Infrastructure","docs\u002Farchitecture\u002FC4-Deployment-And-Infrastructure.md",{"id":70,"title":71,"resource":71},"readme","deploy\u002FREADME.md",{"id":73,"title":74,"resource":75},"base-user","Manifest ms-user (initContainer, securityContext, image sha)","deploy\u002Fapps\u002Fbase\u002Fms-user\u002Fdeployment.yaml",{"id":77,"title":78,"resource":79},"gw","Manifest api-gateway (NODE_ENV)","deploy\u002Fapps\u002Fbase\u002Fapi-gateway\u002Fdeployment.yaml",{"id":34,"title":81,"resource":82},"Bases de données par service","deploy\u002Finfrastructure\u002Fdatabases",[84,88,91,94,97],{"at":85,"by":86,"digest":87},"2026-11-07T15:34:32Z","claude-code\u002Fagent","b48ec88eaae14f12",{"at":89,"by":86,"digest":90},"2026-11-07T15:49:18Z","588cb1216d7d8ddb",{"at":92,"by":86,"digest":93},"2026-11-07T15:55:35Z","f42f7925a6ae2c42",{"at":95,"by":86,"digest":96},"2026-11-07T18:48:20Z","874988c93538ded7",{"at":98,"by":86,"digest":99},"2026-11-08T12:27:59Z","00f4f0c84bd2a17a",{"at":101,"by":102},"2026-10-06T08:54:04Z","claude-code\u002Fclaude-opus-5-5",[104,55,105,106,107],"deploy\u002F","networkpolic","kustomiz","initcontainer","main","6b8f03b57018fa66da4666071dc17c1577ff45e8",".agents\u002Fskills\u002Fvolontariapp-deploy-gitops","https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta","https:\u002F\u002Fgithub.com\u002FVolontariapp","Volontariapp — Application Web\u002FMobile scalabelle construite sur une architecture microservices événementielle (Event-Driven Architecture), implémentant le Trans","2026-11-10 23:40:22",{"source":8,"skillName":5,"commands":116,"prompt":126,"zipAvailable":127},[117,120,123],{"tool":118,"command":119},"npx","npx skills add Volontariapp\u002Fmeta --skill volontariapp-deploy-gitops",{"tool":121,"command":122},"bunx","bunx skills add Volontariapp\u002Fmeta --skill volontariapp-deploy-gitops",{"tool":124,"command":125},"pnpm","pnpm dlx skills add Volontariapp\u002Fmeta --skill volontariapp-deploy-gitops","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-deploy-gitops\u002FSKILL.md",true,[129,135,139,143],{"path":130,"resourceType":131,"resourceTypeDesc":132,"sizeBytes":133,"sourceHtmlUrl":134},"index.md","REFERENCE","参考文档",850,"https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-deploy-gitops\u002Findex.md",{"path":136,"resourceType":131,"resourceTypeDesc":132,"sizeBytes":137,"sourceHtmlUrl":138},"log.md",8267,"https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-deploy-gitops\u002Flog.md",{"path":140,"resourceType":131,"resourceTypeDesc":132,"sizeBytes":141,"sourceHtmlUrl":142},"references\u002Fcluster-specifications.md",3770,"https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-deploy-gitops\u002Freferences\u002Fcluster-specifications.md",{"path":144,"resourceType":131,"resourceTypeDesc":132,"sizeBytes":145,"sourceHtmlUrl":146},"references\u002Flessons.md",16125,"https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-deploy-gitops\u002Freferences\u002Flessons.md",[148,174,200,225,250,269],{"skillNo":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":154,"publishedAt":49},"01M6CSW3Y5ZHVBF5BX7S5BAVS0","volontariapp-user-account-deletion","Suppression de compte en deux temps (PENDING_DELETION): requestDeletionWithUserDeleted, finalizeDeletion, fan-out de user.deleted vers les domaines et gather ws. A utiliser pour tout changement de domain-user touchant la suppression, le statut ou le cycle de vie d'un user.","https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-user-account-deletion\u002FSKILL.md",[],[155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],"volontariapp user account deletion","user","account deletion","deletion","cycle","gather","sonnet","pending","de","code","claude-code","reference","npm","user-account-deletion","src","en","domain","npm-packages","packages",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":183,"publishedAt":49},"01M6CSVC1ZW30DDFZ8VMEYAAK6","volontariapp-proto-contract-evolution","Modifier un contrat Protobuf de proto-registry (champ, message, enum, RPC) sans casser la compatibilité wire, puis suivre la cascade proto-registry, PR automatique dans npm-packages, publication, consommateurs. À utiliser avant toute édition d'un .proto.","https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-proto-contract-evolution\u002FSKILL.md",[180],{"categoryNo":181,"categoryName":182,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000037","律师与法务",[184,185,186,187,164,188,189,173,190,191,192,193,194,167,195,196,197,198,57,199],"evolution","volontariapp proto contract evolution","grpc","lint","workflows","analyze","rpc","ci","pr","protobuf","message","registry","sans","sync","yaml","github",{"skillNo":201,"name":202,"nameZh":6,"description":203,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":204,"categories":205,"keywords":209,"publishedAt":49},"01M6CSV99ET2Z9W4W4DAK4CMQP","volontariapp-meta-scripts","Utiliser et faire evoluer la CLI Node de meta (setup, doctor, migrate, certs, repos, bump) et le manifeste repos.json, source unique des repos et unites du workspace.","https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-meta-scripts\u002FSKILL.md",[206],{"categoryNo":207,"categoryName":208,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[210,211,212,164,213,165,214,215,216,217,218,219,220,221,222,223,224],"Meta","volontariapp meta scripts","repos","migrate","setup","meta-scripts","json","node","workspace","shell","bump","cli","doctor","draft","install",{"skillNo":226,"name":227,"nameZh":6,"description":228,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":229,"categories":230,"keywords":234,"publishedAt":49},"01M6CSTPHNTGE0WF5CPE8W7ZYQ","volontariapp-implement-async-event-flow","Guide pas-à-pas pour concevoir et implémenter un flux asynchrone complet (messaging -> outbox -> post-processors -> ws-service -> client).","https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvolontariapp-implement-async-event-flow\u002FSKILL.md",[231],{"categoryNo":232,"categoryName":233,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[235,236,237,238,239,240,169,241,242,195,243,244,172,245,246,173,247,248,164,249],"async","implement","volontariapp implement async event flow","flow","event","messaging","mapping","flux","streams","flows","shared","outbox","processor","architecture","runner",{"skillNo":251,"name":252,"nameZh":6,"description":253,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":254,"categories":255,"keywords":259,"publishedAt":49},"01M6CSRQHS0T9955M8CTA5BYJQ","research","Investigate a question against high-trust primary sources and capture the findings as a Markdown file in the repo. Use when the user wants a topic researched, docs or API facts gathered, or reading legwork delegated to a background agent.","https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fplugins\u002Fmatt-pocock\u002Fskills\u002Fresearch\u002FSKILL.md",[256],{"categoryNo":257,"categoryName":258,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000021","资料检索与研究",[252,260,156,261,43,262,263,264,265,266,267,268],"api","file","markdown","docs","capture","use","question","trust","investigate",{"skillNo":270,"name":271,"nameZh":6,"description":272,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":273,"categories":274,"keywords":278,"publishedAt":49},"01M6CSRMZ82M1YK96Z4SW1STTD","prototype","Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.","https:\u002F\u002Fgithub.com\u002FVolontariapp\u002Fmeta\u002Fblob\u002Fmain\u002F.agents\u002Fplugins\u002Fmatt-pocock\u002Fskills\u002Fprototype\u002FSKILL.md",[275],{"categoryNo":276,"categoryName":277,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000032","设计师",[271,279,156,280,265,281,266,282,283,284,285,286],"check","ui","look","design","explore","model","build","state"]