fix(preview): point preview lambdas at the branch RDS, not DBInstances[0] - #316

Merged
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host
Aug 12, 2026
Merged

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0]#316
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

The bug

Auth failed in every preview environment while prod was fine. Multiple devs hit it. Symptom from the UI: login appears to work, then hangs ~11s and errors out.

.github/workflows/preview-env.yml resolved the preview lambdas' DB_HOST like this:

DB_HOST=$(aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address" --output text)

This account hosts several unrelated C4C databases and the branch instance is not first:

#instance
0bhchp-postgres ← what previews got
1c4c-hats
2fcc-postgres
3terraform-2025…001 ← the actual branch DB (prod uses this)

So all 12 preview lambdas across PRs #302 and #310 pointed at another project's database, whose security group blackholes our traffic. Every query hit the 5s connectionTimeoutMillis in db.ts and threw.

Why it looked like an auth bug

authenticateRequest wrapped both the JWT verification and the branch.users lookup in one try/catch that returned isAuthenticated: false. A dead database therefore surfaced as 401 Authentication required.

That is also actively harmful beyond the bad diagnostics: the frontend treats 401 as an expired session, so authedFetch refreshes, retries, then calls endSession()a database blip logs the user out.

Login and refresh kept working throughout because they only talk to Cognito and never touch the DB, which is exactly why this looked like "auth is broken" rather than "the DB is unreachable."

Evidence

Against the PR-310 preview API, with a valid token:

probebeforeafter
/auth/me, real token401 in 5.06s / 5.12s / 5.58s200 in 1.46s, correct user
/auth/me, forged signature (real kid)401 in 165msunchanged
nc to bhchp-postgres:5432hangs (blackholed)n/a
nc to branch RDS:54320.27sn/a

The forged-token probe is what isolated it: JWKS fetch and JWT verification were healthy and failed fast, so the 5s had to come from the line after verify(). aws-jwt-verify's own timeouts (1500ms socket / 3000ms response) never matched 5.06s; connectionTimeoutMillis: 5000 did, exactly.

The fix

  1. preview-env.yml — stop re-deriving DB_HOST. The prod lambda config the workflow already reads carries the right value, so inherit it like every other DB credential. Added a guard that fails the job if DB_HOST / DB creds / Cognito ids are missing, instead of silently shipping a preview that 401s on every call.
  2. shared/lambda-auth/src/authenticate.ts — only catch token verification. DB errors and a missing COGNITO_USER_POOL_ID now propagate into the handlers' existing 500 mapping. No handler changes needed; they all already have an outer catchjson(500, …).
  3. Tests flipped to assert the new behavior. Two of them previously documented this footgun as intended — including the comment "This is why a missing env var manifests as blanket silent 401s across all six lambdas rather than a loud 500."

Already applied out of band

The workflow only resolves lambda env on label-add, so the 12 already-created preview lambdas kept the bad DB_HOST regardless of this change. I repatched all of them to the branch RDS, which is what infrastructure/preview/variables.tf already documents as intended ("Preview envs deliberately reuse the shared RDS + Cognito pool"). PR-310's preview is working now — verified with the 200 above.

Verification

  • shared/lambda-auth: npx jest42/42 pass; npx tsc --noEmit clean.
  • apps/backend/lambdas/auth: 67 unit tests pass. 3 auth.e2e.test.ts failures are pre-existing — they need a lambda on localhost:3000 and fail identically on clean main.
  • Live: /auth/me on the PR-310 preview returns 200 with the correct user record.

Follow-ups not in scope here

  • Preview envs share the prod database and Cognito pool. That's a deliberate, documented tradeoff, but it means a preview can write to prod data.
  • apps/backend/lambdas/*/db.ts duplicates the same Pool config six times, so the 5s timeout and TLS rules have to be kept in sync by hand.

🤖 Generated with Claude Code

github-actionsBot added a commit that referenced this pull request Aug 12, 2026
…s[0]
Preview environments 401'd on every authenticated request while prod was fine.
preview-env.yml resolved the preview lambdas' DB_HOST with
`aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address"`.
This account hosts several unrelated C4C databases, and the branch instance is
not first, so every preview lambda was pointed at `bhchp-postgres` — another
project's DB, whose security group blackholes our traffic. Each query hit the
5s `connectionTimeoutMillis` in db.ts and threw.
The prod lambda config the workflow already reads carries the correct DB_HOST,
so stop re-deriving it and inherit it like every other DB credential. Added a
guard that fails the job if any required key is missing, rather than shipping a
preview that 401s on every call.
Second half of the bug: authenticateRequest wrapped both the JWT check and the
branch.users lookup in one try/catch that returned `isAuthenticated: false`, so
an unreachable database surfaced as 401 "Authentication required". That hid the
real fault and, because the frontend treats 401 as an expired session, cleared
the user's tokens and logged them out on a DB blip. Only token verification is
caught now; DB errors and a missing COGNITO_USER_POOL_ID propagate to the
handlers' existing 500 mapping.
Existing preview stacks were repaired out of band — the workflow only resolves
lambda env on label-add, so already-created stacks kept the bad DB_HOST.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nourshoreibah
nourshoreibahforce-pushed the worktree-fix-preview-db-host branch from c034de6 to a7c9513CompareAugust 12, 2026 02:17
@nourshoreibahnourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open:https://d3nmtjoh6ir9ym.cloudfront.net/pr-316/
API:https://dr79itqqoj.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

@nourshoreibahnourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
github-actionsBot added a commit that referenced this pull request Aug 12, 2026
@nourshoreibah
nourshoreibah merged commit 0403171 into mainAug 12, 2026
24 checks passed
@nourshoreibah
nourshoreibah deleted the worktree-fix-preview-db-host branch August 12, 2026 02:23
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-reviewThe PR review bot won't runtest-environmentCreates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nourshoreibah
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0] - #316

Merged
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host
Aug 12, 2026
Merged

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0]#316
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

The bug

Auth failed in every preview environment while prod was fine. Multiple devs hit it. Symptom from the UI: login appears to work, then hangs ~11s and errors out.

.github/workflows/preview-env.yml resolved the preview lambdas' DB_HOST like this:

DB_HOST=$(aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address" --output text)

This account hosts several unrelated C4C databases and the branch instance is not first:

#instance
0bhchp-postgres ← what previews got
1c4c-hats
2fcc-postgres
3terraform-2025…001 ← the actual branch DB (prod uses this)

So all 12 preview lambdas across PRs #302 and #310 pointed at another project's database, whose security group blackholes our traffic. Every query hit the 5s connectionTimeoutMillis in db.ts and threw.

Why it looked like an auth bug

authenticateRequest wrapped both the JWT verification and the branch.users lookup in one try/catch that returned isAuthenticated: false. A dead database therefore surfaced as 401 Authentication required.

That is also actively harmful beyond the bad diagnostics: the frontend treats 401 as an expired session, so authedFetch refreshes, retries, then calls endSession()a database blip logs the user out.

Login and refresh kept working throughout because they only talk to Cognito and never touch the DB, which is exactly why this looked like "auth is broken" rather than "the DB is unreachable."

Evidence

Against the PR-310 preview API, with a valid token:

probebeforeafter
/auth/me, real token401 in 5.06s / 5.12s / 5.58s200 in 1.46s, correct user
/auth/me, forged signature (real kid)401 in 165msunchanged
nc to bhchp-postgres:5432hangs (blackholed)n/a
nc to branch RDS:54320.27sn/a

The forged-token probe is what isolated it: JWKS fetch and JWT verification were healthy and failed fast, so the 5s had to come from the line after verify(). aws-jwt-verify's own timeouts (1500ms socket / 3000ms response) never matched 5.06s; connectionTimeoutMillis: 5000 did, exactly.

The fix

  1. preview-env.yml — stop re-deriving DB_HOST. The prod lambda config the workflow already reads carries the right value, so inherit it like every other DB credential. Added a guard that fails the job if DB_HOST / DB creds / Cognito ids are missing, instead of silently shipping a preview that 401s on every call.
  2. shared/lambda-auth/src/authenticate.ts — only catch token verification. DB errors and a missing COGNITO_USER_POOL_ID now propagate into the handlers' existing 500 mapping. No handler changes needed; they all already have an outer catchjson(500, …).
  3. Tests flipped to assert the new behavior. Two of them previously documented this footgun as intended — including the comment "This is why a missing env var manifests as blanket silent 401s across all six lambdas rather than a loud 500."

Already applied out of band

The workflow only resolves lambda env on label-add, so the 12 already-created preview lambdas kept the bad DB_HOST regardless of this change. I repatched all of them to the branch RDS, which is what infrastructure/preview/variables.tf already documents as intended ("Preview envs deliberately reuse the shared RDS + Cognito pool"). PR-310's preview is working now — verified with the 200 above.

Verification

  • shared/lambda-auth: npx jest42/42 pass; npx tsc --noEmit clean.
  • apps/backend/lambdas/auth: 67 unit tests pass. 3 auth.e2e.test.ts failures are pre-existing — they need a lambda on localhost:3000 and fail identically on clean main.
  • Live: /auth/me on the PR-310 preview returns 200 with the correct user record.

Follow-ups not in scope here

  • Preview envs share the prod database and Cognito pool. That's a deliberate, documented tradeoff, but it means a preview can write to prod data.
  • apps/backend/lambdas/*/db.ts duplicates the same Pool config six times, so the 5s timeout and TLS rules have to be kept in sync by hand.

🤖 Generated with Claude Code

github-actionsBot added a commit that referenced this pull request Aug 12, 2026
…s[0]
Preview environments 401'd on every authenticated request while prod was fine.
preview-env.yml resolved the preview lambdas' DB_HOST with
`aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address"`.
This account hosts several unrelated C4C databases, and the branch instance is
not first, so every preview lambda was pointed at `bhchp-postgres` — another
project's DB, whose security group blackholes our traffic. Each query hit the
5s `connectionTimeoutMillis` in db.ts and threw.
The prod lambda config the workflow already reads carries the correct DB_HOST,
so stop re-deriving it and inherit it like every other DB credential. Added a
guard that fails the job if any required key is missing, rather than shipping a
preview that 401s on every call.
Second half of the bug: authenticateRequest wrapped both the JWT check and the
branch.users lookup in one try/catch that returned `isAuthenticated: false`, so
an unreachable database surfaced as 401 "Authentication required". That hid the
real fault and, because the frontend treats 401 as an expired session, cleared
the user's tokens and logged them out on a DB blip. Only token verification is
caught now; DB errors and a missing COGNITO_USER_POOL_ID propagate to the
handlers' existing 500 mapping.
Existing preview stacks were repaired out of band — the workflow only resolves
lambda env on label-add, so already-created stacks kept the bad DB_HOST.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nourshoreibah
nourshoreibahforce-pushed the worktree-fix-preview-db-host branch from c034de6 to a7c9513CompareAugust 12, 2026 02:17
@nourshoreibahnourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open:https://d3nmtjoh6ir9ym.cloudfront.net/pr-316/
API:https://dr79itqqoj.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

@nourshoreibahnourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
github-actionsBot added a commit that referenced this pull request Aug 12, 2026
@nourshoreibah
nourshoreibah merged commit 0403171 into mainAug 12, 2026
24 checks passed
@nourshoreibah
nourshoreibah deleted the worktree-fix-preview-db-host branch August 12, 2026 02:23
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-reviewThe PR review bot won't runtest-environmentCreates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nourshoreibah
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0] - #316

