@coder/ai-sdk-provider 0.4.20 → 0.4.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +113 -202
  2. package/docs/security.md +159 -0
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -4,12 +4,11 @@
4
4
  [![npm](https://img.shields.io/npm/v/@coder/ai-sdk-provider.svg)](https://www.npmjs.com/package/@coder/ai-sdk-provider)
5
5
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)
6
6
 
7
- A **Vercel AI SDK provider that routes requests through your Coder deployment's
8
- [AI Gateway](https://coder.com/docs/ai-coder/ai-gateway)** (formerly "AI Bridge"). Point it
9
- at your deployment URL, hand it a Coder API token, and use any model your
10
- deployment proxies with the AI SDK's `generateText` / `streamText` — the same way
11
- you'd use [OpenRouter](https://ai-sdk.dev/providers/community-providers/openrouter)
12
- or any other provider.
7
+ A Vercel AI SDK provider that routes requests through your Coder deployment's
8
+ [AI Gateway](https://coder.com/docs/ai-coder/ai-gateway) (formerly "AI Bridge").
9
+ Use any model your deployment proxies, the same way you'd use
10
+ [OpenRouter](https://ai-sdk.dev/providers/community-providers/openrouter) or any
11
+ other provider.
13
12
 
14
13
  ```ts
15
14
  import { generateText } from "ai";
@@ -26,24 +25,20 @@ const { text } = await generateText({
26
25
  });
27
26
  ```
28
27
 
29
- ## Why
30
-
31
- **AI Gateway** is Coder's LLM gateway: it sits between your AI tooling and the
32
- upstream providers (Anthropic, OpenAI, Bedrock, Copilot, …), authenticates each
33
- request against a Coder identity, injects the centrally-managed provider keys, and
34
- audits usage per user. This package lets the Vercel AI SDK speak to it natively, so
35
- your developers never handle raw provider keys — they authenticate with their Coder
36
- token and the deployment decides which models and providers are available.
37
-
38
- ## Provider vs. Agent
39
-
40
- This package is for **plain model calls** — `generateText`, `streamText`, and
41
- `generateObject` (schema‑constrained structured output). If you need Coder's
42
- **server‑side agent** — the multi‑step tool loop, built‑in tools, MCP servers, or
43
- workspace file/shell tools — use **[`@coder/ai-sdk-agent`](../agent)** instead.
44
- Rule of thumb: **need a model → provider; need server‑side tools, MCP, or a
45
- workspace → Agent.** They compose: use the provider for pure text/JSON steps and
46
- the Agent for the tool‑driven ones.
28
+ **Why:** in centralized mode (the default), developers authenticate with their
29
+ Coder token and never handle raw provider keys. AI Gateway authenticates each
30
+ request against a Coder identity, injects the centrally managed keys for the
31
+ upstream providers (Anthropic, OpenAI, Bedrock, Copilot, …), and audits usage
32
+ per user. In bring-your-own-key mode, developers supply their own upstream key
33
+ instead (see [Authentication](#authentication)). The deployment decides
34
+ which models and providers are available.
35
+
36
+ > [!TIP]
37
+ > This package is for **plain model calls**: `generateText`, `streamText`, and
38
+ > `generateObject` (schema-constrained structured output). For Coder's
39
+ > **server-side agent** (multi-step tool loop, built-in tools, MCP servers,
40
+ > workspace file/shell tools), use [`@coder/ai-sdk-agent`](https://github.com/coder/ai-sdk/tree/main/packages/agent). They
41
+ > compose: provider for pure text/JSON steps, Agent for tool-driven ones.
47
42
 
48
43
  ## Install
49
44
 
@@ -51,28 +46,31 @@ the Agent for the tool‑driven ones.
51
46
  pnpm add @coder/ai-sdk-provider ai zod
52
47
  ```
53
48
 
54
- Requires Node ≥ 22, `ai` v7, and a Coder deployment with **AI Gateway enabled**
55
- (stable since Coder **v2.29**, GA in v2.30, on by default in v2.34; requires the
56
- AI Governance Add-On).
49
+ Requirements:
50
+
51
+ - Node ≥ 22 and `ai` v7.
52
+ - A Coder deployment with **AI Gateway enabled**: stable since Coder **v2.29**,
53
+ GA in v2.30, on by default in v2.34. Requires the AI Governance Add-On.
57
54
 
58
55
  ## Named providers and the two wire protocols
59
56
 
60
- Your Coder admins define AI Gateway **providers** — named routes on the
61
- deployment (`/api/v2/aibridge/<name>/v1/…`), each speaking one of **two wire
62
- protocols** determined by its admin-configured type:
57
+ Coder admins define AI Gateway **providers**: named routes on the deployment
58
+ (`/api/v2/aibridge/<name>/v1/…`). Each speaks one of two wire protocols,
59
+ set by its admin-configured type:
63
60
 
64
61
  | Wire protocol | Provider types behind it | Default name |
65
62
  | ------------------------ | ------------------------------------------------------------------------------- | ------------ |
66
63
  | **OpenAI-compatible** | `openai`, `azure`, `google`, `copilot`, `openai-compat`, `openrouter`, `vercel` | `openai` |
67
64
  | **Anthropic-compatible** | `anthropic` (native Claude), `bedrock` (Bedrock-hosted Claude) | `anthropic` |
68
65
 
69
- Routing is **by URL path, not by model id** — the provider name in the URL
70
- decides which upstream handles the request. `createCoder` fronts the default
71
- `openai` / `anthropic` pair: the bare call `coder(modelId)` picks between them
72
- by heuristic (model ids starting with `claude`/`anthropic` go to the
73
- Anthropic-protocol provider, everything else to the OpenAI-protocol one), and
74
- the explicit accessors override it (e.g. to reach Claude through a
75
- Copilot-typed provider on the OpenAI protocol):
66
+ Routing is **by URL path, not by model id**: the provider name in the URL
67
+ decides which upstream handles the request.
68
+
69
+ `createCoder` fronts the default `openai` / `anthropic` pair. The bare call
70
+ `coder(modelId)` picks one by heuristic: ids starting with `claude` or
71
+ `anthropic` go to the Anthropic-protocol provider, everything else to the
72
+ OpenAI-protocol one. Explicit accessors override the heuristic, e.g. to reach
73
+ Claude through a Copilot-typed provider on the OpenAI protocol:
76
74
 
77
75
  ```ts
78
76
  coder("gpt-4o"); // → `openai` provider
@@ -81,16 +79,20 @@ coder.openai("claude-sonnet-4"); // → `openai` provider (e.g. Copilot)
81
79
  coder.anthropic("claude-opus-4-5"); // → `anthropic` provider (explicit)
82
80
  ```
83
81
 
82
+ Model ids pass through **unchanged** to the upstream provider (no
83
+ `vendor/model` namespacing). Use whatever ids your deployment's providers
84
+ accept.
85
+
84
86
  ### Custom-named providers
85
87
 
86
- Provider names are admin-chosen (matching `^[a-z0-9]+(-[a-z0-9]+)*$`), so a
87
- deployment may expose e.g. an Azure-backed `azure-openai` next to a
88
- Bedrock-backed `anthropic-bedrock`. Reach them in two ways:
88
+ Provider names are admin-chosen and match `^[a-z0-9]+(-[a-z0-9]+)*$`. A
89
+ deployment may expose, say, an Azure-backed `azure-openai` next to a
90
+ Bedrock-backed `anthropic-bedrock`. There are two ways to reach them.
89
91
 
90
- **Sub-provider accessors** — `openaiProvider(name)` / `anthropicProvider(name)`
91
- return a full sub-provider bound to that gateway provider, so one
92
- `createCoder` instance can target any number of providers. Pick the accessor
93
- matching the provider's wire protocol:
92
+ **Sub-provider accessors.** `openaiProvider(name)` / `anthropicProvider(name)`
93
+ return a full sub-provider bound to that gateway provider, so one `createCoder`
94
+ instance can target any number of providers. Pick the accessor that matches the
95
+ provider's wire protocol:
94
96
 
95
97
  ```ts
96
98
  const azure = coder.openaiProvider("azure-openai"); // OpenAI-compatible type
@@ -100,12 +102,14 @@ await generateText({ model: azure("gpt-4o"), prompt: "Hi" });
100
102
  await generateText({ model: bedrock("claude-sonnet-4-6"), prompt: "Hi" });
101
103
  ```
102
104
 
103
- A name outside the gateway's grammar throws the AI SDK's
104
- `InvalidArgumentError` at accessor time (such a name can never be registered);
105
- a well-formed name that is not configured on your deployment fails at request
106
- time with the gateway's 404.
105
+ | Name | Fails with |
106
+ | -------------------------------------------- | ---------------------------------------------- |
107
+ | Outside the gateway's grammar | AI SDK `InvalidArgumentError` at accessor time |
108
+ | Well-formed but not configured on deployment | The gateway's 404 at request time |
109
+
110
+ A name outside the grammar can never be registered, so it fails early.
107
111
 
108
- **Re-pointing the defaults** — when your deployment simply names its one
112
+ **Re-pointing the defaults.** If your deployment just names its one
109
113
  OpenAI/Anthropic pair differently, override the names once and keep using the
110
114
  bare call and the `openai` / `anthropic` accessors:
111
115
 
@@ -117,34 +121,40 @@ const renamed = createCoder({
117
121
  });
118
122
  ```
119
123
 
120
- **Provider names come from your platform team.** Discovery is admin-only
124
+ **Ask your Coder admins for provider names.** Discovery is admin-only
121
125
  server-side: `GET /api/v2/ai/providers` returns `403` for regular users, and
122
- the models endpoint does not attribute models to providers. Ask your Coder
123
- admins which provider names your deployment defines.
126
+ the models endpoint does not attribute models to providers.
124
127
 
125
- Model ids are passed through **unchanged** to the upstream provider (no
126
- `vendor/model` namespacing) — use whatever ids your deployment's providers accept.
128
+ ### Embeddings are not supported yet
127
129
 
128
- **Embeddings are not supported yet.** AI Gateway does not intercept
129
- `/v1/embeddings`, so `coder.textEmbeddingModel(id)` — and the embedding
130
- accessors on `coder.openai` — throw the AI SDK's `NoSuchModelError`
131
- immediately instead of emitting a request the gateway rejects with a 404. The
132
- accessors stay so they can light up without a breaking change once the gateway
133
- adds an embeddings route — see
134
- [coder/ai-sdk#69](https://github.com/coder/ai-sdk/issues/69).
130
+ `coder.textEmbeddingModel(id)` and the embedding accessors on `coder.openai`
131
+ throw the AI SDK's `NoSuchModelError` immediately.
132
+
133
+ <details>
134
+ <summary>Why embeddings throw instead of sending a request</summary>
135
+
136
+ AI Gateway does not intercept `/v1/embeddings`, so a request would be rejected
137
+ with a 404. Throwing up front avoids emitting it. The accessors stay so they
138
+ can light up without a breaking change once the gateway adds an embeddings
139
+ route. See [coder/ai-sdk#69](https://github.com/coder/ai-sdk/issues/69).
140
+
141
+ </details>
135
142
 
136
143
  ## Authentication
137
144
 
138
- **Centralized mode (default).** Pass your Coder API token as `apiKey`. AI Gateway
139
- holds the upstream provider keys and brokers the call — this is all you need:
145
+ | Mode | Set | Upstream provider key |
146
+ | ------------------------- | ------------------------------------------------------ | ------------------------------------------ |
147
+ | **Centralized** (default) | `apiKey`: your Coder API token | Held by AI Gateway, which brokers the call |
148
+ | **Bring-your-own-key** | `coderToken`: Coder token; `apiKey`: your upstream key | Yours, forwarded to the upstream |
149
+
150
+ Centralized mode is all most apps need:
140
151
 
141
152
  ```ts
142
153
  createCoder({ baseURL: "https://coder.example.com", apiKey: coderToken });
143
154
  ```
144
155
 
145
- **Bring-your-own-key (BYOK) mode.** Set `coderToken` (sent in the
146
- `X-Coder-AI-Governance-Token` header to authenticate you to the gateway) and pass
147
- your _upstream_ provider key as `apiKey` (forwarded to the upstream):
156
+ BYOK sends `coderToken` in the `X-Coder-AI-Governance-Token` header to
157
+ authenticate you to the gateway, and forwards `apiKey` to the upstream:
148
158
 
149
159
  ```ts
150
160
  createCoder({
@@ -156,150 +166,51 @@ createCoder({
156
166
 
157
167
  ## Configuration
158
168
 
159
- | Option | Type | Default | Description |
160
- | --------------- | ------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- |
161
- | `baseURL` | `string` | — (required) | Your Coder deployment URL, e.g. `https://coder.example.com`. The AI Gateway path is appended for you. |
162
- | `apiKey` | `string` | — | Coder API token (centralized) or upstream key (BYOK). |
163
- | `coderToken` | `string` | — | Enables BYOK mode; sent in `X-Coder-AI-Governance-Token`. |
164
- | `headers` | `Record<string,string>` | — | Extra headers merged into every request. |
165
- | `aiGatewayPath` | `string` | `/api/v2/aibridge` | Override if your deployment uses a different mount path. |
166
- | `providers` | `{ openai?, anthropic? }` | `openai` / `anthropic` | Re-point the default pair at differently-named providers. |
167
- | `fetch` | `typeof fetch` | global `fetch` | Custom fetch (testing / middleware). |
169
+ | Option | Type | Default | Description |
170
+ | --------------- | ------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- |
171
+ | `baseURL` | `string` | — (required) | Your Coder deployment URL, e.g. `https://coder.example.com`. The AI Gateway path is appended for you. |
172
+ | `apiKey` | `string` | — | Coder API token (centralized) or upstream key (BYOK). |
173
+ | `coderToken` | `string` | — | Enables BYOK mode; sent in `X-Coder-AI-Governance-Token`. |
174
+ | `headers` | `Record<string,string>` | — | Extra headers merged into every request. |
175
+ | `aiGatewayPath` | `string` | `/api/v2/aibridge` | Override if your deployment uses a different mount path. Use the canonical `/api/v2/ai-gateway` with Coder v2.35.0+. |
176
+ | `providers` | `{ openai?, anthropic? }` | `openai` / `anthropic` | Re-point the default pair at differently-named providers. |
177
+ | `fetch` | `typeof fetch` | global `fetch` | Custom fetch (testing / middleware). |
168
178
 
169
179
  ## Enterprise governance & security
170
180
 
171
- Reference for security reviewers evaluating this package. The boundary between
172
- the two kinds of claims below matters: **client behavior** (what this package
173
- puts on the wire) is verifiable in [`src/provider.ts`](./src/provider.ts) —
174
- ~250 lines with no dependencies beyond the official AI SDK provider packages —
175
- while **gateway behavior** (key custody, audit capture, retention) is a
176
- property of your Coder deployment, enforced server-side regardless of what any
177
- client does, and documented in the
178
- [AI Gateway docs](https://coder.com/docs/ai-coder/ai-gateway).
179
-
180
- ### Data flow
181
-
182
- ```text
183
- your app ──HTTP(S)──▶ your Coder deployment ──▶ upstream provider
184
- (this package) (AI Gateway intercepts (Anthropic, OpenAI,
185
- /api/v2/aibridge/…) Bedrock, Copilot, …)
186
- ```
187
-
188
- **What leaves your app.** This package's only network destination is the
189
- `baseURL` you configure. It never contacts upstream vendors directly and adds
190
- no telemetry of its own; each request is exactly what the AI SDK builds for a
191
- normal provider call:
192
-
193
- - **URL** — `POST <baseURL>/api/v2/aibridge/<provider>/v1/chat/completions`
194
- (OpenAI surface) or `…/v1/messages` (Anthropic surface). `/api/v2/aibridge`
195
- is this package's default for compatibility. Use the canonical
196
- `/api/v2/ai-gateway` path with Coder v2.35.0 or later (see `aiGatewayPath`).
197
- - **Auth headers** — per the mode matrix below.
198
- - **Body** — standard OpenAI-/Anthropic-format JSON: the model id (passed
199
- through unchanged), your full prompt/message content, tool definitions, and
200
- sampling parameters. Prompt content is visible to the Gateway — that is what
201
- enables auditing.
202
- - Anything you add via the `headers` option.
203
-
204
- Transport security follows the scheme of your `baseURL` — the client does not
205
- enforce `https://` (an `http://` URL sends tokens, keys, and prompts in
206
- plaintext). Always use an HTTPS deployment URL outside trusted local
207
- environments.
208
-
209
- **What the Gateway does before forwarding** _(server-side —
210
- [authentication docs](https://coder.com/docs/ai-coder/ai-gateway/auth))_: it
211
- authenticates the token as an active Coder user and rejects the request
212
- outright when it is missing or invalid (nothing is forwarded upstream), strips
213
- all Coder credentials from the outbound request, and attaches the upstream
214
- credential for the mode in use.
215
-
216
- ### Credential isolation: centralized vs. BYOK
217
-
218
- | | **Centralized (default)** | **BYOK** |
219
- | ------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
220
- | Your app holds | Coder API token only | Coder API token **+** the user's own upstream key |
221
- | On the wire | `Authorization: Bearer <Coder token>` (both surfaces) | `X-Coder-AI-Governance-Token: <Coder token>`; upstream key in `Authorization: Bearer` (OpenAI surface) / `x-api-key` (Anthropic surface) |
222
- | Upstream provider keys live | On the deployment, admin-configured — never distributed to developers | With the individual user; forwarded per request, bypassing the deployment's central key pool |
223
- | Coder token forwarded upstream? | **No** — stripped and replaced by the deployment's provider key | **No** — the governance header is stripped before forwarding |
224
- | Admin control | Provider and key configuration, rotation, failover | Can be disabled deployment-wide (`CODER_AI_GATEWAY_ALLOW_BYOK=false` rejects requests carrying the governance header with `403`) |
225
-
226
- Both modes are audited identically: audit records store _which credential
227
- kind_ was used (`centralized` / `byok`), not the credential itself.
228
-
229
- ### Audit capture
230
-
231
- Every request is attributed to the Coder user whose token authenticated it —
232
- per user, per request, in both modes. Per intercepted request the Gateway
233
- records _(server-side —
234
- [audit docs](https://coder.com/docs/ai-coder/ai-gateway/audit))_:
235
-
236
- - **Identity & metadata** — initiating user, provider, model, client,
237
- credential kind, timestamps.
238
- - **Last user prompt** — earlier turns and system prompts are not stored.
239
- - **Token usage** — input / output / cache counts.
240
- - **Tool calls** — tool name and arguments; tool _results_ are not stored.
241
- - **Model reasoning** — extended-thinking / reasoning-summary content when
242
- present.
243
-
244
- Model-generated response text is **discarded**, not stored. Retention defaults
245
- to **60 days** and is configurable (`CODER_AI_GATEWAY_RETENTION`; `0` keeps
246
- data indefinitely). Auditors browse sessions and causal tool-call chains in
247
- the deployment dashboard under `/ai-gateway/sessions`.
248
-
249
- ### Required Coder permissions
250
-
251
- | To… | You need |
252
- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
253
- | Send requests through the Gateway | An active Coder user with a valid API token — no extra role. (RBAC: members hold create/update on `aibridge_interception`, i.e. recording their own traffic.) |
254
- | Read audit data (prompts, tool calls, usage) | The **Owner** or **Auditor** role. Regular members cannot read interceptions back — not even their own. |
255
- | Configure providers / keys, toggle BYOK | Deployment administrator (server flags / deployment configuration). |
256
- | Use the feature at all | A deployment licensed for [AI Governance](https://coder.com/docs/ai-coder/ai-governance) — the Gateway is license-gated server-side. |
257
-
258
- ### Security FAQ
259
-
260
- - **Does prompt data ever go anywhere other than my deployment?** This package
261
- only ever _initiates_ requests to `baseURL`; onward traffic to the upstream
262
- vendor originates from your deployment (or its standalone gateway replicas),
263
- using the providers your admins configured. One standard-`fetch` caveat:
264
- redirects are followed by default, so a cross-origin redirect issued by your
265
- deployment or an intermediary would resend the request — prompt body and
266
- non-`Authorization` headers (in BYOK mode that includes `x-api-key` and the
267
- governance token) — to the redirect target. To forbid this, supply a custom
268
- fetch: `fetch: (url, init) => fetch(url, { ...init, redirect: "error" })`.
269
- - **Do developers ever handle raw provider keys?** Centralized mode: no —
270
- developers only ever hold a Coder token. BYOK mode: they supply their own
271
- personal key, which is forwarded per request without entering central custody.
272
- - **What is the blast radius of a leaked Coder token?** A Coder API token is
273
- not an AI-only credential — it grants the bearer the user's **full Coder API
274
- permissions** (workspaces, templates, and anything else that user's roles
275
- allow), and, until revoked or expired, AI usage through your Gateway (fully
276
- attributed to that user). What it can _not_ do is authenticate to upstream
277
- vendors: under normal Gateway forwarding it is stripped and does not leave
278
- your deployment (the cross-origin-redirect caveat above is the exception —
279
- a redirect can resend non-`Authorization` headers, including the governance
280
- header, before the Gateway ever sees them). Treat a leak as a Coder account
281
- compromise — revoke the token — and prefer short-lived, dedicated tokens
282
- for AI workloads.
283
- - **Is model-generated content stored?** Partially. Assistant _response text_
284
- is discarded, but two model-generated artifacts are retained for auditing:
285
- reasoning content (extended thinking / reasoning summaries) and tool-call
286
- arguments — alongside the last user prompt and token counts.
287
- - **Can I verify the client claims myself?** Yes: [`src/provider.ts`](./src/provider.ts)
288
- is the entire wire-facing surface — it only selects base URLs and auth
289
- headers, then delegates request construction to the official AI SDK provider
290
- packages. [`test/provider.test.ts`](./test/provider.test.ts) asserts the
291
- request URL, auth headers, and model pass-through for the chat/messages
292
- routes in both auth modes; the underlying AI SDK packages add their own
293
- protocol headers (e.g. `anthropic-version`) and are not re-tested here.
181
+ The full reference for security reviewers is in
182
+ [`docs/security.md`](./docs/security.md): data flow, credential isolation,
183
+ audit capture, required permissions, and a security FAQ.
184
+
185
+ Key points:
186
+
187
+ - **Client vs. gateway.** What this package puts on the wire is verifiable in
188
+ [`src/provider.ts`](https://github.com/coder/ai-sdk/blob/main/packages/provider/src/provider.ts). Key custody, audit capture, and
189
+ retention are enforced server-side by your Coder deployment.
190
+ - **One destination.** The package only initiates requests to your `baseURL`.
191
+ It never contacts upstream vendors directly and adds no telemetry.
192
+ - **Use HTTPS outside trusted local environments.** The client does not
193
+ enforce `https://`; an `http://` URL sends tokens, keys, and prompts in
194
+ plaintext.
195
+ - **Redirects are followed by default.** A cross-origin redirect would resend
196
+ the prompt body and non-`Authorization` headers (in BYOK mode, including
197
+ `x-api-key` and the governance token) to the redirect target. Pass
198
+ `fetch: (url, init) => fetch(url, { ...init, redirect: "error" })` to forbid
199
+ it.
200
+ - **Coder tokens are not AI-only.** A token grants the user's full Coder API
201
+ permissions. Treat a leak as a Coder account compromise (revoke the token)
202
+ and prefer short-lived, dedicated tokens for AI workloads.
203
+ - **Audit.** Every request is attributed to the authenticating Coder user. The
204
+ Gateway stores the last user prompt, tool calls (name and arguments),
205
+ reasoning content when present, token usage, and metadata. Response text is
206
+ discarded. Retention defaults to 60 days and is configurable.
294
207
 
295
208
  ## Examples
296
209
 
297
- Runnable scripts live in [`examples/`](./examples) (run against a real deployment via `tsx`):
210
+ Runnable scripts live in [`examples/`](https://github.com/coder/ai-sdk/tree/main/packages/provider/examples); see its
211
+ [README](https://github.com/coder/ai-sdk/blob/main/packages/provider/examples/README.md) for setup.
298
212
 
299
213
  ```bash
300
- export CODER_URL=https://coder.example.com
301
- export CODER_API_TOKEN=$(coder tokens create --name ai-sdk-provider-example)
302
-
303
214
  pnpm example:generate # non-streaming generateText
304
215
  pnpm example:stream # streaming streamText
305
216
  pnpm example:anthropic # the Anthropic surface (native Claude)
@@ -0,0 +1,159 @@
1
+ # Enterprise governance & security
2
+
3
+ Reference for security reviewers evaluating `@coder/ai-sdk-provider`.
4
+
5
+ Two kinds of claims appear below. Keep them apart:
6
+
7
+ | Claim kind | Covers | Where it is enforced / verifiable |
8
+ | -------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
9
+ | **Client behavior** | What this package puts on the wire | [`src/provider.ts`](https://github.com/coder/ai-sdk/blob/main/packages/provider/src/provider.ts): ~250 lines, no dependencies beyond the official AI SDK provider packages |
10
+ | **Gateway behavior** | Key custody, audit capture, retention | Your Coder deployment, server-side, regardless of what any client does. See the [AI Gateway docs](https://coder.com/docs/ai-coder/ai-gateway) |
11
+
12
+ ## Data flow
13
+
14
+ ```text
15
+ your app ──HTTP(S)──▶ your Coder deployment ──▶ upstream provider
16
+ (this package) (AI Gateway intercepts (Anthropic, OpenAI,
17
+ /api/v2/aibridge/…) Bedrock, Copilot, …)
18
+ ```
19
+
20
+ ### What leaves your app
21
+
22
+ The only network destination is the `baseURL` you configure. The package never
23
+ contacts upstream vendors directly and adds no telemetry of its own. Each
24
+ request is exactly what the AI SDK builds for a normal provider call:
25
+
26
+ - **URL:** `POST <baseURL>/api/v2/aibridge/<provider>/v1/chat/completions`
27
+ (OpenAI surface) or `…/v1/messages` (Anthropic surface). `/api/v2/aibridge`
28
+ is this package's default for compatibility. Use the canonical
29
+ `/api/v2/ai-gateway` path with Coder v2.35.0 or later (see `aiGatewayPath`).
30
+ - **Auth headers:** per the [mode matrix](#credential-isolation-centralized-vs-byok).
31
+ - **Body:** standard OpenAI-/Anthropic-format JSON: the model id (passed
32
+ through unchanged), your full prompt/message content, tool definitions, and
33
+ sampling parameters. Prompt content is visible to the Gateway; that is what
34
+ enables auditing.
35
+ - Anything you add via the `headers` option.
36
+
37
+ **Transport security follows your `baseURL` scheme.** The client does not
38
+ enforce `https://`: an `http://` URL sends tokens, keys, and prompts in
39
+ plaintext. Always use an HTTPS deployment URL outside trusted local
40
+ environments.
41
+
42
+ ### What the Gateway does before forwarding
43
+
44
+ _Server-side; see the [authentication docs](https://coder.com/docs/ai-coder/ai-gateway/auth)._
45
+
46
+ 1. Authenticates the token as an active Coder user. A missing or invalid token
47
+ is rejected outright; nothing is forwarded upstream.
48
+ 2. Strips all Coder credentials from the outbound request.
49
+ 3. Attaches the upstream credential for the mode in use.
50
+
51
+ ## Credential isolation: centralized vs. BYOK
52
+
53
+ | | **Centralized (default)** | **BYOK** |
54
+ | ------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
55
+ | Your app holds | Coder API token only | Coder API token **+** the user's own upstream key |
56
+ | On the wire | `Authorization: Bearer <Coder token>` (both surfaces) | `X-Coder-AI-Governance-Token: <Coder token>`; upstream key in `Authorization: Bearer` (OpenAI surface) / `x-api-key` (Anthropic surface) |
57
+ | Upstream provider keys live | On the deployment, admin-configured — never distributed to developers | With the individual user; forwarded per request, bypassing the deployment's central key pool |
58
+ | Coder token forwarded upstream? | **No** — stripped and replaced by the deployment's provider key | **No** — the governance header is stripped before forwarding |
59
+ | Admin control | Provider and key configuration, rotation, failover | Can be disabled deployment-wide (`CODER_AI_GATEWAY_ALLOW_BYOK=false` rejects requests carrying the governance header with `403`) |
60
+
61
+ Both modes are audited identically. Audit records store _which credential
62
+ kind_ was used (`centralized` / `byok`), not the credential itself.
63
+
64
+ ## Audit capture
65
+
66
+ _Server-side; see the [audit docs](https://coder.com/docs/ai-coder/ai-gateway/audit)._
67
+
68
+ Every request is attributed to the Coder user whose token authenticated it:
69
+ per user, per request, in both modes. For each intercepted request the Gateway
70
+ records:
71
+
72
+ | Recorded | Details |
73
+ | ------------------- | --------------------------------------------------------------------- |
74
+ | Identity & metadata | Initiating user, provider, model, client, credential kind, timestamps |
75
+ | Last user prompt | Earlier turns and system prompts are not stored |
76
+ | Token usage | Input / output / cache counts |
77
+ | Tool calls | Tool name and arguments; tool _results_ are not stored |
78
+ | Model reasoning | Extended-thinking / reasoning-summary content when present |
79
+
80
+ - Model-generated response text is **discarded**, not stored.
81
+ - Retention defaults to **60 days** and is configurable
82
+ (`CODER_AI_GATEWAY_RETENTION`; `0` keeps data indefinitely).
83
+ - Auditors browse sessions and causal tool-call chains in the deployment
84
+ dashboard under `/ai-gateway/sessions`.
85
+
86
+ ## Required Coder permissions
87
+
88
+ | To… | You need |
89
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
90
+ | Send requests through the Gateway | An active Coder user with a valid API token — no extra role. (RBAC: members hold create/update on `aibridge_interception`, i.e. recording their own traffic.) |
91
+ | Read audit data (prompts, tool calls, usage) | The **Owner** or **Auditor** role. Regular members cannot read interceptions back — not even their own. |
92
+ | Configure providers / keys, toggle BYOK | Deployment administrator (server flags / deployment configuration). |
93
+ | Use the feature at all | A deployment licensed for [AI Governance](https://coder.com/docs/ai-coder/ai-governance) — the Gateway is license-gated server-side. |
94
+
95
+ ## Security FAQ
96
+
97
+ ### Does prompt data ever go anywhere other than my deployment?
98
+
99
+ This package only ever _initiates_ requests to `baseURL`. Onward traffic to the
100
+ upstream vendor originates from your deployment (or its standalone gateway
101
+ replicas), using the providers your admins configured.
102
+
103
+ **Redirect caveat (standard `fetch`):** redirects are followed by default. A
104
+ cross-origin redirect issued by your deployment or an intermediary would
105
+ resend the request to the redirect target: the prompt body and
106
+ non-`Authorization` headers (in BYOK mode that includes `x-api-key` and the
107
+ governance token). To forbid this, supply a custom fetch:
108
+
109
+ ```ts
110
+ createCoder({
111
+ baseURL: "https://coder.example.com",
112
+ apiKey: process.env.CODER_API_TOKEN!,
113
+ fetch: (url, init) => fetch(url, { ...init, redirect: "error" }),
114
+ });
115
+ ```
116
+
117
+ ### Do developers ever handle raw provider keys?
118
+
119
+ - **Centralized mode:** no. Developers only ever hold a Coder token.
120
+ - **BYOK mode:** they supply their own personal key, which is forwarded per
121
+ request without entering central custody.
122
+
123
+ ### What is the blast radius of a leaked Coder token?
124
+
125
+ A Coder API token is not an AI-only credential. Until revoked or expired, it
126
+ grants the bearer:
127
+
128
+ - the user's **full Coder API permissions** (workspaces, templates, and
129
+ anything else that user's roles allow), and
130
+ - AI usage through your Gateway, fully attributed to that user.
131
+
132
+ It can _not_ authenticate to upstream vendors. Under normal Gateway forwarding
133
+ it is stripped and does not leave your deployment. The exception is the
134
+ [cross-origin-redirect caveat](#does-prompt-data-ever-go-anywhere-other-than-my-deployment):
135
+ a redirect can resend non-`Authorization` headers, including the governance
136
+ header, before the Gateway ever sees them.
137
+
138
+ Treat a leak as a Coder account compromise: revoke the token. Prefer
139
+ short-lived, dedicated tokens for AI workloads.
140
+
141
+ ### Is model-generated content stored?
142
+
143
+ Partially. Assistant _response text_ is discarded, but two model-generated
144
+ artifacts are retained for auditing: reasoning content (extended thinking /
145
+ reasoning summaries) and tool-call arguments. The last user prompt and token
146
+ counts are retained alongside them.
147
+
148
+ ### Can I verify the client claims myself?
149
+
150
+ Yes.
151
+
152
+ - [`src/provider.ts`](https://github.com/coder/ai-sdk/blob/main/packages/provider/src/provider.ts) is the entire wire-facing surface. It
153
+ only selects base URLs and auth headers, then delegates request construction
154
+ to the official AI SDK provider packages.
155
+ - [`test/provider.test.ts`](https://github.com/coder/ai-sdk/blob/main/packages/provider/test/provider.test.ts) asserts the request URL,
156
+ auth headers, and model pass-through for the chat/messages routes in both
157
+ auth modes.
158
+ - The underlying AI SDK packages add their own protocol headers (e.g.
159
+ `anthropic-version`) and are not re-tested here.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coder/ai-sdk-provider",
3
- "version": "0.4.20",
3
+ "version": "0.4.21",
4
4
  "description": "Vercel AI SDK provider for Coder AI Gateway. Point the AI SDK at your Coder deployment with a Coder API token and use any model your deployment proxies — across AI Gateway's OpenAI- and Anthropic-compatible surfaces.",
5
5
  "keywords": [
6
6
  "ai",
@@ -27,6 +27,7 @@
27
27
  },
28
28
  "files": [
29
29
  "dist",
30
+ "docs",
30
31
  "README.md"
31
32
  ],
32
33
  "type": "module",