# Secrets Rotation Runbook — SentryAgent.ai AgentIdP **Control:** SOC 2 CC9.2 — Secrets Rotation **Last updated:** 2026-03-31 --- ## Overview AgentIdP manages three categories of secrets that require periodic rotation: 1. **Agent client secrets** — Per-credential client secrets used for OAuth 2.0 token issuance 2. **OIDC signing keys** — RSA/EC keys used to sign ID tokens 3. **AES-256-CBC encryption key** — Column-level database encryption key (see `encryption-runbook.md`) --- ## 1. Agent Credential (Client Secret) Rotation ### API endpoint ``` POST /api/v1/agents/:agentId/credentials/:credentialId/rotate ``` Requires Bearer token with `agents:write` scope. ### Procedure ```bash # 1. List active credentials for the agent curl -s -H "Authorization: Bearer " \ "https://api.sentryagent.ai/v1/agents//credentials?status=active" # 2. Rotate the credential (generate new secret) curl -s -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"expiresAt": "2027-03-31T00:00:00.000Z"}' \ "https://api.sentryagent.ai/v1/agents//credentials//rotate" # Response includes the new clientSecret — store it immediately; it is never shown again ``` ### Key points - The new `clientSecret` is returned **once only** — store it securely before the response is discarded - The agent's previous secret is immediately invalidated (Vault KV v2 version overwritten) - An audit event `credential.rotated` is logged to the immutable audit chain - A `credential.rotated` webhook event is dispatched to all active subscriptions ### Recommended rotation schedule | Credential type | Recommended rotation interval | |---|---| | Production agent credentials | 90 days | | Staging / development credentials | 180 days | | Service account credentials | 365 days (annual) | | Credentials involved in a security incident | Immediately | ### Automated expiry detection `SecretsRotationJob` runs hourly and queries credentials expiring within 7 days. Prometheus alert `CredentialExpiryApproaching` fires immediately when any are detected. Respond to this alert by rotating the flagged credential(s) before the expiry date. --- ## 2. OIDC Signing Key Rotation ### Overview OIDC signing keys are managed by `OIDCKeyService` (`src/services/OIDCKeyService.ts`). Keys are stored in the `oidc_keys` PostgreSQL table. The current active key is used to sign all new ID tokens; public keys are exposed via `GET /.well-known/jwks.json`. ### When to rotate - Key compromise or suspected exposure - Scheduled rotation (recommended every 90 days for production) - Algorithm upgrade (e.g. RS256 → ES256) ### Rotation procedure OIDC key rotation is handled automatically by `OIDCKeyService.ensureCurrentKey()`: ```bash # Force generation of a new signing key by calling the internal rotate endpoint # (or trigger by redeploying with OIDC_FORCE_KEY_ROTATION=true) # 1. Mark current key as inactive (if manual rotation is required) psql "$DATABASE_URL" -c " UPDATE oidc_keys SET active = false WHERE active = true;" # 2. Restart the application — ensureCurrentKey() will generate a new key on startup kubectl rollout restart deployment/agentidp ``` ### JWKS update behavior - Old public keys remain in `GET /.well-known/jwks.json` for **24 hours** after rotation (grace period for in-flight tokens) - After the grace period, old keys are removed from the JWKS endpoint - Redis JWKS cache TTL is configured by `JWKS_CACHE_TTL_SECONDS` (default: 3600) ### Impact on existing tokens Existing valid tokens signed with the old key **continue to work** until they expire, as long as the old public key remains in JWKS. After the grace period, old tokens will fail verification. --- ## 3. Encryption Key Rotation See `docs/compliance/encryption-runbook.md` for the full AES-256-CBC encryption key rotation procedure. **Summary:** Generate new 32-byte hex key → write to Vault at `ENCRYPTION_KEY_VAULT_PATH` → restart app → existing rows re-encrypted lazily on next read-write cycle. --- ## Schedule Recommendations | Secret Type | Production Interval | Staging Interval | Trigger for Immediate Rotation | |---|---|---|---| | Agent client secrets | 90 days | 180 days | Credential suspected compromised | | OIDC signing keys | 90 days | 180 days | Key file exposed, algorithm upgrade | | AES-256-CBC encryption key | 365 days (annual) | On demand | Key exposed, Vault breach, compliance audit requirement | | Webhook HMAC secrets | Per customer policy | N/A | Webhook endpoint compromised | --- ## Compliance Evidence For SOC 2 CC9.2 evidence collection: - Prometheus metric history: `agentidp_credentials_expiring_soon_total` - Audit log entries with `action: credential.rotated` — query via `GET /audit?action=credential.rotated` - Key rotation records from Vault audit log - This runbook + sign-off from Security Engineering