Merged
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host
Aug 12, 2026
Merged

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0]#316
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

The bug

Auth failed in every preview environment while prod was fine. Multiple devs hit it. Symptom from the UI: login appears to work, then hangs ~11s and errors out.

.github/workflows/preview-env.yml resolved the preview lambdas' DB_HOST like this:

DB_HOST=$(aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address" --output text)

This account hosts several unrelated C4C databases and the branch instance is not first:

#instance
0bhchp-postgres ← what previews got
1c4c-hats
2fcc-postgres
3terraform-2025…001 ← the actual branch DB (prod uses this)

So all 12 preview lambdas across PRs #302 and #310 pointed at another project's database, whose security group blackholes our traffic. Every query hit the 5s connectionTimeoutMillis in db.ts and threw.

Why it looked like an auth bug

authenticateRequest wrapped both the JWT verification and the branch.users lookup in one try/catch that returned isAuthenticated: false. A dead database therefore surfaced as 401 Authentication required.

That is also actively harmful beyond the bad diagnostics: the frontend treats 401 as an expired session, so authedFetch refreshes, retries, then calls endSession()a database blip logs the user out.

Login and refresh kept working throughout because they only talk to Cognito and never touch the DB, which is exactly why this looked like "auth is broken" rather than "the DB is unreachable."

Evidence

Against the PR-310 preview API, with a valid token:

probebeforeafter
/auth/me, real token401 in 5.06s / 5.12s / 5.58s200 in 1.46s, correct user
/auth/me, forged signature (real kid)401 in 165msunchanged
nc to bhchp-postgres:5432hangs (blackholed)n/a
nc to branch RDS:54320.27sn/a

The forged-token probe is what isolated it: JWKS fetch and JWT verification were healthy and failed fast, so the 5s had to come from the line after verify(). aws-jwt-verify's own timeouts (1500ms socket / 3000ms response) never matched 5.06s; connectionTimeoutMillis: 5000 did, exactly.

The fix

  1. preview-env.yml — stop re-deriving DB_HOST. The prod lambda config the workflow already reads carries the right value, so inherit it like every other DB credential. Added a guard that fails the job if DB_HOST / DB creds / Cognito ids are missing, instead of silently shipping a preview that 401s on every call.
  2. shared/lambda-auth/src/authenticate.ts — only catch token verification. DB errors and a missing COGNITO_USER_POOL_ID now propagate into the handlers' existing 500 mapping. No handler changes needed; they all already have an outer catchjson(500, …).
  3. Tests flipped to assert the new behavior. Two of them previously documented this footgun as intended — including the comment "This is why a missing env var manifests as blanket silent 401s across all six lambdas rather than a loud 500."

Already applied out of band

The workflow only resolves lambda env on label-add, so the 12 already-created preview lambdas kept the bad DB_HOST regardless of this change. I repatched all of them to the branch RDS, which is what infrastructure/preview/variables.tf already documents as intended ("Preview envs deliberately reuse the shared RDS + Cognito pool"). PR-310's preview is working now — verified with the 200 above.

Verification

  • shared/lambda-auth: npx jest42/42 pass; npx tsc --noEmit clean.
  • apps/backend/lambdas/auth: 67 unit tests pass. 3 auth.e2e.test.ts failures are pre-existing — they need a lambda on localhost:3000 and fail identically on clean main.
  • Live: /auth/me on the PR-310 preview returns 200 with the correct user record.

Follow-ups not in scope here

  • Preview envs share the prod database and Cognito pool. That's a deliberate, documented tradeoff, but it means a preview can write to prod data.
  • apps/backend/lambdas/*/db.ts duplicates the same Pool config six times, so the 5s timeout and TLS rules have to be kept in sync by hand.

🤖 Generated with Claude Code

github-actionsBot added a commit that referenced this pull request Aug 12, 2026
…s[0]
Preview environments 401'd on every authenticated request while prod was fine.
preview-env.yml resolved the preview lambdas' DB_HOST with
`aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address"`.
This account hosts several unrelated C4C databases, and the branch instance is
not first, so every preview lambda was pointed at `bhchp-postgres` — another
project's DB, whose security group blackholes our traffic. Each query hit the
5s `connectionTimeoutMillis` in db.ts and threw.
The prod lambda config the workflow already reads carries the correct DB_HOST,
so stop re-deriving it and inherit it like every other DB credential. Added a
guard that fails the job if any required key is missing, rather than shipping a
preview that 401s on every call.
Second half of the bug: authenticateRequest wrapped both the JWT check and the
branch.users lookup in one try/catch that returned `isAuthenticated: false`, so
an unreachable database surfaced as 401 "Authentication required". That hid the
real fault and, because the frontend treats 401 as an expired session, cleared
the user's tokens and logged them out on a DB blip. Only token verification is
caught now; DB errors and a missing COGNITO_USER_POOL_ID propagate to the
handlers' existing 500 mapping.
Existing preview stacks were repaired out of band — the workflow only resolves
lambda env on label-add, so already-created stacks kept the bad DB_HOST.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nourshoreibah
nourshoreibahforce-pushed the worktree-fix-preview-db-host branch from c034de6 to a7c9513CompareAugust 12, 2026 02:17
@nourshoreibahnourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open:https://d3nmtjoh6ir9ym.cloudfront.net/pr-316/
API:https://dr79itqqoj.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

@nourshoreibahnourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
github-actionsBot added a commit that referenced this pull request Aug 12, 2026
@nourshoreibah
nourshoreibah merged commit 0403171 into mainAug 12, 2026
24 checks passed
@nourshoreibah
nourshoreibah deleted the worktree-fix-preview-db-host branch August 12, 2026 02:23
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-reviewThe PR review bot won't runtest-environmentCreates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nourshoreibah
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0] - #316

Merged
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host
Aug 12, 2026
Merged

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0]#316
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

The bug

Auth failed in every preview environment while prod was fine. Multiple devs hit it. Symptom from the UI: login appears to work, then hangs ~11s and errors out.

.github/workflows/preview-env.yml resolved the preview lambdas' DB_HOST like this:

DB_HOST=$(aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address" --output text)

This account hosts several unrelated C4C databases and the branch instance is not first:

#instance
0bhchp-postgres ← what previews got
1c4c-hats
2fcc-postgres
3terraform-2025…001 ← the actual branch DB (prod uses this)

So all 12 preview lambdas across PRs #302 and #310 pointed at another project's database, whose security group blackholes our traffic. Every query hit the 5s connectionTimeoutMillis in db.ts and threw.

Why it looked like an auth bug

authenticateRequest wrapped both the JWT verification and the branch.users lookup in one try/catch that returned isAuthenticated: false. A dead database therefore surfaced as 401 Authentication required.

That is also actively harmful beyond the bad diagnostics: the frontend treats 401 as an expired session, so authedFetch refreshes, retries, then calls endSession()a database blip logs the user out.

Login and refresh kept working throughout because they only talk to Cognito and never touch the DB, which is exactly why this looked like "auth is broken" rather than "the DB is unreachable."

Evidence

Against the PR-310 preview API, with a valid token:

probebeforeafter
/auth/me, real token401 in 5.06s / 5.12s / 5.58s200 in 1.46s, correct user
/auth/me, forged signature (real kid)401 in 165msunchanged
nc to bhchp-postgres:5432hangs (blackholed)n/a
nc to branch RDS:54320.27sn/a

The forged-token probe is what isolated it: JWKS fetch and JWT verification were healthy and failed fast, so the 5s had to come from the line after verify(). aws-jwt-verify's own timeouts (1500ms socket / 3000ms response) never matched 5.06s; connectionTimeoutMillis: 5000 did, exactly.

The fix

  1. preview-env.yml — stop re-deriving DB_HOST. The prod lambda config the workflow already reads carries the right value, so inherit it like every other DB credential. Added a guard that fails the job if DB_HOST / DB creds / Cognito ids are missing, instead of silently shipping a preview that 401s on every call.
  2. shared/lambda-auth/src/authenticate.ts — only catch token verification. DB errors and a missing COGNITO_USER_POOL_ID now propagate into the handlers' existing 500 mapping. No handler changes needed; they all already have an outer catchjson(500, …).
  3. Tests flipped to assert the new behavior. Two of them previously documented this footgun as intended — including the comment "This is why a missing env var manifests as blanket silent 401s across all six lambdas rather than a loud 500."

Already applied out of band

The workflow only resolves lambda env on label-add, so the 12 already-created preview lambdas kept the bad DB_HOST regardless of this change. I repatched all of them to the branch RDS, which is what infrastructure/preview/variables.tf already documents as intended ("Preview envs deliberately reuse the shared RDS + Cognito pool"). PR-310's preview is working now — verified with the 200 above.

Verification

  • shared/lambda-auth: npx jest42/42 pass; npx tsc --noEmit clean.
  • apps/backend/lambdas/auth: 67 unit tests pass. 3 auth.e2e.test.ts failures are pre-existing — they need a lambda on localhost:3000 and fail identically on clean main.
  • Live: /auth/me on the PR-310 preview returns 200 with the correct user record.

Follow-ups not in scope here

  • Preview envs share the prod database and Cognito pool. That's a deliberate, documented tradeoff, but it means a preview can write to prod data.
  • apps/backend/lambdas/*/db.ts duplicates the same Pool config six times, so the 5s timeout and TLS rules have to be kept in sync by hand.

🤖 Generated with Claude Code

github-actionsBot added a commit that referenced this pull request Aug 12, 2026
…s[0]
Preview environments 401'd on every authenticated request while prod was fine.
preview-env.yml resolved the preview lambdas' DB_HOST with
`aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address"`.
This account hosts several unrelated C4C databases, and the branch instance is
not first, so every preview lambda was pointed at `bhchp-postgres` — another
project's DB, whose security group blackholes our traffic. Each query hit the
5s `connectionTimeoutMillis` in db.ts and threw.
The prod lambda config the workflow already reads carries the correct DB_HOST,
so stop re-deriving it and inherit it like every other DB credential. Added a
guard that fails the job if any required key is missing, rather than shipping a
preview that 401s on every call.
Second half of the bug: authenticateRequest wrapped both the JWT check and the
branch.users lookup in one try/catch that returned `isAuthenticated: false`, so
an unreachable database surfaced as 401 "Authentication required". That hid the
real fault and, because the frontend treats 401 as an expired session, cleared
the user's tokens and logged them out on a DB blip. Only token verification is
caught now; DB errors and a missing COGNITO_USER_POOL_ID propagate to the
handlers' existing 500 mapping.
Existing preview stacks were repaired out of band — the workflow only resolves
lambda env on label-add, so already-created stacks kept the bad DB_HOST.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nourshoreibah
nourshoreibahforce-pushed the worktree-fix-preview-db-host branch from c034de6 to a7c9513CompareAugust 12, 2026 02:17
@nourshoreibahnourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open:https://d3nmtjoh6ir9ym.cloudfront.net/pr-316/
API:https://dr79itqqoj.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

@nourshoreibahnourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
github-actionsBot added a commit that referenced this pull request Aug 12, 2026
@nourshoreibah
nourshoreibah merged commit 0403171 into mainAug 12, 2026
24 checks passed
@nourshoreibah
nourshoreibah deleted the worktree-fix-preview-db-host branch August 12, 2026 02:23
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-reviewThe PR review bot won't runtest-environmentCreates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nourshoreibah
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0] - #316

