Rotation des clés Ed25519
Les emergency-action tokens (playbook runs, isolation commands, agent self-update wrappers) sont signés Ed25519 par la hub. L’agent vérifie contre une public key pinée. Pour la rotation sans downtime :
Le problème
Section intitulée « Le problème »Rotation naïve = nouvelle key + tous les agents vérifient immédiatement contre la nouvelle key → les tokens in-flight signés avec l’ancienne deviennent soudain invalides. En cas de compromise vous VOULEZ ça (révocation instantanée), mais pour une rotation planifiée vous voulez une grace.
Comment ça marche
Section intitulée « Comment ça marche »La hub maintient un set de hub_signing_keys par tenant. Chaque key a is_active=true, optionnellement expires_at. À la rotation :
- Génère une nouvelle keypair Ed25519
- Public part
INSERTdanshub_signing_keysavecis_active=true,expires_at=NULL - Les active keys existantes reçoivent
expires_at = NOW() + grace_days × INTERVAL '1 day' - La private key est montrée UNE FOIS dans la response, puis plus jamais
L’agent fetche périodiquement GET /api/v1/agents/:id/signing-keys/active (uniquement non-expired actives). Pendant la grace l’agent a donc LES DEUX keys dans son trust set ; les tokens signés avec l’ancienne OU la nouvelle valident.
Après expires_at de l’ancienne key elle tombe automatiquement du trust set.
Comment le faire dans l’UI
Section intitulée « Comment le faire dans l’UI »/settings → onglet Signing keys :
- Donnez une raison (“annual rotation”, “suspected compromise”, …)
- Choisissez grace days (default 7, max 90)
- Cliquez Rotate now
- Copiez la private_hex affichée immédiatement — pas stockée, montrée une fois
- Collez dans la config de déploiement de la hub (env var
MONSYS_EMERGENCY_PRIVATE_KEY_HEXou secrets manager) - Redémarrez la hub. À partir de là, la hub signe avec la nouvelle key ; les anciens tokens restent valides pendant
grace_days.
Le tableau sous le bouton montre toutes les keys : ACTIVE (pas d’expiry), EXPIRES <date> (en grace), ou RETIRED.
Scénario compromise
Section intitulée « Scénario compromise »En cas de suspicion de compromise :
- Rotate avec
grace_days=0(raccourcit grace à ~maintenant) - Collez la nouvelle private key dans le deploy
- Redémarrez la hub
- Tous les anciens tokens deviennent immédiatement invalides
Ça déclenche : les playbook-runs in-flight pas encore reçus par l’agent peuvent échouer. Donc à utiliser UNIQUEMENT pour un VRAI compromise — pas pour une maintenance planifiée.
GET /api/v1/signing-keys (admin only)POST /api/v1/signing-keys/rotate (admin, rate-limit 5/h)GET /api/v1/agents/:id/signing-keys/active (agent-auth)Body POST /rotate :
{ "reason": "annual rotation 2026", "grace_days": 7}Response (UNE FOIS) :
{ "id": "uuid", "public_hex": "abc…64", "private_hex": "def…128", "expires_grace_days": 7, "expires_at": "2026-05-17T...Z", "warning": "Save the private key now — it is shown only this once."}Chaque rotation log dans audit_log avec resource_type='signing_key', resource_id=<new_key_id>, IP, user, reason. Reviewable via /audit?resource_type=signing_key.