@coder/ai-sdk-provider 0.4.19 → 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.
- package/README.md +113 -202
- package/docs/security.md +159 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@coder/ai-sdk-provider)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
|
|
7
|
-
A
|
|
8
|
-
[AI Gateway](https://coder.com/docs/ai-coder/ai-gateway)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
upstream providers (Anthropic, OpenAI, Bedrock, Copilot, …),
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
70
|
-
decides which upstream handles the request.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
87
|
-
deployment may expose
|
|
88
|
-
Bedrock-backed `anthropic-bedrock`.
|
|
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
|
|
91
|
-
return a full sub-provider bound to that gateway provider, so one
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
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
|
-
**
|
|
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.
|
|
123
|
-
admins which provider names your deployment defines.
|
|
126
|
+
the models endpoint does not attribute models to providers.
|
|
124
127
|
|
|
125
|
-
|
|
126
|
-
`vendor/model` namespacing) — use whatever ids your deployment's providers accept.
|
|
128
|
+
### Embeddings are not supported yet
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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
|
-
|
|
146
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
[
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
- **
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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/`](
|
|
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)
|
package/docs/security.md
ADDED
|
@@ -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.
|
|
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",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
"provenance": true
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"@ai-sdk/anthropic": "^4.0.
|
|
51
|
+
"@ai-sdk/anthropic": "^4.0.60",
|
|
51
52
|
"@ai-sdk/openai-compatible": "^3.0.53",
|
|
52
53
|
"@ai-sdk/provider": "4.0.17"
|
|
53
54
|
},
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
56
57
|
"@types/node": "^26.6.2",
|
|
57
58
|
"@vitest/coverage-v8": "^5.0.1",
|
|
58
|
-
"ai": "^7.0.
|
|
59
|
+
"ai": "^7.0.111",
|
|
59
60
|
"publint": "^0.3.24",
|
|
60
61
|
"tsup": "^8.5.1",
|
|
61
62
|
"tsx": "^4.23.15",
|