feat: aimock-owned upstream provider keys on fixture-miss passthrough#293
Merged
Conversation
Add opt-in, backward-compatible injection of aimock's own upstream API key
when a fixture-miss request is proxied upstream. Callers commonly send a dummy
placeholder key (e.g. sk-aimock-...) to satisfy an SDK's non-empty-key check;
today that dummy is forwarded verbatim and the upstream rejects it. When a
built-in key is configured for the provider, aimock now injects it using the
provider's native scheme (OpenAI/OpenRouter/Cohere bearer, Anthropic
x-api-key, Gemini x-goog-api-key) while leaving a real caller key untouched.
- New src/provider-auth.ts: applyProviderAuth + readProviderKeysFromEnv, with
the sk-aimock- dummy marker (overridable via AIMOCK_DUMMY_KEY_MARKER).
- Keys sourced from AIMOCK_PROVIDER_{OPENAI,ANTHROPIC,GEMINI}_KEY env vars
(not CLI flags — secrets stay out of ps) into RecordConfig.providerKeys.
- Signed/OAuth providers (Bedrock/Vertex/Azure-AD) are never rewritten.
- Injection runs only on the fixture-miss proxy path; a fixture match
short-circuits before proxyAndRecord.
- Real-surface red-green test: a fake upstream records the auth header aimock
forwards; dummy->built-in swap proven, backward-compat cases covered.
commit: |
Contributor
Author
|
Design spec (reviewed, 2 rounds, zero actionable) is published: https://app.notion.com/p/39d3aa3818528168a9ace2604256e910 This PR implements it. The spec's Gemini section was corrected to header-only ( |
jpr5
marked this pull request as ready for review
July 14, 2026 04:35
jpr5
marked this pull request as draft
July 14, 2026 04:40
Add README subsection and record-replay docs page section for the aimock-owned upstream provider key feature: the AIMOCK_PROVIDER_OPENAI_KEY / AIMOCK_PROVIDER_ANTHROPIC_KEY / AIMOCK_PROVIDER_GEMINI_KEY env vars, the dummy-key override precedence (sk-aimock- marker, overridable via AIMOCK_DUMMY_KEY_MARKER), opt-in/backward-compat inertness, per-provider auth schemes, and the excluded signed/exchanged providers (Bedrock/Vertex/Azure-AD).
jpr5
marked this pull request as ready for review
July 14, 2026 04:46
jpr5
marked this pull request as draft
July 14, 2026 04:47
Complete the own-key-injection feature so every static-key provider aimock proxies is configurable end-to-end (scheme + env var + injection + tests + docs), closing the gap where OpenRouter/Cohere were named but never read. Providers wired (env var -> injected header): - OpenAI/OpenRouter/Cohere/Grok/Ollama Authorization: Bearer <key> - Anthropic x-api-key: <key> - Gemini/Gemini-Interactions/Veo x-goog-api-key: <key> - Azure OpenAI api-key: <key> - ElevenLabs xi-api-key: <key> - fal.ai Authorization: Key <key> New scheme kinds: api-key, xi-api-key, fal-key (Authorization: Key). fal own-key injection is centralized in walkFalQueue (the queue path does not route through proxyAndRecord), covering both fal.ts and fal-audio.ts callers. Semantics unchanged per provider: fixture-miss-only, dummy/absent-credential override, real-caller-key override, empty env var inert. Bedrock (SigV4) and Vertex AI (OAuth) remain excluded and forwarded verbatim. Tests: fake-upstream coverage per distinct scheme (bearer via Cohere+Ollama, api-key via Azure, xi-api-key via ElevenLabs, fal Key via queue submit) plus real-key-override and inert-when-unset cases, and full env-var read coverage.
jpr5
marked this pull request as ready for review
July 14, 2026 05:22
jpr5
added a commit
that referenced
this pull request
Jul 15, 2026
…#293) ## What Opt-in, backward-compatible support for **aimock owning the upstream provider API key**. On a fixture-miss passthrough, aimock forwards the request to the real upstream. Callers commonly send a **dummy placeholder key** (e.g. `sk-aimock-…`) only to satisfy an SDK's non-empty-key requirement; today that dummy is forwarded verbatim and the upstream rejects it. This PR lets aimock inject its own configured key in that case, while never clobbering a real caller key. ## Design - **Env-sourced keys** (not CLI flags — secrets stay out of `ps`): `AIMOCK_PROVIDER_OPENAI_KEY`, `AIMOCK_PROVIDER_ANTHROPIC_KEY`, `AIMOCK_PROVIDER_GEMINI_KEY` → plumbed into new `RecordConfig.providerKeys` (parallel to `providers`), wired from `cli.ts` via `readProviderKeysFromEnv()`. - **`applyProviderAuth(forwardHeaders, target, providerKey, builtinKey)`** invoked immediately after `buildForwardHeaders(req)` on the proxy path. Provider-aware schemes: - OpenAI / OpenRouter / Cohere → `Authorization: Bearer <key>` - Anthropic → `x-api-key: <key>` - Gemini (+ gemini-interactions) → `x-goog-api-key: <key>` - **Precedence (dummy-override semantics):** caller credential absent or dummy-prefixed (`sk-aimock-`, overridable via `AIMOCK_DUMMY_KEY_MARKER`) → inject built-in key; real caller key → forwarded unchanged (caller overrides); no built-in key configured → no-op (feature inert, verbatim forward). - **Excluded:** Bedrock (SigV4) / Vertex / Azure-AD (OAuth) — signed/exchanged credentials are never rewritten; those keep forwarding caller creds. ### Gemini mechanism decision Google's Generative Language API accepts the key via either `x-goog-api-key` **header** or a `?key=` query param. This PR uses the **header** form (consistent with the header-injection approach for every other provider, and no URL mutation). The `target: URL` param to `applyProviderAuth` is reserved (currently `void`) so a query-param scheme can be added later without a signature change. ## Red-Green Proof (real forwarded-header surface) The test stands up a **fake upstream HTTP server** that records the `Authorization` / `x-api-key` / `x-goog-api-key` it receives, points aimock's proxy at it in `--proxy-only` mode, sends a request through aimock, and asserts on the header the fake upstream actually saw. **RED** — injection call disabled in `recorder.ts`: ``` × GREEN: built-in key set + caller sends dummy → aimock injects its own key → expected 'Bearer sk-aimock-dev-ci-only' to be 'Bearer sk-real-test-123' × no caller credential + built-in key set → aimock injects its own key → expected undefined to be 'Bearer sk-real-test-123' × Anthropic: built-in key set + caller sends dummy → injects x-api-key → expected 'sk-aimock-dev-ci-only' to be 'sk-real-test-123' × Gemini: built-in key set + caller sends dummy → injects x-goog-api-key → expected 'sk-aimock-dev-ci-only' to be 'sk-real-test-123' Tests 4 failed | 6 passed (10) ``` The dummy `sk-aimock-dev-ci-only` reaching the upstream verbatim proves today's forwarding behavior. **GREEN** — feature enabled: ``` ✓ GREEN: built-in key set + caller sends dummy → aimock injects its own key (fake upstream saw: Authorization: Bearer sk-real-test-123) Test Files 1 passed (1) Tests 10 passed (10) ``` ### Backward-compat cases covered (all pass) - (a) Feature OFF (no built-in key) → dummy forwarded unchanged. - (b) Caller sends a REAL key (not `sk-aimock-`) → forwarded unchanged even with a built-in key set (caller overrides). - (c) A fixture-**matched** request never triggers injection (short-circuits before `proxyAndRecord`; fake upstream request count = 0). - (d) Anthropic uses `x-api-key`; Gemini uses `x-goog-api-key`. ## Files changed - `src/provider-auth.ts` (new) — injection logic + env reader + dummy marker. - `src/recorder.ts` — invoke `applyProviderAuth` after `buildForwardHeaders`. - `src/cli.ts` — plumb `providerKeys` from env into `RecordConfig`. - `src/types.ts` — add `RecordConfig.providerKeys`. - `src/__tests__/provider-auth.test.ts` (new) — real-surface red-green suite. ## Verification `prettier --check`, `eslint`, `tsc --noEmit`, `tsdown` build, and the full `vitest` suite (**4344 tests / 147 files**) all pass. --- **DRAFT** pending finalization of the concurrent design spec.
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
Opt-in, backward-compatible support for aimock owning the upstream provider API key. On a fixture-miss passthrough, aimock forwards the request to the real upstream. Callers commonly send a dummy placeholder key (e.g.
sk-aimock-…) only to satisfy an SDK's non-empty-key requirement; today that dummy is forwarded verbatim and the upstream rejects it. This PR lets aimock inject its own configured key in that case, while never clobbering a real caller key.Design
ps):AIMOCK_PROVIDER_OPENAI_KEY,AIMOCK_PROVIDER_ANTHROPIC_KEY,AIMOCK_PROVIDER_GEMINI_KEY→ plumbed into newRecordConfig.providerKeys(parallel toproviders), wired fromcli.tsviareadProviderKeysFromEnv().applyProviderAuth(forwardHeaders, target, providerKey, builtinKey)invoked immediately afterbuildForwardHeaders(req)on the proxy path. Provider-aware schemes:Authorization: Bearer <key>x-api-key: <key>x-goog-api-key: <key>sk-aimock-, overridable viaAIMOCK_DUMMY_KEY_MARKER) → inject built-in key; real caller key → forwarded unchanged (caller overrides); no built-in key configured → no-op (feature inert, verbatim forward).Gemini mechanism decision
Google's Generative Language API accepts the key via either
x-goog-api-keyheader or a?key=query param. This PR uses the header form (consistent with the header-injection approach for every other provider, and no URL mutation). Thetarget: URLparam toapplyProviderAuthis reserved (currentlyvoid) so a query-param scheme can be added later without a signature change.Red-Green Proof (real forwarded-header surface)
The test stands up a fake upstream HTTP server that records the
Authorization/x-api-key/x-goog-api-keyit receives, points aimock's proxy at it in--proxy-onlymode, sends a request through aimock, and asserts on the header the fake upstream actually saw.RED — injection call disabled in
recorder.ts:The dummy
sk-aimock-dev-ci-onlyreaching the upstream verbatim proves today's forwarding behavior.GREEN — feature enabled:
Backward-compat cases covered (all pass)
sk-aimock-) → forwarded unchanged even with a built-in key set (caller overrides).proxyAndRecord; fake upstream request count = 0).x-api-key; Gemini usesx-goog-api-key.Files changed
src/provider-auth.ts(new) — injection logic + env reader + dummy marker.src/recorder.ts— invokeapplyProviderAuthafterbuildForwardHeaders.src/cli.ts— plumbproviderKeysfrom env intoRecordConfig.src/types.ts— addRecordConfig.providerKeys.src/__tests__/provider-auth.test.ts(new) — real-surface red-green suite.Verification
prettier --check,eslint,tsc --noEmit,tsdownbuild, and the fullvitestsuite (4344 tests / 147 files) all pass.DRAFT pending finalization of the concurrent design spec.