# Session key rotation

Gray uses two independent keyrings:

- `GRAY_SESSION_KEYS` encrypts durable session records and keys user indexes.
- `GRAY_SESSION_RISK_KEYS` hashes coarse risk signals.

Each value is a comma-separated list of `key-id:64-hex`. The first key is
active for writes. Up to seven later keys are retiring read keys.

```text
GRAY_SESSION_KEYS=2026-07:64-hex,2026-06:64-hex
GRAY_SESSION_RISK_KEYS=risk-2026-07:64-hex,risk-2026-06:64-hex
```

Generate independent 256-bit values with an operating-system CSPRNG. Do not
reuse encryption and risk keys, commit keys, place them in Gray source, or
include them in logs.

## Rotation procedure

1. Generate a new key under a new identifier.
2. Deploy it first while retaining every key currently needed for reads.
3. Confirm all workers have the same ordered keyring.
4. Observe store failures and perform representative session reads.
5. Gray rewrites records and risk baselines with the active key as they are
   touched.
6. Retain old encryption keys for at least the longest possible absolute
   session lifetime plus deployment overlap and backup retention.
7. Retain old risk keys through the configured risk-retention deadline.
8. Remove a retiring key only after evidence shows it is no longer needed.

Deleting a required key does not log users in with empty state. Reads fail
closed with a bounded error that does not echo ciphertext, tokens, or key
material.

## Recovery

Restore the durable database and the matching historical keyring as one
recovery unit. Restoring only the database produces unavailable-key failures.
Restoring only keys cannot reconstruct session state. If a key may be
compromised, revoke affected sessions and require authentication rather than
silently accepting unauthenticated state.
