Skip to content

feat: route Bedrock via Cloudflare AI Gateway BYOK (drop client-side SigV4)#47

Open
jphein wants to merge 2 commits into
masterfrom
feat/18-ai-gateway-byok
Open

feat: route Bedrock via Cloudflare AI Gateway BYOK (drop client-side SigV4)#47
jphein wants to merge 2 commits into
masterfrom
feat/18-ai-gateway-byok

Conversation

@jphein

@jphein jphein commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Closes #18.

What this does

Migrates the chat backend's Bedrock auth from in-worker AWS SigV4 signing to Cloudflare AI Gateway BYOK (Bring Your Own Keys).

When AI_GATEWAY_BASE and AI_GATEWAY_AUTH_TOKEN are both present, invokeBedrock now:

  • sends an unsigned request to the gateway,
  • authenticates to the gateway with cf-aig-authorization: Bearer <token>,
  • omits the AWS Authorization header entirely — the gateway holds the AWS credentials and performs SigV4 on its own infra before forwarding to Bedrock.

This removes the per-request 4×HMAC-SHA256 + 2×SHA256 from the chat hot path (~10ms → ~1ms CPU/request), which is what was pushing requests toward the Workers CPU cap and surfacing as 1102 errors.

SigV4 signing is retained as a fallback (direct Bedrock, or unauthenticated gateway pass-through) for when no BYOK token is configured — so AWS keys remain a belt-and-suspenders option rather than being deleted outright.

Changes

  • lib/chat-server/bedrock.ts — add AI_GATEWAY_AUTH_TOKEN to BedrockEnv; branch BYOK vs SigV4; AWS keys are now optional (required only for the SigV4 path, with a clear thrown error if neither auth mode is configured).
  • pages/api/chat.ts — read AI_GATEWAY_AUTH_TOKEN; relax the misconfigured guard to accept BYOK OR AWS keys (session secret still always required).
  • .env.example — document the two Bedrock auth modes and the chat env vars (previously undocumented).

The Haiku topic classifier (classify.ts) inherits the new path automatically — it calls the same invokeBedrock.

⚠️ Required Cloudflare-side setup BEFORE merge/deploy

This PR is code-only and must NOT be auto-merged. The following must be done on the Cloudflare side first, or chat will fall back to SigV4 (or fail if AWS keys are later removed):

  1. AI Gateway provider key (BYOK): In the dash → AI → AI Gateway → the techempower-chat gateway → Provider KeysAdd → AWS Bedrock. Paste the AWS access key + secret and set region us-west-2 (must match AWS_REGION).
  2. Gateway auth token: Enable an authenticated gateway and copy the generated AI Gateway token. Set it as a Worker secret:
    pnpm exec wrangler secret put AI_GATEWAY_AUTH_TOKEN
  3. Gateway base URL: Ensure AI_GATEWAY_BASE is set (Worker secret/var) to the base up to but not including /aws-bedrock, i.e. https://gateway.ai.cloudflare.com/v1/<account_id>/<gateway_id>.
  4. Deploy this branch, then send a test chat message and confirm a normal reply (and a request appearing in the AI Gateway logs).
  5. After verifying: the per-request AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY Worker secrets can optionally be removed — code no longer needs them in BYOK mode. Recommend keeping them initially as a direct-Bedrock fallback, then dropping once BYOK is proven stable.

Note: the issue mentions us-east-1 in the generic docs example, but this app uses us-west-2 (per AWS_REGION default and the existing CPU/cost notes). The BYOK provider-key region must match.

Validation

  • pnpm install
  • npx tsc --noEmit
  • pnpm test (eslint + prettier) ✅

No unit-test framework in this repo, so no tests added.

Summary by CodeRabbit

  • New Features
    • Added support for multiple Bedrock authentication modes in the chat assistant
    • Enables gateway-based authentication using a gateway token when configured
    • Keeps a local SigV4 fallback option when AWS keys are provided
    • Preserves gateway response caching behavior in both modes
  • Documentation
    • Updated .env.example with a new, documented configuration section for the chat assistant authentication, including optional bot-defense settings