Merged
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host
Aug 12, 2026
Merged

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0]#316
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

The bug

Auth failed in every preview environment while prod was fine. Multiple devs hit it. Symptom from the UI: login appears to work, then hangs ~11s and errors out.

.github/workflows/preview-env.yml resolved the preview lambdas' DB_HOST like this:

DB_HOST=$(aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address" --output text)

This account hosts several unrelated C4C databases and the branch instance is not first:

#instance
0bhchp-postgres ← what previews got
1c4c-hats
2fcc-postgres
3terraform-2025…001 ← the actual branch DB (prod uses this)

So all 12 preview lambdas across PRs #302 and #310 pointed at another project's database, whose security group blackholes our traffic. Every query hit the 5s connectionTimeoutMillis in db.ts and threw.

Why it looked like an auth bug

authenticateRequest wrapped both the JWT verification and the branch.users lookup in one try/catch that returned isAuthenticated: false. A dead database therefore surfaced as 401 Authentication required.

That is also actively harmful beyond the bad diagnostics: the frontend treats 401 as an expired session, so authedFetch refreshes, retries, then calls endSession()a database blip logs the user out.

Login and refresh kept working throughout because they only talk to Cognito and never touch the DB, which is exactly why this looked like "auth is broken" rather than "the DB is unreachable."

Evidence

Against the PR-310 preview API, with a valid token:

probebeforeafter
/auth/me, real token401 in 5.06s / 5.12s / 5.58s200 in 1.46s, correct user
/auth/me, forged signature (real kid)401 in 165msunchanged
nc to bhchp-postgres:5432hangs (blackholed)n/a
nc to branch RDS:54320.27sn/a

The forged-token probe is what isolated it: JWKS fetch and JWT verification were healthy and failed fast, so the 5s had to come from the line after verify(). aws-jwt-verify's own timeouts (1500ms socket / 3000ms response) never matched 5.06s; connectionTimeoutMillis: 5000 did, exactly.

The fix

  1. preview-env.yml — stop re-deriving DB_HOST. The prod lambda config the workflow already reads carries the right value, so inherit it like every other DB credential. Added a guard that fails the job if DB_HOST / DB creds / Cognito ids are missing, instead of silently shipping a preview that 401s on every call.
  2. shared/lambda-auth/src/authenticate.ts — only catch token verification. DB errors and a missing COGNITO_USER_POOL_ID now propagate into the handlers' existing 500 mapping. No handler changes needed; they all already have an outer catchjson(500, …).
  3. Tests flipped to assert the new behavior. Two of them previously documented this footgun as intended — including the comment "This is why a missing env var manifests as blanket silent 401s across all six lambdas rather than a loud 500."

Already applied out of band

The workflow only resolves lambda env on label-add, so the 12 already-created preview lambdas kept the bad DB_HOST regardless of this change. I repatched all of them to the branch RDS, which is what infrastructure/preview/variables.tf already documents as intended ("Preview envs deliberately reuse the shared RDS + Cognito pool"). PR-310's preview is working now — verified with the 200 above.

Verification

  • shared/lambda-auth: npx jest42/42 pass; npx tsc --noEmit clean.
  • apps/backend/lambdas/auth: 67 unit tests pass. 3 auth.e2e.test.ts failures are pre-existing — they need a lambda on localhost:3000 and fail identically on clean main.
  • Live: /auth/me on the PR-310 preview returns 200 with the correct user record.

Follow-ups not in scope here

  • Preview envs share the prod database and Cognito pool. That's a deliberate, documented tradeoff, but it means a preview can write to prod data.
  • apps/backend/lambdas/*/db.ts duplicates the same Pool config six times, so the 5s timeout and TLS rules have to be kept in sync by hand.

🤖 Generated with Claude Code

github-actionsBot added a commit that referenced this pull request Aug 12, 2026
…s[0]
Preview environments 401'd on every authenticated request while prod was fine.
preview-env.yml resolved the preview lambdas' DB_HOST with
`aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address"`.
This account hosts several unrelated C4C databases, and the branch instance is
not first, so every preview lambda was pointed at `bhchp-postgres` — another
project's DB, whose security group blackholes our traffic. Each query hit the
5s `connectionTimeoutMillis` in db.ts and threw.
The prod lambda config the workflow already reads carries the correct DB_HOST,
so stop re-deriving it and inherit it like every other DB credential. Added a
guard that fails the job if any required key is missing, rather than shipping a
preview that 401s on every call.
Second half of the bug: authenticateRequest wrapped both the JWT check and the
branch.users lookup in one try/catch that returned `isAuthenticated: false`, so
an unreachable database surfaced as 401 "Authentication required". That hid the
real fault and, because the frontend treats 401 as an expired session, cleared
the user's tokens and logged them out on a DB blip. Only token verification is
caught now; DB errors and a missing COGNITO_USER_POOL_ID propagate to the
handlers' existing 500 mapping.
Existing preview stacks were repaired out of band — the workflow only resolves
lambda env on label-add, so already-created stacks kept the bad DB_HOST.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nourshoreibah
nourshoreibahforce-pushed the worktree-fix-preview-db-host branch from c034de6 to a7c9513CompareAugust 12, 2026 02:17
@nourshoreibahnourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open:https://d3nmtjoh6ir9ym.cloudfront.net/pr-316/
API:https://dr79itqqoj.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

@nourshoreibahnourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
github-actionsBot added a commit that referenced this pull request Aug 12, 2026
@nourshoreibah
nourshoreibah merged commit 0403171 into mainAug 12, 2026
24 checks passed
@nourshoreibah
nourshoreibah deleted the worktree-fix-preview-db-host branch August 12, 2026 02:23
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-reviewThe PR review bot won't runtest-environmentCreates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nourshoreibah
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0] - #316

Merged
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host
Aug 12, 2026
Merged

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0]#316
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

The bug

Auth failed in every preview environment while prod was fine. Multiple devs hit it. Symptom from the UI: login appears to work, then hangs ~11s and errors out.

.github/workflows/preview-env.yml resolved the preview lambdas' DB_HOST like this:

DB_HOST=$(aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address" --output text)

This account hosts several unrelated C4C databases and the branch instance is not first:

#instance
0bhchp-postgres ← what previews got
1c4c-hats
2fcc-postgres
3terraform-2025…001 ← the actual branch DB (prod uses this)

So all 12 preview lambdas across PRs #302 and #310 pointed at another project's database, whose security group blackholes our traffic. Every query hit the 5s connectionTimeoutMillis in db.ts and threw.

Why it looked like an auth bug

authenticateRequest wrapped both the JWT verification and the branch.users lookup in one try/catch that returned isAuthenticated: false. A dead database therefore surfaced as 401 Authentication required.

That is also actively harmful beyond the bad diagnostics: the frontend treats 401 as an expired session, so authedFetch refreshes, retries, then calls endSession()a database blip logs the user out.

Login and refresh kept working throughout because they only talk to Cognito and never touch the DB, which is exactly why this looked like "auth is broken" rather than "the DB is unreachable."

Evidence

Against the PR-310 preview API, with a valid token:

probebeforeafter
/auth/me, real token401 in 5.06s / 5.12s / 5.58s200 in 1.46s, correct user
/auth/me, forged signature (real kid)401 in 165msunchanged
nc to bhchp-postgres:5432hangs (blackholed)n/a
nc to branch RDS:54320.27sn/a

The forged-token probe is what isolated it: JWKS fetch and JWT verification were healthy and failed fast, so the 5s had to come from the line after verify(). aws-jwt-verify's own timeouts (1500ms socket / 3000ms response) never matched 5.06s; connectionTimeoutMillis: 5000 did, exactly.

The fix

  1. preview-env.yml — stop re-deriving DB_HOST. The prod lambda config the workflow already reads carries the right value, so inherit it like every other DB credential. Added a guard that fails the job if DB_HOST / DB creds / Cognito ids are missing, instead of silently shipping a preview that 401s on every call.
  2. shared/lambda-auth/src/authenticate.ts — only catch token verification. DB errors and a missing COGNITO_USER_POOL_ID now propagate into the handlers' existing 500 mapping. No handler changes needed; they all already have an outer catchjson(500, …).
  3. Tests flipped to assert the new behavior. Two of them previously documented this footgun as intended — including the comment "This is why a missing env var manifests as blanket silent 401s across all six lambdas rather than a loud 500."

Already applied out of band

The workflow only resolves lambda env on label-add, so the 12 already-created preview lambdas kept the bad DB_HOST regardless of this change. I repatched all of them to the branch RDS, which is what infrastructure/preview/variables.tf already documents as intended ("Preview envs deliberately reuse the shared RDS + Cognito pool"). PR-310's preview is working now — verified with the 200 above.

Verification

  • shared/lambda-auth: npx jest42/42 pass; npx tsc --noEmit clean.
  • apps/backend/lambdas/auth: 67 unit tests pass. 3 auth.e2e.test.ts failures are pre-existing — they need a lambda on localhost:3000 and fail identically on clean main.
  • Live: /auth/me on the PR-310 preview returns 200 with the correct user record.

Follow-ups not in scope here

  • Preview envs share the prod database and Cognito pool. That's a deliberate, documented tradeoff, but it means a preview can write to prod data.
  • apps/backend/lambdas/*/db.ts duplicates the same Pool config six times, so the 5s timeout and TLS rules have to be kept in sync by hand.

🤖 Generated with Claude Code

github-actionsBot added a commit that referenced this pull request Aug 12, 2026
…s[0]
Preview environments 401'd on every authenticated request while prod was fine.
preview-env.yml resolved the preview lambdas' DB_HOST with
`aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address"`.
This account hosts several unrelated C4C databases, and the branch instance is
not first, so every preview lambda was pointed at `bhchp-postgres` — another
project's DB, whose security group blackholes our traffic. Each query hit the
5s `connectionTimeoutMillis` in db.ts and threw.
The prod lambda config the workflow already reads carries the correct DB_HOST,
so stop re-deriving it and inherit it like every other DB credential. Added a
guard that fails the job if any required key is missing, rather than shipping a
preview that 401s on every call.
Second half of the bug: authenticateRequest wrapped both the JWT check and the
branch.users lookup in one try/catch that returned `isAuthenticated: false`, so
an unreachable database surfaced as 401 "Authentication required". That hid the
real fault and, because the frontend treats 401 as an expired session, cleared
the user's tokens and logged them out on a DB blip. Only token verification is
caught now; DB errors and a missing COGNITO_USER_POOL_ID propagate to the
handlers' existing 500 mapping.
Existing preview stacks were repaired out of band — the workflow only resolves
lambda env on label-add, so already-created stacks kept the bad DB_HOST.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nourshoreibah
nourshoreibahforce-pushed the worktree-fix-preview-db-host branch from c034de6 to a7c9513CompareAugust 12, 2026 02:17
@nourshoreibahnourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open:https://d3nmtjoh6ir9ym.cloudfront.net/pr-316/
API:https://dr79itqqoj.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

@nourshoreibahnourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
github-actionsBot added a commit that referenced this pull request Aug 12, 2026
@nourshoreibah
nourshoreibah merged commit 0403171 into mainAug 12, 2026
24 checks passed
@nourshoreibah
nourshoreibah deleted the worktree-fix-preview-db-host branch August 12, 2026 02:23
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-reviewThe PR review bot won't runtest-environmentCreates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nourshoreibah
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0] - #316

