Security: a key ring for stored secrets, with a key id in the envelope and rotation
- Dominant language
- C#
- Stars
- 6
- Forks
- 7
- Avg merge
- 4h 42m
- Merged PRs (30d)
- 307
Description
Stored secrets are encrypted under one key each, with no key id and no rotation. `barakoCMS/Infrastructure/Security/SecretProtector.cs:38-43` derives its key from `Secrets:Key`, falling back to `JWT:Key`. `barakoCMS/Infrastructure/Connectors/ConnectorSecretProtector.cs:41-42` uses `Connectors:Key` and `barakoCMS/Infrastructure/Auth/Mfa/MfaSecretProtector.cs:24-28` uses `Mfa:Key`, also falling back to `JWT:Key`. When a value does not decrypt, `SecretProtector.Unprotect` returns null (`:48-58`), and a connector only finds out at send time as a missing credential (`barakoCMS/Infrastructure/Connectors/ConnectorSender.cs:200-201`).
### Why it matters
Rotating the JWT key after a leak, restoring a backup onto a server with a different key, or moving a client to another box silently loses every stored workflow credential, connector secret and MFA enrolment. Nothing fails at startup and nothing names what was lost. #880 adds an `enc:v1:` prefix and #785 loads keys from a vault, but #785 says rotation still breaks existing values.
### The general concept
A key ring: several keys, each with an id, one marked current. The envelope carries the key id, so a value decrypts under whichever key wrote it and new writes use the current key. A background re-wrap moves old values to the current key, and startup reports how many stored values no key in the ring can open. `JWT:Key` stops being a fallback for stored secrets once a ring is configured.
### Where it lives
Core: security, connectors and MFA.
### Compatibility
Released, and it changes how secrets are stored. Values without a key id are read with the existing single key, which becomes the first entry in the ring, so nothing needs migrating up front; the re-wrap job rewrites them over time. Builds on the `enc:v1:` prefix from #880. No HTTP change.
### Done when
- A secret written under key A still decrypts after key B becomes current, and a test showing that fails before the change.
- After the re-wrap job runs, every stored value names the current key.
- Startup reports a count of values no key in the ring decrypts.
Found in the architecture sweep of 15 September 2026.
Contributor guide
Research direction
Start with SecretProtector.cs, ConnectorSecretProtector.cs, and MfaSecretProtector.cs to understand the existing key derivation and envelope handling, then trace the failure path in ConnectorSender.cs. The work is done when old-key values remain readable, re-wrapping assigns the current key id to every stored value, and startup reports values no key can decrypt.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100