Repository navigation
Support AGENT_SECRETS_KEY rotation with a keyring and rewrap command - #534
Closed
arseniycodes wants to merge 1 commit into
Closed
arseniycodes wants to merge 1 commit into
arseniycodes wants to merge 1 commit into
Conversation
AGENT_SECRETS_KEY now accepts a keyring, "<version>:<base64>,<version>:<base64>", in addition to the single-key format (which stays version 1). The first entry encrypts new values; the rest are decrypt-only. Parsing validates versions (unique integers >= 1) and key length without printing key material. Decryption looks up each row's recorded key version, so a deployment can move to a new key while existing rows remain readable. Callers that only checked whether the variable was set now validate it through the shared parser, and the read-side "version missing means 1" fallbacks use a named constant. Adds `pnpm --filter @superlog/db credentials:rewrap [--dry-run]`, which re-encrypts every envelope that is not on the current version. The column registry covers all 28 encrypted columns across 17 tables, and a test fails if a *_key_version column in the schema is missing from it. Updates are guarded on id, old version and old ciphertext so concurrent writers win; the command is idempotent, reports per-column counts, and exits non-zero on failures or leftover rows.
Contributor
Author
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Contributor
Author
|
Closing: not needed for now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
AGENT_SECRETS_KEYcould not be rotated: the cipher only knew version 1, so changing the value made every stored integration credential undecryptable. This adds a keyring format in the same variable and a command that re-encrypts stored values.Key format (same env var, no new config):
"<version>:<base64>,<version>:<base64>". The first entry encrypts new values; the rest only decrypt.Validation rejects empty values, non-integer or out-of-range versions, duplicate versions, malformed entries, and keys that do not decode to 32 bytes. Error messages name the entry position and version, never the key. The parsed keyring keeps key bytes in a private field so logging or serializing it prints no key material.
Encryption/decryption:
encryptIntegrationSecretwrites with the current version;decryptIntegrationSecretuses the row's recorded version. Existing version-1 rows keep working under both formats.Validation in every caller: places that only checked that the variable was set (system capabilities, GCP config, Render routes, the Render and GCP worker jobs) now validate it through the shared parser. Env-object callers use
isValidIntegrationSecretKeyring(raw), which takes noprocess.envfallback. The read-side?? 1fallbacks for envelopes without a recorded version now useLEGACY_INTEGRATION_SECRET_KEY_VERSION. Every write site already storescipher.keyVersion, so the schema.default(1)values are never what a write relies on.Rewrap command:
pnpm --filter @superlog/db credentials:rewrap [--dry-run] [--batch-size=N]integration-secret-columns.tslists all 28 encrypted columns across 17 tables. That includes nullable envelopes and the Sentry columns that store the nonce as base64 text.id, the old key version, the old ciphertext and the old nonce. If another writer changed the row in the meantime, the update does nothing and is counted as a conflict.--dry-rundecrypts every pending row without writing, so rows that would fail show up before any old key is removed.Rotation sequence for operators
1:<old>,2:<new>first if you need zero decrypt errors during a rolling deploy.2:<new>,1:<old>to every service that reads the key.credentials:rewrap --dry-run, thencredentials:rewrap. The run must finish withremaining: 0andfailed: 0.2:<new>.Tests
integration-secrets.test.ts: keyring parsing and validation, no key material in errors or inspect/JSON output, encrypting with the current version and decrypting old versions, legacy single key reading keyring-written v1 rows, a clear error when a version is missing, and a wrong key under a version number failing authentication.integration-secret-rewrap.test.ts(PGlite):*_key_versioncolumn in the schema that is not in the registry;Validation run locally:
pnpm --filter @superlog/db test(273 pass)pnpm -r typecheckSummary by cubic
Supports rotating
AGENT_SECRETS_KEY: the variable now accepts a keyring format and a rewrap command re-encrypts stored credentials. Previously the cipher only knew version 1, so changing the key made every stored integration credential undecryptable.AGENT_SECRETS_KEYnow accepts"<version>:<base64>,<version>:<base64>"in addition to the single-key format (still version 1). The first entry encrypts new values; later entries only decrypt. Decryption follows each row's recorded version, so existing version-1 rows keep working, and callers that only checked the variable was set now validate it. Invalid keys are rejected without echoing key material.Rewrap command
pnpm --filter @superlog/db credentials:rewrap [--dry-run] [--batch-size=N]re-encrypts every envelope not on the current version across all 28 encrypted columns.Migration
1:<old>,2:<new>first to avoid decrypt errors during a rolling deploy.2:<new>,1:<old>everywhere, run a dry-run then a real rewrap, and confirmremaining: 0andfailed: 0.2:<new>and drop the old entry.Written for commit 8ddd71e. Summary will update on new commits.