Merged
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host
Aug 12, 2026
Merged

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0]#316
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

The bug

Auth failed in every preview environment while prod was fine. Multiple devs hit it. Symptom from the UI: login appears to work, then hangs ~11s and errors out.

.github/workflows/preview-env.yml resolved the preview lambdas' DB_HOST like this:

DB_HOST=$(aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address" --output text)

This account hosts several unrelated C4C databases and the branch instance is not first:

#instance
0bhchp-postgres ← what previews got
1c4c-hats
2fcc-postgres
3terraform-2025…001 ← the actual branch DB (prod uses this)

So all 12 preview lambdas across PRs #302 and #310 pointed at another project's database, whose security group blackholes our traffic. Every query hit the 5s connectionTimeoutMillis in db.ts and threw.

Why it looked like an auth bug

authenticateRequest wrapped both the JWT verification and the branch.users lookup in one try/catch that returned isAuthenticated: false. A dead database therefore surfaced as 401 Authentication required.

That is also actively harmful beyond the bad diagnostics: the frontend treats 401 as an expired session, so authedFetch refreshes, retries, then calls endSession()a database blip logs the user out.

Login and refresh kept working throughout because they only talk to Cognito and never touch the DB, which is exactly why this looked like "auth is broken" rather than "the DB is unreachable."

Evidence

Against the PR-310 preview API, with a valid token:

probebeforeafter
/auth/me, real token401 in 5.06s / 5.12s / 5.58s200 in 1.46s, correct user
/auth/me, forged signature (real kid)401 in 165msunchanged
nc to bhchp-postgres:5432hangs (blackholed)n/a
nc to branch RDS:54320.27sn/a

The forged-token probe is what isolated it: JWKS fetch and JWT verification were healthy and failed fast, so the 5s had to come from the line after verify(). aws-jwt-verify's own timeouts (1500ms socket / 3000ms response) never matched 5.06s; connectionTimeoutMillis: 5000 did, exactly.

The fix

  1. preview-env.yml — stop re-deriving DB_HOST. The prod lambda config the workflow already reads carries the right value, so inherit it like every other DB credential. Added a guard that fails the job if DB_HOST / DB creds / Cognito ids are missing, instead of silently shipping a preview that 401s on every call.
  2. shared/lambda-auth/src/authenticate.ts — only catch token verification. DB errors and a missing COGNITO_USER_POOL_ID now propagate into the handlers' existing 500 mapping. No handler changes needed; they all already have an outer catchjson(500, …).
  3. Tests flipped to assert the new behavior. Two of them previously documented this footgun as intended — including the comment "This is why a missing env var manifests as blanket silent 401s across all six lambdas rather than a loud 500."

Already applied out of band

The workflow only resolves lambda env on label-add, so the 12 already-created preview lambdas kept the bad DB_HOST regardless of this change. I repatched all of them to the branch RDS, which is what infrastructure/preview/variables.tf already documents as intended ("Preview envs deliberately reuse the shared RDS + Cognito pool"). PR-310's preview is working now — verified with the 200 above.

Verification

  • shared/lambda-auth: npx jest42/42 pass; npx tsc --noEmit clean.
  • apps/backend/lambdas/auth: 67 unit tests pass. 3 auth.e2e.test.ts failures are pre-existing — they need a lambda on localhost:3000 and fail identically on clean main.
  • Live: /auth/me on the PR-310 preview returns 200 with the correct user record.

Follow-ups not in scope here

  • Preview envs share the prod database and Cognito pool. That's a deliberate, documented tradeoff, but it means a preview can write to prod data.
  • apps/backend/lambdas/*/db.ts duplicates the same Pool config six times, so the 5s timeout and TLS rules have to be kept in sync by hand.

🤖 Generated with Claude Code

github-actionsBot added a commit that referenced this pull request Aug 12, 2026
…s[0]
Preview environments 401'd on every authenticated request while prod was fine.
preview-env.yml resolved the preview lambdas' DB_HOST with
`aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address"`.
This account hosts several unrelated C4C databases, and the branch instance is
not first, so every preview lambda was pointed at `bhchp-postgres` — another
project's DB, whose security group blackholes our traffic. Each query hit the
5s `connectionTimeoutMillis` in db.ts and threw.
The prod lambda config the workflow already reads carries the correct DB_HOST,
so stop re-deriving it and inherit it like every other DB credential. Added a
guard that fails the job if any required key is missing, rather than shipping a
preview that 401s on every call.
Second half of the bug: authenticateRequest wrapped both the JWT check and the
branch.users lookup in one try/catch that returned `isAuthenticated: false`, so
an unreachable database surfaced as 401 "Authentication required". That hid the
real fault and, because the frontend treats 401 as an expired session, cleared
the user's tokens and logged them out on a DB blip. Only token verification is
caught now; DB errors and a missing COGNITO_USER_POOL_ID propagate to the
handlers' existing 500 mapping.
Existing preview stacks were repaired out of band — the workflow only resolves
lambda env on label-add, so already-created stacks kept the bad DB_HOST.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nourshoreibah
nourshoreibahforce-pushed the worktree-fix-preview-db-host branch from c034de6 to a7c9513CompareAugust 12, 2026 02:17
@nourshoreibahnourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open:https://d3nmtjoh6ir9ym.cloudfront.net/pr-316/
API:https://dr79itqqoj.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

@nourshoreibahnourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
github-actionsBot added a commit that referenced this pull request Aug 12, 2026
@nourshoreibah
nourshoreibah merged commit 0403171 into mainAug 12, 2026
24 checks passed
@nourshoreibah
nourshoreibah deleted the worktree-fix-preview-db-host branch August 12, 2026 02:23
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-reviewThe PR review bot won't runtest-environmentCreates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nourshoreibah
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0] - #316

Merged
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host
Aug 12, 2026
Merged

fix(preview): point preview lambdas at the branch RDS, not DBInstances[0]#316
nourshoreibah merged 1 commit into
mainfrom
worktree-fix-preview-db-host

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

The bug

Auth failed in every preview environment while prod was fine. Multiple devs hit it. Symptom from the UI: login appears to work, then hangs ~11s and errors out.

.github/workflows/preview-env.yml resolved the preview lambdas' DB_HOST like this:

DB_HOST=$(aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address" --output text)

This account hosts several unrelated C4C databases and the branch instance is not first:

#instance
0bhchp-postgres ← what previews got
1c4c-hats
2fcc-postgres
3terraform-2025…001 ← the actual branch DB (prod uses this)

So all 12 preview lambdas across PRs #302 and #310 pointed at another project's database, whose security group blackholes our traffic. Every query hit the 5s connectionTimeoutMillis in db.ts and threw.

Why it looked like an auth bug

authenticateRequest wrapped both the JWT verification and the branch.users lookup in one try/catch that returned isAuthenticated: false. A dead database therefore surfaced as 401 Authentication required.

That is also actively harmful beyond the bad diagnostics: the frontend treats 401 as an expired session, so authedFetch refreshes, retries, then calls endSession()a database blip logs the user out.

Login and refresh kept working throughout because they only talk to Cognito and never touch the DB, which is exactly why this looked like "auth is broken" rather than "the DB is unreachable."

Evidence

Against the PR-310 preview API, with a valid token:

probebeforeafter
/auth/me, real token401 in 5.06s / 5.12s / 5.58s200 in 1.46s, correct user
/auth/me, forged signature (real kid)401 in 165msunchanged
nc to bhchp-postgres:5432hangs (blackholed)n/a
nc to branch RDS:54320.27sn/a

The forged-token probe is what isolated it: JWKS fetch and JWT verification were healthy and failed fast, so the 5s had to come from the line after verify(). aws-jwt-verify's own timeouts (1500ms socket / 3000ms response) never matched 5.06s; connectionTimeoutMillis: 5000 did, exactly.

The fix

  1. preview-env.yml — stop re-deriving DB_HOST. The prod lambda config the workflow already reads carries the right value, so inherit it like every other DB credential. Added a guard that fails the job if DB_HOST / DB creds / Cognito ids are missing, instead of silently shipping a preview that 401s on every call.
  2. shared/lambda-auth/src/authenticate.ts — only catch token verification. DB errors and a missing COGNITO_USER_POOL_ID now propagate into the handlers' existing 500 mapping. No handler changes needed; they all already have an outer catchjson(500, …).
  3. Tests flipped to assert the new behavior. Two of them previously documented this footgun as intended — including the comment "This is why a missing env var manifests as blanket silent 401s across all six lambdas rather than a loud 500."

Already applied out of band

The workflow only resolves lambda env on label-add, so the 12 already-created preview lambdas kept the bad DB_HOST regardless of this change. I repatched all of them to the branch RDS, which is what infrastructure/preview/variables.tf already documents as intended ("Preview envs deliberately reuse the shared RDS + Cognito pool"). PR-310's preview is working now — verified with the 200 above.

Verification

  • shared/lambda-auth: npx jest42/42 pass; npx tsc --noEmit clean.
  • apps/backend/lambdas/auth: 67 unit tests pass. 3 auth.e2e.test.ts failures are pre-existing — they need a lambda on localhost:3000 and fail identically on clean main.
  • Live: /auth/me on the PR-310 preview returns 200 with the correct user record.

Follow-ups not in scope here

  • Preview envs share the prod database and Cognito pool. That's a deliberate, documented tradeoff, but it means a preview can write to prod data.
  • apps/backend/lambdas/*/db.ts duplicates the same Pool config six times, so the 5s timeout and TLS rules have to be kept in sync by hand.

🤖 Generated with Claude Code

github-actionsBot added a commit that referenced this pull request Aug 12, 2026
…s[0]
Preview environments 401'd on every authenticated request while prod was fine.
preview-env.yml resolved the preview lambdas' DB_HOST with
`aws rds describe-db-instances --query "DBInstances[0].Endpoint.Address"`.
This account hosts several unrelated C4C databases, and the branch instance is
not first, so every preview lambda was pointed at `bhchp-postgres` — another
project's DB, whose security group blackholes our traffic. Each query hit the
5s `connectionTimeoutMillis` in db.ts and threw.
The prod lambda config the workflow already reads carries the correct DB_HOST,
so stop re-deriving it and inherit it like every other DB credential. Added a
guard that fails the job if any required key is missing, rather than shipping a
preview that 401s on every call.
Second half of the bug: authenticateRequest wrapped both the JWT check and the
branch.users lookup in one try/catch that returned `isAuthenticated: false`, so
an unreachable database surfaced as 401 "Authentication required". That hid the
real fault and, because the frontend treats 401 as an expired session, cleared
the user's tokens and logged them out on a DB blip. Only token verification is
caught now; DB errors and a missing COGNITO_USER_POOL_ID propagate to the
handlers' existing 500 mapping.
Existing preview stacks were repaired out of band — the workflow only resolves
lambda env on label-add, so already-created stacks kept the bad DB_HOST.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nourshoreibah
nourshoreibahforce-pushed the worktree-fix-preview-db-host branch from c034de6 to a7c9513CompareAugust 12, 2026 02:17
@nourshoreibahnourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open:https://d3nmtjoh6ir9ym.cloudfront.net/pr-316/
API:https://dr79itqqoj.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

@nourshoreibahnourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
github-actionsBot added a commit that referenced this pull request Aug 12, 2026
@nourshoreibah
nourshoreibah merged commit 0403171 into mainAug 12, 2026
24 checks passed
@nourshoreibah
nourshoreibah deleted the worktree-fix-preview-db-host branch August 12, 2026 02:23
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-reviewThe PR review bot won't runtest-environmentCreates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nourshoreibah