Skip to content

feat: aimock-owned upstream provider keys on fixture-miss passthrough#293

Merged
jpr5 merged 4 commits into
mainfrom
feat/aimock-owns-provider-key
Jul 14, 2026
Merged

feat: aimock-owned upstream provider keys on fixture-miss passthrough#293
jpr5 merged 4 commits into
mainfrom
feat/aimock-owns-provider-key

Conversation

@jpr5

@jpr5 jpr5 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

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.

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.
@pkg-pr-new

pkg-pr-new Bot commented Jul 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@copilotkit/aimock@293

commit: 4753feb

@jpr5

jpr5 commented Jul 14, 2026

Copy link
Copy Markdown
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 (x-goog-api-key) to match what this PR already does — so code and spec are aligned. Kept draft pending sign-off.

@jpr5
jpr5 marked this pull request as ready for review July 14, 2026 04:35
@jpr5
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
jpr5 marked this pull request as ready for review July 14, 2026 04:46
@jpr5
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
jpr5 marked this pull request as ready for review July 14, 2026 05:22
@jpr5
jpr5 merged commit e6c3121 into main Jul 14, 2026
23 checks passed
@jpr5
jpr5 deleted the feat/aimock-owns-provider-key branch 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.
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