From 0c75c562c5da0a694371b6dcc87932c223599220 Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Tue, 16 Jun 2026 19:13:10 -0700 Subject: [PATCH 1/3] fix: normalize default SOURCEBOT_ENCRYPTION_KEY to 32 characters The default SOURCEBOT_ENCRYPTION_KEY in docker-compose is 33 zeros, which fails the 32-character (AES-256) length validation. Preprocess the value so the all-zeros default is trimmed to 32 characters before validation. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/shared/src/env.server.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/shared/src/env.server.ts b/packages/shared/src/env.server.ts index 4b8d7d8ef..48e150aba 100644 --- a/packages/shared/src/env.server.ts +++ b/packages/shared/src/env.server.ts @@ -341,9 +341,18 @@ const options = { // The key is read as ASCII (1 char = 1 byte), so AES-256's 32-byte key // requirement means this must be exactly 32 characters. Generate one with // `openssl rand -base64 24` (24 random bytes => a 32-character base64 string). - SOURCEBOT_ENCRYPTION_KEY: z.string().length(32, { - message: "SOURCEBOT_ENCRYPTION_KEY must be exactly 32 characters (a 256-bit AES key). Generate one with `openssl rand -base64 24`.", - }), + SOURCEBOT_ENCRYPTION_KEY: z.preprocess( + // @hack in our docker-compose.yml, we mistakenly used a + // encryption key with _33_ zeros. As a hacky mechanism to + // fix peoples deployments without requiring them to update + // their encryption key, we look for keys with this pattern + // and coerce them into _32_ zeros. + // @see https://github.com/sourcebot-dev/sourcebot/commit/e30e75e7af96308b3b063bb3aed8369f5b15aa2e + (value) => value === "0".repeat(33) ? "0".repeat(32) : value, + z.string().length(32, { + message: "SOURCEBOT_ENCRYPTION_KEY must be exactly 32 characters (a 256-bit AES key). Generate one with `openssl rand -base64 24`.", + }), + ), SOURCEBOT_INSTALL_ID: z.string().default("unknown"), SOURCEBOT_LIGHTHOUSE_URL: z.string().url().default("https://deployments.sourcebot.dev"), From 1ba8c5380c49d2bd46497b12525e1bc7e727d9cb Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Tue, 16 Jun 2026 19:13:32 -0700 Subject: [PATCH 2/3] docs: add CHANGELOG entry for SOURCEBOT_ENCRYPTION_KEY fix Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84d4ccb25..52cd095e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Validated that `SOURCEBOT_ENCRYPTION_KEY` is exactly 32 characters at startup, failing fast with an actionable message instead of a runtime encryption error. [#1305](https://github.com/sourcebot-dev/sourcebot/pull/1305) - Fixed the web UI crashing when anonymous access is enabled and a request omits the `User-Agent` header (e.g. proxy or health-check probes). [#1309](https://github.com/sourcebot-dev/sourcebot/pull/1309) - Fixed the Members page crashing when a `User` had a null email. `User.email` is now required (with a backfilling migration), and SSO sign-ins without an email are rejected. [#1310](https://github.com/sourcebot-dev/sourcebot/pull/1310) +- Fixed the default `SOURCEBOT_ENCRYPTION_KEY` (33 zeros) failing the 32-character validation by normalizing it to 32 characters at startup. [#1311](https://github.com/sourcebot-dev/sourcebot/pull/1311) ## [5.0.2] - 2026-06-11 From 45aac21b6a606fa4bd61b62cbdfd214d68f0c538 Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Tue, 16 Jun 2026 19:18:46 -0700 Subject: [PATCH 3/3] refactor: move encryption key coercion into crypto.ts Move the 33-zeros -> 32-zeros default key normalization out of the env schema and into encrypt()/decrypt() in crypto.ts, where the key is actually used. SOURCEBOT_ENCRYPTION_KEY is now a plain string in the env schema. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/shared/src/crypto.ts | 13 +++++++++++-- packages/shared/src/env.server.ts | 14 ++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/shared/src/crypto.ts b/packages/shared/src/crypto.ts index fbb4be79b..f9006dbe0 100644 --- a/packages/shared/src/crypto.ts +++ b/packages/shared/src/crypto.ts @@ -15,8 +15,17 @@ const generateIV = (): Buffer => { return crypto.randomBytes(ivLength); }; +// @hack in our docker-compose.yml, we mistakenly used an encryption key with +// _33_ zeros. As a hacky mechanism to fix peoples deployments without requiring +// them to update their encryption key, we look for keys with this pattern and +// coerce them into _32_ zeros (AES-256 requires a 32-byte key). +// @see https://github.com/sourcebot-dev/sourcebot/commit/e30e75e7af96308b3b063bb3aed8369f5b15aa2e +const coerceEncryptionKey = (key: string): string => { + return key === "0".repeat(33) ? "0".repeat(32) : key; +}; + export function encrypt(text: string): { iv: string; encryptedData: string } { - const encryptionKey = Buffer.from(env.SOURCEBOT_ENCRYPTION_KEY, 'ascii'); + const encryptionKey = Buffer.from(coerceEncryptionKey(env.SOURCEBOT_ENCRYPTION_KEY), 'ascii'); const iv = generateIV(); const cipher = crypto.createCipheriv(algorithm, encryptionKey, iv); @@ -28,7 +37,7 @@ export function encrypt(text: string): { iv: string; encryptedData: string } { } export function decrypt(iv: string, encryptedText: string): string { - const encryptionKey = Buffer.from(env.SOURCEBOT_ENCRYPTION_KEY, 'ascii'); + const encryptionKey = Buffer.from(coerceEncryptionKey(env.SOURCEBOT_ENCRYPTION_KEY), 'ascii'); const ivBuffer = Buffer.from(iv, 'hex'); const encryptedBuffer = Buffer.from(encryptedText, 'hex'); diff --git a/packages/shared/src/env.server.ts b/packages/shared/src/env.server.ts index 48e150aba..7c5473706 100644 --- a/packages/shared/src/env.server.ts +++ b/packages/shared/src/env.server.ts @@ -341,18 +341,8 @@ const options = { // The key is read as ASCII (1 char = 1 byte), so AES-256's 32-byte key // requirement means this must be exactly 32 characters. Generate one with // `openssl rand -base64 24` (24 random bytes => a 32-character base64 string). - SOURCEBOT_ENCRYPTION_KEY: z.preprocess( - // @hack in our docker-compose.yml, we mistakenly used a - // encryption key with _33_ zeros. As a hacky mechanism to - // fix peoples deployments without requiring them to update - // their encryption key, we look for keys with this pattern - // and coerce them into _32_ zeros. - // @see https://github.com/sourcebot-dev/sourcebot/commit/e30e75e7af96308b3b063bb3aed8369f5b15aa2e - (value) => value === "0".repeat(33) ? "0".repeat(32) : value, - z.string().length(32, { - message: "SOURCEBOT_ENCRYPTION_KEY must be exactly 32 characters (a 256-bit AES key). Generate one with `openssl rand -base64 24`.", - }), - ), + // @note: the key is normalized in shared/src/crypto.ts before use. + SOURCEBOT_ENCRYPTION_KEY: z.string(), SOURCEBOT_INSTALL_ID: z.string().default("unknown"), SOURCEBOT_LIGHTHOUSE_URL: z.string().url().default("https://deployments.sourcebot.dev"),