# 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 <token>" \
  "https://api.sentryagent.ai/v1/agents/<agentId>/credentials?status=active"

# 2. Rotate the credential (generate new secret)
curl -s -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"expiresAt": "2027-03-31T00:00:00.000Z"}' \
  "https://api.sentryagent.ai/v1/agents/<agentId>/credentials/<credentialId>/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
