BaryoDev / BaryoDev/barakoCMS

Security: a key ring for stored secrets, with a key id in the envelope and rotation

Open
#900 0 comments 0 reactions 0 assignees View on GitHub
core design security
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.