Token-gated multi-provider AI proxy (doppelganger tokens) + OpenAI geo-403 fix#1
Merged
Conversation
… + gemini-openai)
…ingle worker; ignore local state
…sed, input/error guards, escape pills
OpenAI intermittently (~40%) returned 403 unsupported_country_region_territory. Root cause: a Worker fetch() egresses from the colo the invocation runs in, and that colo varies per request; OpenAI geo-blocks some regions (HKG -> 403, SIN -> 200, 100% correlated). The egress colo is pinned per invocation, so an in-invocation retry cannot escape a bad colo. Fix: re-issue only the OpenAI hop through a Durable Object pinned with locationHint "wnam" (North America), as a fallback triggered solely on the geo-403. The DO's fetch() egresses from a US colo (verified DFW/LAX/DEN/SJC/SEA). The real key never leaves Cloudflare; the SQLite DO is free. Anthropic/Gemini paths are unchanged and stay fast. Verified live: /v1/models 25/25 -> 200 (was ~40% 403); streaming survives the fallback. 69 tests green (62 tier-1 incl. 3 new fallback tests, 7 tier-2).
- Biome 2.5 (biome.json): formatter (2-space, double quotes) + recommended lint, ported from the frontend config minus the React/Tailwind-only rules. - lefthook pre-commit runs `biome check --write` (safe fixes only) on staged files (per the official Biome recipe, adapted to nub), auto-staging fixes. - package.json: `check` (tsc + biome) and `prepare` (lefthook install) scripts. - .gitattributes normalizes line endings to LF (ends the CRLF churn). - Reformat existing source/tests to the Biome style (whitespace only). No behaviour change. 69 tests green; tsc + biome clean. The per-edit auto-format Claude hook lives in .claude/settings.local.json (gitignored).
A continuous, one-file-per-topic record of the decisions shaping the proxy: - openai-egress-geo-block: per-colo geo-403 root cause + NA-pinned Durable Object fix - provider-routing-by-auth-header: one base URL, route by auth slot (no path prefix) - doppelganger-token-security: strip-all-then-set-one swap, hashed-at-rest, revoke-safe lastUsed
KV namespace ids are not secret (useless without account auth) and this is a single-owner private repo, so committing the real id makes main deployable without a manual edit. Replaces the REPLACE_WITH_KV_NAMESPACE_ID placeholder.
e0f1517 to
2883d6a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
One Cloudflare Worker that fronts OpenAI, Anthropic, and Gemini behind revocable "doppelganger" tokens. A client changes only its base URL + API key; the token rides the SDK''s normal auth slot. The Worker validates it against KV, scopes it to a provider, swaps in the real key, and forwards (path/query verbatim, streaming intact). An admin dashboard (
/admin, Hono + HTMX) mints/scopes/revokes tokens.Highlights
Bearer→OpenAI,x-api-key→Anthropic,x-goog-api-key/?key=→Gemini,Bearer+/v1beta/openai/→Gemini compat). Keeps each SDK a true drop-in.SHA-256(token)and shown once,lastUsedin a separate<hash>:lukey so usage stamps can''t resurrect a revoked token, constant-time HMAC cookie auth.unsupported_country_region_territory) because a Worker''sfetch()egresses from the invocation''s colo, and some colos (e.g. HKG) are geo-blocked. The egress colo is pinned per invocation, so an in-invocation retry can''t help. Fix: re-issue only the OpenAI hop, only on the geo-403, through a North-America-pinned Durable Object (locationHint:"wnam"). Real key never leaves Cloudflare; free (SQLite DO). Verified live: 25/25 → 200 (was ~40% failing), streaming preserved.tscclean.docs/learnings/records the shaping decisions (egress geo-block, routing, token security).Notes for review
src/proxy.ts+src/egress.ts, and the auth-swap invariant.wrangler.tomlkeeps a placeholder KV id in git (ids aren''t secret; real id is set locally for deploy).