# DSH VPS — Rotation des cles API et incident « API key is invalid » **Instance auteur** : hermes-nyora **Date** : 2026-08-25 **Tags** : dsh, vps-contabo, docker, secrets, rotation, bifrost, opencode **Statut** : clos — rotation complete, revocation confirmee, tour UI web verifie au vert --- ## Contexte Rotation des deux cles du dsh-vps (VK Bifrost `sk-bf-37***` + cle OpenCode Zen `sk-DWb***`) apres double exposition : passage par canal de chat (Telegram) ET commit initial du depot `bolbol/dsh-vps` (22a18ae) embarquant les valeurs reelles en fallback heredoc de `entrypoint.sh` et en dur dans `docker-compose.yml`. Rotation executee par Nabil via script local (saisie `read -s`, valeurs jamais passees par un canal) : ecriture atomique de `$DSH_HOME/.credentials.yaml` (volume `dsh_vps_home`) + `.env` projet. ## L'incident (25/08) Apres rotation : « This turn failed — API key is invalid » dans l'UI web DSH, alors que tous les fichiers de configuration semblaient a jour. Diagnostic structure (mission Gemini, cadrage hermes-nyora, 3 hypotheses a discriminer) : | Hypothese | Test | Verdict | |---|---|---| | H1 session navigateur perimee | sonde headless echouait aussi (401 AuthError) | eliminee | | H2 process demarre avant la rotation | StartedAt conteneur < rotation, ENV conteneur = anciennes cles | confirnee partielle | | H3 residus de configuration | cles revoquees EN DUR dans `docker-compose.yml` hote + `entrypoint.sh` | cause racine | **Piege de verification central** : le depot Gitea montrait les valeurs tronquees (`sk-bf-...e9d4`, sanitisation manuelle lors du commit) mais le fichier REEL sur l'hote portait les cles completes. Les preuves du premier rapport portaient sur le repo, le runtime lisait l'hote. ## Fix final 1. Cles externalisees dans `/home/dsh-agent/dsh-vps/.env` (chmod 600, couvert par `*.env` du .gitignore), injection via `env_file:` (commit 7cdda5d). 2. Fallbacks interdits : `${VAR:-sk-reelle}` remplace par `${VAR:?variable manquante}` partout (commit 7cdf4c1) — le scaffold echoue bruyamment plutot que de demarrer avec une vieille valeur. 3. `docker compose up -d --build` (recreation, pas restart), prefixes ENV du conteneur verifies alignes sur `.env`. 4. `verify-dsh.sh` durci : `process.exit` explicite sur les tests node http sinon un test echoue peut passer vert (commit e9ef336). 5. Revocation des anciennes cles : admin Bifrost (VK) + dashboard OpenCode — confirmee par Nabil. 6. Validation finale : session UI web NEUVE, tour reel reussi (le symptome initial reproduit au vert). ## Ce qui ne fonctionne PAS | Tentative | Resultat | Raison | |---|---|---| | `docker compose restart` apres changement de cles | anciennes vars conservees | restart ne relit NI le `.env` NI le compose | | Sanitiser uniquement rapports/preuves | secrets vivants dans les fichiers scaffoldes | lire INTEGRALEMENT chaque fichier qui entre en Git | | Diagnostiquer depuis des curls externes seuls | ne voit pas ce que le process charge | comparer StartedAt vs horodatages config + printenv DANS le conteneur | ## Regle fleet-wide associee Posee dans context-hub (scope infra, cle `secrets_rule`, relue en live le 25/08) : aucune valeur de cle/secret en clair dans un message, rapport ou mail. Depot obligatoire dans `/volume1/docker/.secrets-staging/.env` (hors git), reference par chemin uniquement. Reproduite dans l'`AGENTS.md` du workspace DSH (mecanisme natif `dsh-agent-instructions`). ## References - Depots : `bolbol/dsh-vps` (22a18ae → 43c5dec), `bolbol/context-hub` (e7f78f8, lecon coding-bp-0001) - Script de rotation : `workspace/scripts/rotate-dsh-keys.sh` (hermes-nyora) - Garde-fou rejouable : `.agents/verify-dsh.sh` (5 tests : pnpm, plugin, MCP authentifie, isolation 403, nettoyage skill mort)