Skip to content

Token-gated multi-provider AI proxy (doppelganger tokens) + OpenAI geo-403 fix#1

Merged
Bug-Finderr merged 12 commits into
mainfrom
doppelganger-tokens
Jun 22, 2026
Merged

Token-gated multi-provider AI proxy (doppelganger tokens) + OpenAI geo-403 fix#1
Bug-Finderr merged 12 commits into
mainfrom
doppelganger-tokens

Conversation

@Bug-Finderr

Copy link
Copy Markdown
Owner

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

  • Routing by auth header, no path prefix (Bearer→OpenAI, x-api-key→Anthropic, x-goog-api-key/?key=→Gemini, Bearer+/v1beta/openai/→Gemini compat). Keeps each SDK a true drop-in.
  • Security: strip-all-then-set-one auth swap (token never forwarded upstream), tokens stored as SHA-256(token) and shown once, lastUsed in a separate <hash>:lu key so usage stamps can''t resurrect a revoked token, constant-time HMAC cookie auth.
  • OpenAI geo-403 fix: OpenAI intermittently 403''d (unsupported_country_region_territory) because a Worker''s fetch() 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.
  • Tests: 62 tier-1 (workerd) + 7 tier-2 (real OpenAI/Anthropic/Gemini SDKs against a mock upstream) = 69 green; tsc clean.
  • Docs: docs/learnings/ records the shaping decisions (egress geo-block, routing, token security).

Notes for review

  • Focus areas: the egress fallback in src/proxy.ts + src/egress.ts, and the auth-swap invariant.
  • Aiming for KISS/YAGNI: please flag anything over-built; Phase-2 items (rate limits, spend caps, expiry, analytics, CORS, Gemini uploads) are deliberately deferred.
  • wrangler.toml keeps a placeholder KV id in git (ids aren''t secret; real id is set locally for deploy).

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.
@Bug-Finderr Bug-Finderr force-pushed the doppelganger-tokens branch from e0f1517 to 2883d6a Compare June 22, 2026 16:08
@Bug-Finderr Bug-Finderr self-assigned this Jun 22, 2026
@Bug-Finderr Bug-Finderr merged commit e4aef41 into main Jun 22, 2026
2 checks passed
@Bug-Finderr Bug-Finderr deleted the doppelganger-tokens branch June 22, 2026 16:21
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.

1 participant