Problem
`ENCRYPTION_KEY` (AES-256-GCM, 32 chars) encrypts every connector credential at rest — API keys, OAuth tokens, database passwords. There is currently no built-in way to rotate it.
Simply swapping the value in `.env` and restarting bricks every connector: existing ciphertext was sealed with the old key, decryption fails, and every tool invocation throws.
PR #178 documents a manual workaround in `SECURITY.md`:
- Take a database backup
- Export every connector config to JSON (decrypted in transit)
- Generate a new key
- Wipe the encrypted columns / restore from backup without secrets
- Re-import each connector with the new key
This is fine for installations with a handful of connectors but is operationally painful (and risky — step 2 puts decrypted secrets on disk) for production deployments with dozens of connectors.
Proposal
Ship a one-shot rotation command:
```bash
docker compose exec app node packages/backend/dist/scripts/rotate-encryption-key.js \
--old "$OLD_ENCRYPTION_KEY" --new "$NEW_ENCRYPTION_KEY"
```
The script should:
- Run inside a single database transaction (so partial failures roll back, no half-rotated state)
- Iterate over every row in tables that hold AES-256-GCM ciphertext (connector credentials, OAuth tokens, per-user MCP API keys if any are encrypted)
- Decrypt with the old key, re-encrypt with the new key, update the row
- Refuse to run if either key is the wrong length or if the old key fails to decrypt the first row (early-fail rather than silently corrupting the table)
- Print a summary at the end: `X rows re-encrypted across Y tables`
The crypto primitive already exists at `packages/backend/src/common/crypto/encryption.util.ts` — the rotation command is a thin wrapper that walks the schema.
Acceptance criteria
Related
Problem
`ENCRYPTION_KEY` (AES-256-GCM, 32 chars) encrypts every connector credential at rest — API keys, OAuth tokens, database passwords. There is currently no built-in way to rotate it.
Simply swapping the value in `.env` and restarting bricks every connector: existing ciphertext was sealed with the old key, decryption fails, and every tool invocation throws.
PR #178 documents a manual workaround in `SECURITY.md`:
This is fine for installations with a handful of connectors but is operationally painful (and risky — step 2 puts decrypted secrets on disk) for production deployments with dozens of connectors.
Proposal
Ship a one-shot rotation command:
```bash
docker compose exec app node packages/backend/dist/scripts/rotate-encryption-key.js \
--old "$OLD_ENCRYPTION_KEY" --new "$NEW_ENCRYPTION_KEY"
```
The script should:
The crypto primitive already exists at `packages/backend/src/common/crypto/encryption.util.ts` — the rotation command is a thin wrapper that walks the schema.
Acceptance criteria
Related