…SigV4)

When AI_GATEWAY_BASE and AI_GATEWAY_AUTH_TOKEN are both set, the chat
backend now sends UNSIGNED Bedrock requests with a `cf-aig-authorization:
Bearer <token>` header and lets the Cloudflare AI Gateway perform SigV4
on its own infrastructure. This eliminates the per-request 4×HMAC-SHA256
+ 2×SHA256 that was pushing the chat hot path toward the Workers CPU cap
(~10ms -> ~1ms), structurally killing the 1102 errors (issue #18).

SigV4 signing is kept as a fallback for direct Bedrock calls or an
unauthenticated gateway pass-through when no BYOK token is configured, so
AWS keys remain a belt-and-suspenders option.

- bedrock.ts: add AI_GATEWAY_AUTH_TOKEN to BedrockEnv; branch on BYOK vs
  SigV4; AWS keys now optional (only required for the SigV4 path)
- chat.ts: read AI_GATEWAY_AUTH_TOKEN, relax misconfigured check to accept
  BYOK OR AWS keys
- .env.example: document the two Bedrock auth modes + chat env vars

Closes #18

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bf8b4431-8450-443f-bb50-11b8fd16a088

📥 Commits

Reviewing files that changed from the base of the PR and between 4e6609e and 735d03e.

📒 Files selected for processing (2)
  • lib/chat-server/bedrock.ts
  • pages/api/chat.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • pages/api/chat.ts

📝 Walkthrough

Walkthrough

Adds a BYOK authentication mode to Bedrock calls routed through the Cloudflare AI Gateway. BedrockEnv gains an optional AI_GATEWAY_AUTH_TOKEN field; invokeBedrock conditionally sends a cf-aig-authorization header instead of computing SigV4 signatures. AWS credentials become optional and serve only the fallback path. The chat API route and .env.example are updated to match.

Changes

Bedrock BYOK Auth via Cloudflare AI Gateway

Layer / File(s) Summary
BedrockEnv interface and dual-mode auth in invokeBedrock
lib/chat-server/bedrock.ts
AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY made optional on BedrockEnv; AI_GATEWAY_AUTH_TOKEN?: string added. invokeBedrock normalizes AI_GATEWAY_BASE to HTTPS, computes useByok from both gateway fields being present, sends cf-aig-authorization: Bearer <token> in BYOK mode, and explicitly throws when AWS keys are absent in the SigV4 fallback path.
Chat API route validation and env wiring
pages/api/chat.ts
Extracts AI_GATEWAY_AUTH_TOKEN from process.env, changes the misconfigured guard to require SESSION_SIGN_SECRET plus either BYOK credentials or SigV4 keys, and adds the token to bedrockEnv.
Config documentation
.env.example
New commented section documents both auth modes (BYOK token and SigV4 fallback), session signing secret, and optional Turnstile key for /api/chat.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ChatAPI as pages/api/chat.ts
  participant invokeBedrock as lib/chat-server/bedrock.ts
  participant AIGateway as Cloudflare AI Gateway
  participant Bedrock as AWS Bedrock

  Client->>ChatAPI: POST /api/chat
  ChatAPI->>ChatAPI: Validate SESSION_SIGN_SECRET + (BYOK creds OR SigV4 keys)
  ChatAPI->>invokeBedrock: invokeBedrock(bedrockEnv with AI_GATEWAY_AUTH_TOKEN)

  alt BYOK mode (AI_GATEWAY_BASE + AI_GATEWAY_AUTH_TOKEN set)
    invokeBedrock->>AIGateway: fetch with cf-aig-authorization header (no SigV4)
    AIGateway->>Bedrock: SigV4-signed request (Cloudflare signs)
    Bedrock-->>AIGateway: Streaming response
    AIGateway-->>invokeBedrock: Streaming response
  else SigV4 fallback (AWS keys set)
    invokeBedrock->>Bedrock: fetch with SigV4 Authorization header
    Bedrock-->>invokeBedrock: Streaming response
  end

  invokeBedrock-->>ChatAPI: InvokeResult
  ChatAPI-->>Client: Streamed reply
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 Hop hop, no more signing by paw,
The Gateway now handles SigV4 raw.
A token is all that we send through the wire,
Less CPU heat, no more 1102 fire!
With fallback in place, we're dressed belt-and-suspenders,
The bunny delights in these cloud-side surrenders. 🌩️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: migrating Bedrock auth to Cloudflare AI Gateway BYOK and eliminating client-side SigV4 signing.
Description check ✅ Passed The description is comprehensive and well-structured, covering what the PR does, implementation details, required Cloudflare setup, and validation results.
Linked Issues check ✅ Passed All primary objectives from #18 are met: authentication branching logic (BYOK vs SigV4), optional AWS keys, optional AI_GATEWAY_AUTH_TOKEN, SigV4 retained as fallback, and environment documentation.
Out of Scope Changes check ✅ Passed All code changes are directly aligned with the linked issue objectives: configuration updates, authentication logic, environment documentation, and hardening commits addressing code review findings.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/18-ai-gateway-byok

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a "Bring Your Own Key" (BYOK) authentication mode for AWS Bedrock via Cloudflare AI Gateway, which offloads SigV4 signing to the gateway to reduce Worker CPU usage, while retaining local SigV4 signing as a fallback. Feedback on these changes suggests trimming environment variables in pages/api/chat.ts to handle whitespace-only values robustly, and automatically prepending https:// to AI_GATEWAY_BASE in lib/chat-server/bedrock.ts if the protocol is omitted.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread pages/api/chat.ts
Comment on lines +155 to +156
const hasByok = !!aiGatewayBase && !!aiGatewayAuthToken
const hasAwsKeys = !!awsAccessKeyId && !!awsSecretAccessKey

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent issues with whitespace-only environment variables or secrets (which can happen during copy-pasting), it is safer to trim the values before checking if they are present.

Suggested change
const hasByok = !!aiGatewayBase && !!aiGatewayAuthToken
const hasAwsKeys = !!awsAccessKeyId && !!awsSecretAccessKey
const hasByok = !!aiGatewayBase.trim() && !!aiGatewayAuthToken.trim()
const hasAwsKeys = !!awsAccessKeyId.trim() && !!awsSecretAccessKey.trim()

Comment thread lib/chat-server/bedrock.ts Outdated
Comment on lines +99 to +103
const gatewayBase = (env.AI_GATEWAY_BASE ?? '').trim()
const authToken = (env.AI_GATEWAY_AUTH_TOKEN ?? '').trim()
// BYOK is active only when we have both a gateway to route through AND a
// token to authenticate to it. Either one alone falls back to SigV4.
const useByok = gatewayBase !== '' && authToken !== ''

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If AI_GATEWAY_BASE is configured without a protocol (e.g., gateway.ai.cloudflare.com/...), the subsequent fetch call will fail. Automatically prepending https:// if the protocol is missing makes the configuration more robust and user-friendly.

  let gatewayBase = (env.AI_GATEWAY_BASE ?? '').trim()
  if (gatewayBase && !/^https?:\/\//i.test(gatewayBase)) {
    gatewayBase = `https://${gatewayBase}`
  }
  const authToken = (env.AI_GATEWAY_AUTH_TOKEN ?? '').trim()
  // BYOK is active only when we have both a gateway to route through AND a
  // token to authenticate to it. Either one alone falls back to SigV4.
  const useByok = gatewayBase !== '' && authToken !== ''

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pages/api/chat.ts (1)

141-157: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize env values before auth-mode checks.

hasByok/hasAwsKeys are computed from untrimmed values here, but invokeBedrock trims gateway values again before choosing BYOK. A whitespace-only secret can pass this guard and then fail later at runtime instead of returning early as misconfigured.

Suggested fix
-  const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID ?? ''
-  const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY ?? ''
+  const awsAccessKeyId = (process.env.AWS_ACCESS_KEY_ID ?? '').trim()
+  const awsSecretAccessKey = (process.env.AWS_SECRET_ACCESS_KEY ?? '').trim()
   const awsRegion = process.env.AWS_REGION ?? 'us-west-2'
-  const aiGatewayBase = process.env.AI_GATEWAY_BASE ?? ''
-  const aiGatewayAuthToken = process.env.AI_GATEWAY_AUTH_TOKEN ?? ''
+  const aiGatewayBase = (process.env.AI_GATEWAY_BASE ?? '').trim()
+  const aiGatewayAuthToken = (process.env.AI_GATEWAY_AUTH_TOKEN ?? '').trim()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pages/api/chat.ts` around lines 141 - 157, The environment variables are
being checked for truthiness without trimming whitespace, but invokeBedrock
trims these values later. This allows whitespace-only secrets to pass the early
validation guard and fail at runtime instead. Trim the aiGatewayBase and
aiGatewayAuthToken values (and similarly for awsAccessKeyId and
awsSecretAccessKey if needed) before computing the hasByok and hasAwsKeys
boolean checks to ensure that whitespace-only values are properly treated as
empty and rejected early with the misconfiguration check at the if statement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@pages/api/chat.ts`:
- Around line 141-157: The environment variables are being checked for
truthiness without trimming whitespace, but invokeBedrock trims these values
later. This allows whitespace-only secrets to pass the early validation guard
and fail at runtime instead. Trim the aiGatewayBase and aiGatewayAuthToken
values (and similarly for awsAccessKeyId and awsSecretAccessKey if needed)
before computing the hasByok and hasAwsKeys boolean checks to ensure that
whitespace-only values are properly treated as empty and rejected early with the
misconfiguration check at the if statement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fcab0d55-6f7a-4951-9d3c-fe90487ab835

📥 Commits

Reviewing files that changed from the base of the PR and between 6c832c0 and 4e6609e.

📒 Files selected for processing (3)
  • .env.example
  • lib/chat-server/bedrock.ts
  • pages/api/chat.ts

Addresses the two medium findings from the gemini-code-assist review on
PR #47 (coderabbit found 0 actionable items):

- bedrock.ts: auto-prepend `https://` to AI_GATEWAY_BASE when it is
  configured without a scheme — fetch() requires an absolute URL, so a
  scheme-less value would throw at deploy time and take chat down.
- chat.ts: trim the Bedrock-auth env vars (AWS keys + gateway base/token)
  at the source. Whitespace-only values (copy-paste mishap) would
  otherwise pass the `!!` presence guards while being effectively empty;
  bedrock.ts already trims the gateway values when choosing BYOK vs SigV4,
  so trimming here keeps the guard and the consumer in agreement.

No behavior change for correctly-configured environments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jphein

jphein commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the reviews 🙏

CodeRabbit: 0 actionable comments, 5/5 pre-merge checks passed.

Gemini — both medium findings addressed in 735d03e:

  1. AI_GATEWAY_BASE without a schemebedrock.ts now prepends https:// when no http(s):// prefix is present (guards against a scheme-less value making fetch() throw at deploy time).
  2. Whitespace-only env vars passing presence checks → fixed at the source: the AWS keys + gateway base/token are now .trim()-ed when read from process.env in chat.ts. This keeps the !!-based hasByok/hasAwsKeys guards honest and in agreement with bedrock.ts, which already trims the gateway values when choosing BYOK vs SigV4. (Did it at the read site rather than inline in the guard so the cleaned values also flow into bedrockEnv/SigV4.)

npx tsc --noEmit, eslint, and prettier all pass. No behavior change for correctly-configured environments.

Reminder for reviewers: this is not mergeable until the Cloudflare-side BYOK setup is done (AI Gateway provider key + AI_GATEWAY_AUTH_TOKEN worker secret) — full steps in the PR description. Holding for JP's sign-off.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate AWS Bedrock auth to AI Gateway BYOK — drop client-side SigV4

1 participant