zuplo 7.7.16 → 7.8.0
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/docs/ai-gateway/bedrock-runtime.mdx +317 -0
- package/docs/ai-gateway/providers.mdx +30 -0
- package/docs/ai-gateway/universal-api.mdx +30 -0
- package/docs/ai-gateway/usage-limits.mdx +34 -0
- package/docs/dev-portal/zudoku/components/stepper.mdx +34 -1
- package/docs/dev-portal/zudoku/configuration/llms.md +4 -0
- package/docs/policies/ai-gateway-auth-v2-inbound/doc.md +5 -2
- package/docs/policies/ai-gateway-configuration-executor-v2-inbound/doc.md +26 -2
- package/docs/policies/ai-gateway-configuration-loader-v2-inbound/doc.md +0 -2
- package/docs/policies/ai-gateway-dlp-inbound/schema.json +34 -10
- package/docs/policies/ai-gateway-fallback-model-v2-inbound/doc.md +0 -2
- package/docs/policies/ai-gateway-metering-v2-inbound/doc.md +0 -2
- package/docs/policies/ai-gateway-model-filtering-v2-inbound/doc.md +0 -2
- package/docs/policies/ai-gateway-semantic-cache-v2-inbound/doc.md +0 -2
- package/docs/policies/ai-gateway-smart-router-inbound/doc.md +7 -2
- package/docs/policies/akamai-ai-firewall-v2-inbound/doc.md +0 -2
- package/docs/policies/auth0-jwt-auth-inbound/doc.md +1 -1
- package/docs/policies/comet-opik-tracing-v2-inbound/doc.md +0 -2
- package/docs/policies/galileo-tracing-v2-inbound/doc.md +0 -2
- package/docs/policies/mcp-auth0-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-auth0-oauth-inbound/schema.json +2 -2
- package/docs/policies/mcp-clerk-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-cognito-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-entra-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-google-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-keycloak-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-logto-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-oauth-inbound/schema.json +1 -1
- package/docs/policies/mcp-okta-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-onelogin-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-ping-oauth-inbound/doc.md +0 -2
- package/docs/policies/mcp-token-exchange-inbound/doc.md +48 -2
- package/docs/policies/mcp-token-exchange-inbound/schema.json +8 -0
- package/docs/policies/mcp-workos-oauth-inbound/doc.md +0 -2
- package/docs/policies/monetization-inbound/doc.md +10 -2
- package/docs/policies/propel-auth-jwt-inbound/doc.md +1 -1
- package/docs/policies/secret-masking-outbound/doc.md +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Using Bedrock Runtime
|
|
3
|
+
sidebar_label: Bedrock Runtime
|
|
4
|
+
description:
|
|
5
|
+
Put the AI Gateway in front of Amazon Bedrock's native API without changing
|
|
6
|
+
your code. Point your AWS SDK client at your app and keep every call site.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Bedrock Runtime** is
|
|
10
|
+
[Amazon Bedrock's native API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_Operations_Amazon_Bedrock_Runtime.html)—the
|
|
11
|
+
one the AWS SDKs call when you use `bedrock-runtime` from `@aws-sdk/*`, `boto3`,
|
|
12
|
+
or the Go and Java SDKs. Adding it as a provider lets those clients keep working
|
|
13
|
+
exactly as they are, with the gateway in the path for authentication, model
|
|
14
|
+
filtering, usage metering, and cost tracking.
|
|
15
|
+
|
|
16
|
+
This provider exists for one job: **you change the client's configuration, not
|
|
17
|
+
your code.** Point the client's `endpoint` at your app's URL, give it the app's
|
|
18
|
+
API key instead of AWS credentials, and every `ConverseCommand`,
|
|
19
|
+
`InvokeModelCommand`, and streaming call you already wrote keeps running.
|
|
20
|
+
|
|
21
|
+
Requests reach your own AWS account on your own AWS billing, signed by the
|
|
22
|
+
gateway with an IAM key pair you configure once.
|
|
23
|
+
|
|
24
|
+
## Bedrock Runtime or Bedrock Mantle?
|
|
25
|
+
|
|
26
|
+
Zuplo supports two Amazon Bedrock providers. Both reach your own AWS account—
|
|
27
|
+
what separates them is the API your client speaks.
|
|
28
|
+
|
|
29
|
+
| | Bedrock Runtime | [Bedrock Mantle](./bedrock-mantle.mdx) |
|
|
30
|
+
| ----------------- | ----------------------------------------------- | -------------------------------------- |
|
|
31
|
+
| API your app uses | Bedrock's native API | OpenAI- or Anthropic-shaped |
|
|
32
|
+
| Client | an AWS SDK | any OpenAI or Anthropic client |
|
|
33
|
+
| Gateway path | `/{app_id}/model/{modelId}/{operation}` | `/{app_id}/v1/...` |
|
|
34
|
+
| Credential | long-term IAM access key pair | long-term Bedrock API key (`ABSK`) |
|
|
35
|
+
| Best for | existing AWS SDK code you don't want to rewrite | new code, or one API across providers |
|
|
36
|
+
|
|
37
|
+
Choose **Bedrock Runtime** when you already have AWS SDK code. Choose **Bedrock
|
|
38
|
+
Mantle** when you want Bedrock models through the same
|
|
39
|
+
[Universal API](./universal-api.mdx) as every other provider.
|
|
40
|
+
|
|
41
|
+
The two need separate provider configurations even for the same AWS account: a
|
|
42
|
+
provider holds exactly one credential, and a Bedrock API key can't be used as an
|
|
43
|
+
IAM access key pair.
|
|
44
|
+
|
|
45
|
+
## Supported operations
|
|
46
|
+
|
|
47
|
+
The gateway serves the four Bedrock Runtime operations at their AWS paths:
|
|
48
|
+
|
|
49
|
+
| Operation | Path | Streaming |
|
|
50
|
+
| ------------------------------- | ---------------------------------------------- | --------- |
|
|
51
|
+
| `Converse` | `/model/{modelId}/converse` | No |
|
|
52
|
+
| `ConverseStream` | `/model/{modelId}/converse-stream` | Yes |
|
|
53
|
+
| `InvokeModel` | `/model/{modelId}/invoke` | No |
|
|
54
|
+
| `InvokeModelWithResponseStream` | `/model/{modelId}/invoke-with-response-stream` | Yes |
|
|
55
|
+
|
|
56
|
+
Your request and response bodies pass through unchanged, so anything these
|
|
57
|
+
operations support works—tool use, guardrail configuration,
|
|
58
|
+
`additionalModelRequestFields`, and model-native `InvokeModel` bodies the
|
|
59
|
+
gateway has no knowledge of. Streaming responses keep Bedrock's
|
|
60
|
+
`application/vnd.amazon.eventstream` framing, so the SDK's own decoder reads
|
|
61
|
+
them as it always has.
|
|
62
|
+
|
|
63
|
+
Your request headers reach Bedrock as well, including the `x-amzn-bedrock-*`
|
|
64
|
+
headers and the `x-amz-*` ones your SDK sets. The gateway removes only what it
|
|
65
|
+
owns: the `Authorization` header carrying your app's API key, the SigV4 headers
|
|
66
|
+
it replaces with its own signature (`x-amz-date`, `x-amz-content-sha256`, and
|
|
67
|
+
`x-amz-security-token`), cookies, the hop-by-hop headers, and the `zp-` and
|
|
68
|
+
`cf-` headers added on the way in. It sets `content-type` itself.
|
|
69
|
+
|
|
70
|
+
`CountTokens` and `InvokeModelWithBidirectionalStream` aren't served. Calling
|
|
71
|
+
either returns a `400 ValidationException` naming the operations that are.
|
|
72
|
+
|
|
73
|
+
:::note
|
|
74
|
+
|
|
75
|
+
A Bedrock Runtime provider serves only these native paths. It doesn't serve the
|
|
76
|
+
[Universal API](./universal-api.mdx) endpoints (`/v1/chat/completions`,
|
|
77
|
+
`/v1/messages`, `/v1/responses`, `/v1/embeddings`), and its models don't appear
|
|
78
|
+
in an app's `GET /v1/models` listing. Add [Bedrock Mantle](./bedrock-mantle.mdx)
|
|
79
|
+
alongside it if you want both.
|
|
80
|
+
|
|
81
|
+
:::
|
|
82
|
+
|
|
83
|
+
## Before you begin
|
|
84
|
+
|
|
85
|
+
You need:
|
|
86
|
+
|
|
87
|
+
- An AWS account with access to Amazon Bedrock in the region you plan to use.
|
|
88
|
+
- A **long-term IAM user access key pair**—an access key ID starting with `AKIA`
|
|
89
|
+
and its secret. Temporary STS credentials (`ASIA…`, or any pair with a session
|
|
90
|
+
token) are rejected: provider secrets are baked into the deployed gateway, so
|
|
91
|
+
a credential that expires in hours would break the gateway between redeploys.
|
|
92
|
+
- Two IAM permissions on the models you plan to call, in that region:
|
|
93
|
+
`bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream`. That's the
|
|
94
|
+
whole list—`bedrock:Converse` and `bedrock:ConverseStream` aren't IAM actions,
|
|
95
|
+
and the Converse operations are authorized by the InvokeModel pair.
|
|
96
|
+
- An AI Gateway project in the Zuplo Portal, and an [app](./apps.mdx) to call
|
|
97
|
+
from. The app page shows its API URL, and its API key lives on the app's **API
|
|
98
|
+
Key** tab.
|
|
99
|
+
|
|
100
|
+
## Add the provider
|
|
101
|
+
|
|
102
|
+
Adding or editing providers requires the **Edit** permission, granted to Zuplo
|
|
103
|
+
account and project **Admins**—see
|
|
104
|
+
[Managing Providers](./managing-providers.mdx).
|
|
105
|
+
|
|
106
|
+
<Stepper>
|
|
107
|
+
|
|
108
|
+
1. Open
|
|
109
|
+
[**Settings → AI Providers**](https://portal.zuplo.com/+/account/project/ai/settings/data-models)
|
|
110
|
+
in your AI Gateway project in the Zuplo Portal.
|
|
111
|
+
|
|
112
|
+
1. Click the **Add Provider** button.
|
|
113
|
+
|
|
114
|
+
1. In the **AI Provider** list, select **Bedrock Runtime**.
|
|
115
|
+
|
|
116
|
+
1. Review the **Provider Name**, which fills in as `bedrockruntime`. You can
|
|
117
|
+
replace it with your own name, but only now—the name is permanent after
|
|
118
|
+
creation, and it's the prefix in every model reference.
|
|
119
|
+
|
|
120
|
+
1. In **AWS Region**, enter the lowercase code of the region you use Bedrock in,
|
|
121
|
+
such as `us-east-1`. The gateway sends this provider's requests to
|
|
122
|
+
`https://bedrock-runtime.<region>.amazonaws.com`—the standard endpoint
|
|
123
|
+
family, not the FIPS or dual-stack ones. Regions in the China partition
|
|
124
|
+
(`cn-`) aren't supported.
|
|
125
|
+
|
|
126
|
+
1. In **Access Key ID**, enter the key ID, which starts with `AKIA`. In **Secret
|
|
127
|
+
Access Key**, enter the secret. AWS shows the secret only once, when the key
|
|
128
|
+
is created.
|
|
129
|
+
|
|
130
|
+
1. Select the models to enable, then click **Create**.
|
|
131
|
+
|
|
132
|
+
</Stepper>
|
|
133
|
+
|
|
134
|
+
:::note
|
|
135
|
+
|
|
136
|
+
Saving provider settings triggers an automatic production deployment of your
|
|
137
|
+
gateway, because provider credentials are part of the deployed gateway. The
|
|
138
|
+
change is live once the deployment completes.
|
|
139
|
+
|
|
140
|
+
:::
|
|
141
|
+
|
|
142
|
+
## Point your AWS SDK at the gateway
|
|
143
|
+
|
|
144
|
+
Two configuration changes, and **both are required**:
|
|
145
|
+
|
|
146
|
+
1. Set `endpoint` to your app's API URL.
|
|
147
|
+
2. Make the client send the app's API key as a bearer token.
|
|
148
|
+
|
|
149
|
+
The second one matters more than it looks. An existing client usually has AWS
|
|
150
|
+
credentials available—in the environment, in a shared profile, or from an
|
|
151
|
+
instance role—and the SDK signs with SigV4 whenever it finds any. It would then
|
|
152
|
+
never send your gateway key, and the request would fail with a `401`. Setting
|
|
153
|
+
the auth scheme explicitly is what prevents that.
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import {
|
|
157
|
+
BedrockRuntimeClient,
|
|
158
|
+
ConverseCommand,
|
|
159
|
+
} from "@aws-sdk/client-bedrock-runtime";
|
|
160
|
+
|
|
161
|
+
const apiKey = process.env.ZUPLO_APP_API_KEY;
|
|
162
|
+
if (!apiKey) {
|
|
163
|
+
throw new Error("Set ZUPLO_APP_API_KEY to your app's API key.");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const client = new BedrockRuntimeClient({
|
|
167
|
+
region: "us-east-1",
|
|
168
|
+
// Your app's API URL—no /v1, no trailing path.
|
|
169
|
+
endpoint:
|
|
170
|
+
"https://my-gateway-main-2e18f50.zuplo.app/config_fe0a04972d2848e0a94ae4b8bcd1497e",
|
|
171
|
+
// Send the app's API key as a bearer token instead of signing with SigV4.
|
|
172
|
+
authSchemePreference: ["httpBearerAuth"],
|
|
173
|
+
// `token` requires a string, so the check above is what makes this compile.
|
|
174
|
+
token: { token: apiKey },
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Unchanged from here down.
|
|
178
|
+
const response = await client.send(
|
|
179
|
+
new ConverseCommand({
|
|
180
|
+
modelId: "bedrockruntime/us.anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
181
|
+
messages: [{ role: "user", content: [{ text: "Say hi" }] }],
|
|
182
|
+
}),
|
|
183
|
+
);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Any AWS SDK that supports bearer-token authentication for Bedrock works the same
|
|
187
|
+
way—set the endpoint, supply the token, and make sure the client prefers bearer
|
|
188
|
+
auth over SigV4. Bearer-token support arrived in AWS SDK for JavaScript 3.840,
|
|
189
|
+
boto3 1.39, AWS SDK for Go 1.35, and AWS SDK for Java 2.31.74; use those
|
|
190
|
+
versions or later.
|
|
191
|
+
|
|
192
|
+
The gateway is the credential boundary: your client never holds AWS credentials,
|
|
193
|
+
and the gateway signs the upstream request with the IAM key pair you configured.
|
|
194
|
+
|
|
195
|
+
## Model references
|
|
196
|
+
|
|
197
|
+
Apps reference models as `providerName/modelId`, where `providerName` is the
|
|
198
|
+
name you gave the provider configuration:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
bedrockruntime/us.anthropic.claude-haiku-4-5-20251001-v1:0
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The gateway strips its own prefix and sends AWS the model ID exactly as you
|
|
205
|
+
wrote it, so **anything Bedrock accepts works**—published model IDs,
|
|
206
|
+
cross-region inference profiles, and provisioned-throughput ARNs alike.
|
|
207
|
+
|
|
208
|
+
:::tip{title="Many models need an inference profile ID"}
|
|
209
|
+
|
|
210
|
+
Bedrock refuses a bare model ID for models it serves only through cross-region
|
|
211
|
+
inference:
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
ValidationException: Invocation of model ID
|
|
215
|
+
anthropic.claude-haiku-4-5-20251001-v1:0 with on-demand throughput isn't
|
|
216
|
+
supported. Retry your request with the ID or ARN of an inference profile that
|
|
217
|
+
contains this model.
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Use the inference profile ID—usually the model ID with a regional prefix such as
|
|
221
|
+
`us.`, `eu.`, or `apac.`. AWS lists them under
|
|
222
|
+
[Supported Regions and models for inference profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html).
|
|
223
|
+
|
|
224
|
+
:::
|
|
225
|
+
|
|
226
|
+
## Pricing and budgets
|
|
227
|
+
|
|
228
|
+
The gateway prices Bedrock Runtime requests from the model catalog by exact
|
|
229
|
+
model ID, the same as every other provider. Cross-region inference profiles are
|
|
230
|
+
priced too: the `us.`, `eu.`, `apac.`, `au.`, and `global.` IDs are catalog rows
|
|
231
|
+
in their own right. A model with no catalog row, such as a
|
|
232
|
+
provisioned-throughput ARN or an inference profile AWS added after the catalog,
|
|
233
|
+
is still served and still counts toward token and request
|
|
234
|
+
[budgets](./usage-limits.mdx), but is priced at zero. It doesn't count toward
|
|
235
|
+
spending budgets, and its response carries no `X-Cost-USD` header.
|
|
236
|
+
|
|
237
|
+
When a budget is exhausted, these routes answer
|
|
238
|
+
`400 ServiceQuotaExceededException`—not the `429` the Universal API returns. AWS
|
|
239
|
+
SDKs treat any `429` as throttling and retry it, and a spent budget doesn't
|
|
240
|
+
refill between retries, so the gateway uses the status Bedrock itself uses for
|
|
241
|
+
an exhausted quota. Your client sees the refusal immediately instead of retrying
|
|
242
|
+
into it.
|
|
243
|
+
|
|
244
|
+
## Policies on Bedrock Runtime routes
|
|
245
|
+
|
|
246
|
+
These routes run your app's [policy chain](./policy-chains.mdx) like any other.
|
|
247
|
+
What changes is that the request body is Bedrock's shape rather than one the
|
|
248
|
+
gateway parses, so a policy that reads the body applies its `onUnknownShape`
|
|
249
|
+
setting instead. Each policy's default follows from its job: a guardrail fails
|
|
250
|
+
closed, while an observer or an optimization fails open.
|
|
251
|
+
|
|
252
|
+
| Policy | On Bedrock Runtime routes |
|
|
253
|
+
| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
254
|
+
| [Authentication](../policies/ai-gateway-auth-v2-inbound.mdx) | Works. Reads the app key from `Authorization: Bearer` |
|
|
255
|
+
| [Model Filtering](../policies/ai-gateway-model-filtering-v2-inbound.mdx) | Works. Refuses a model outside the allow list before any AWS call |
|
|
256
|
+
| [Metering and budgets](../policies/ai-gateway-metering-v2-inbound.mdx) | Works. Meters tokens and cost on all four operations, streamed or not |
|
|
257
|
+
| [DLP](../policies/ai-gateway-dlp-inbound.mdx) and content guardrails | **Blocks by default.** The gateway can't read these bodies, so the policy denies the request. Set `onUnknownShape` to `skip` to forward it uninspected |
|
|
258
|
+
| [Galileo](../policies/galileo-tracing-v2-inbound.mdx) and [Comet Opik](../policies/comet-opik-tracing-v2-inbound.mdx) tracing | **Skipped by default.** The request goes through untraced. Set `onUnknownShape` to `deny` to make a trace a hard requirement |
|
|
259
|
+
| Semantic Cache | Skipped |
|
|
260
|
+
|
|
261
|
+
The table lists every policy the gateway supports on these routes. It audits the
|
|
262
|
+
chain and refuses any other policy, such as a custom policy, Smart Router, or
|
|
263
|
+
[Fallback Model](./fallback.mdx), with a `400 ValidationException` that names
|
|
264
|
+
the policy. Remove it from the chain to use Bedrock Runtime through that app.
|
|
265
|
+
|
|
266
|
+
If you need content inspection on Bedrock traffic, use
|
|
267
|
+
[Bedrock's own guardrails](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html)
|
|
268
|
+
through `guardrailConfig` in the request body—the gateway forwards it untouched.
|
|
269
|
+
|
|
270
|
+
Errors the gateway raises on these routes carry an `x-amzn-ErrorType` header, so
|
|
271
|
+
your SDK surfaces them as typed exceptions rather than as deserialization
|
|
272
|
+
failures.
|
|
273
|
+
|
|
274
|
+
## Troubleshooting
|
|
275
|
+
|
|
276
|
+
**Every request returns `401`.** The client is signing with SigV4 instead of
|
|
277
|
+
sending the app's API key. Set `authSchemePreference` (or your SDK's equivalent)
|
|
278
|
+
to prefer bearer authentication, and confirm the token is the app's API key from
|
|
279
|
+
the app's **API Key** tab.
|
|
280
|
+
|
|
281
|
+
**`ValidationException` about on-demand throughput.** The model needs a
|
|
282
|
+
cross-region inference profile ID rather than the bare model ID—see
|
|
283
|
+
[Model references](#model-references).
|
|
284
|
+
|
|
285
|
+
**`403` from AWS that reads like a bad key.** Check that the IAM user has
|
|
286
|
+
`bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream` on that model
|
|
287
|
+
_in the provider's region_, and that the model is enabled for your AWS account.
|
|
288
|
+
|
|
289
|
+
**The provider dialog rejects your key pair.** An access key ID starting with
|
|
290
|
+
`ASIA` is a temporary credential and is refused on purpose—it would expire while
|
|
291
|
+
baked into the deployed gateway. The dialog also rejects an access key ID that
|
|
292
|
+
isn't 16 to 128 uppercase letters and digits, a secret containing spaces or line
|
|
293
|
+
breaks, and either field filled in without the other.
|
|
294
|
+
|
|
295
|
+
**A `400` naming the supported operations.** You called `CountTokens` or
|
|
296
|
+
`InvokeModelWithBidirectionalStream`, which the gateway doesn't serve.
|
|
297
|
+
|
|
298
|
+
**`ServiceQuotaExceededException` you can't find in AWS.** It's a Zuplo budget,
|
|
299
|
+
not an AWS service quota. An exhausted [budget](./usage-limits.mdx) refuses
|
|
300
|
+
these routes with that exception—check the app's usage limits before you open a
|
|
301
|
+
quota ticket with AWS.
|
|
302
|
+
|
|
303
|
+
**Your app's model list is empty of Bedrock models.** Expected—Bedrock Runtime
|
|
304
|
+
models don't appear in `GET /v1/models`, because model IDs here are open-ended
|
|
305
|
+
(inference profiles and ARNs are valid and aren't catalog entries).
|
|
306
|
+
|
|
307
|
+
## Next steps
|
|
308
|
+
|
|
309
|
+
- [AI Providers](./providers.mdx)—the capability matrix across every supported
|
|
310
|
+
provider.
|
|
311
|
+
- [Using Bedrock Mantle](./bedrock-mantle.mdx)—the other Amazon Bedrock
|
|
312
|
+
provider, for OpenAI- and Anthropic-shaped clients.
|
|
313
|
+
- [Managing Providers](./managing-providers.mdx)—edit models, keys, and the
|
|
314
|
+
region, and understand when changes deploy.
|
|
315
|
+
- [AI Gateway Apps](./apps.mdx)—create the apps that call your models.
|
|
316
|
+
- [Model Filtering policy](../policies/ai-gateway-model-filtering-v2-inbound.mdx)—control
|
|
317
|
+
which models each app can call.
|
|
@@ -23,6 +23,8 @@ Zuplo currently supports the following AI providers:
|
|
|
23
23
|
serving Claude and every Foundry model family from your own Azure subscription
|
|
24
24
|
- [Bedrock Mantle](./bedrock-mantle.mdx)—Amazon Bedrock's compatible-APIs
|
|
25
25
|
endpoint, serving Claude models and models from many other vendors
|
|
26
|
+
- [Bedrock Runtime](./bedrock-runtime.mdx)—Amazon Bedrock's native API, for code
|
|
27
|
+
that already calls Bedrock with an AWS SDK
|
|
26
28
|
- [Vertex AI](./vertex-ai.mdx)—Google Cloud's managed model platform, serving
|
|
27
29
|
Gemini and Model Garden models on your own Google Cloud project
|
|
28
30
|
- [Zuplo Demo](#zuplo-demo)—a free, keyless provider for trying the gateway
|
|
@@ -40,6 +42,7 @@ The following capabilities are supported across providers:
|
|
|
40
42
|
| xAI | ✅ | ✅ | ❌ | ❌ |
|
|
41
43
|
| Azure AI | ✅ | ✅ | ✅ | ✅ |
|
|
42
44
|
| Bedrock Mantle | ✅ | ❌ | ✅ | ✅ |
|
|
45
|
+
| Bedrock Runtime | ❌ | ❌ | ❌ | ❌ |
|
|
43
46
|
| Vertex AI | ✅ | ✅ | ❌ | ✅ |
|
|
44
47
|
| Zuplo Demo | ✅ | ❌ | ❌ | ❌ |
|
|
45
48
|
| OpenAI-compatible (Custom) | ✅ | ✅ | ❌ | ❌ |
|
|
@@ -54,6 +57,13 @@ Messages (plus chat completions through translation), while its other models
|
|
|
54
57
|
serve chat completions and—per model—Responses. See
|
|
55
58
|
[Using Bedrock Mantle](./bedrock-mantle.mdx#supported-endpoints-by-model-family).
|
|
56
59
|
|
|
60
|
+
Bedrock Runtime is the one provider with no Universal API capabilities, and the
|
|
61
|
+
table above says so. It serves Amazon Bedrock's **native** API instead, at its
|
|
62
|
+
own paths, so your existing AWS SDK code keeps working with the gateway in front
|
|
63
|
+
of it. See [Using Bedrock Runtime](./bedrock-runtime.mdx#supported-operations)
|
|
64
|
+
for the operations it serves, and add Bedrock Mantle alongside it if you also
|
|
65
|
+
want Bedrock models on the Universal API.
|
|
66
|
+
|
|
57
67
|
Azure AI's capabilities split by model family, and within the OpenAI-compatible
|
|
58
68
|
family they split per deployed model: a chat model serves chat completions and—
|
|
59
69
|
per model and version—Responses, while an embedding model serves embeddings and
|
|
@@ -119,6 +129,26 @@ instead of an endpoint URL, and it accepts only long-term Bedrock API keys,
|
|
|
119
129
|
which start with `ABSK`. For prerequisites, setup steps, code examples, and
|
|
120
130
|
troubleshooting, see [Using Bedrock Mantle](./bedrock-mantle.mdx).
|
|
121
131
|
|
|
132
|
+
## Bedrock Runtime
|
|
133
|
+
|
|
134
|
+
**Bedrock Runtime** is Amazon Bedrock's native API—the one the AWS SDKs call.
|
|
135
|
+
Unlike every other provider, it doesn't serve the
|
|
136
|
+
[Universal API](./universal-api.mdx): it serves Bedrock's own operations
|
|
137
|
+
(`Converse`, `ConverseStream`, `InvokeModel`, and
|
|
138
|
+
`InvokeModelWithResponseStream`) at Bedrock's own paths.
|
|
139
|
+
|
|
140
|
+
That makes it the provider to choose when you already have code calling Bedrock
|
|
141
|
+
with an AWS SDK. You point the client's endpoint at your app and give it the
|
|
142
|
+
app's API key; every call site stays as it is, and the gateway adds
|
|
143
|
+
authentication, model filtering, and usage metering in front of your own AWS
|
|
144
|
+
account.
|
|
145
|
+
|
|
146
|
+
It authenticates to AWS with a long-term IAM access key pair rather than a
|
|
147
|
+
Bedrock API key, so it needs its own provider configuration even if you also use
|
|
148
|
+
Bedrock Mantle on the same account. For prerequisites, setup steps, code
|
|
149
|
+
examples, and troubleshooting, see
|
|
150
|
+
[Using Bedrock Runtime](./bedrock-runtime.mdx).
|
|
151
|
+
|
|
122
152
|
## Vertex AI
|
|
123
153
|
|
|
124
154
|
**Vertex AI** is Google Cloud's managed model platform. One provider
|
|
@@ -69,3 +69,33 @@ list—so clients that can't set a model still work.
|
|
|
69
69
|
| `/v1/embeddings` | Embeddings for the providers marked in the [capability matrix](./providers.mdx#supported-providers) |
|
|
70
70
|
| `/v1/responses` | OpenAI Responses API: OpenAI, and the OpenAI-compatible models that serve it on [Azure AI](./azure-ai.mdx) and [Bedrock Mantle](./bedrock-mantle.mdx) |
|
|
71
71
|
| `/v1/messages` | Anthropic Messages API: Anthropic, and the Claude models of [Azure AI](./azure-ai.mdx), [Bedrock Mantle](./bedrock-mantle.mdx) and [Vertex AI](./vertex-ai.mdx) |
|
|
72
|
+
|
|
73
|
+
## Request headers
|
|
74
|
+
|
|
75
|
+
The gateway forwards your request headers to the provider. A header your client
|
|
76
|
+
or your own policies set—a session ID, a trace header, a feature flag—arrives
|
|
77
|
+
with the request.
|
|
78
|
+
|
|
79
|
+
The gateway removes what it owns first:
|
|
80
|
+
|
|
81
|
+
| Removed | Why |
|
|
82
|
+
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
83
|
+
| `authorization`, `x-api-key`, `api-key`, `proxy-authorization`, and cookies | Credentials addressed to the gateway. The provider gets its own credential from your provider configuration instead. |
|
|
84
|
+
| `content-type`, `accept`, `anthropic-version`, `anthropic-beta` | The gateway controls the wire format it sends upstream, which isn't always the one your client asked for. |
|
|
85
|
+
| `openai-organization`, `openai-project`, `x-goog-api-key`, `x-goog-user-project` | Another vendor's credentials and tenant selectors. They cause a `401` as soon as a request routes to a provider they don't belong to. |
|
|
86
|
+
| `host`, `connection`, `transfer-encoding`, `content-length`, and the rest of the hop-by-hop set | They describe the connection to the gateway, not the one to the provider. |
|
|
87
|
+
| `cdn-loop`, and anything starting with `zp-`, `cf-`, or `x-amz-` | They describe the path your request took to reach the gateway. |
|
|
88
|
+
|
|
89
|
+
Everything else reaches the provider, including `user-agent`, `x-forwarded-for`,
|
|
90
|
+
`true-client-ip`, `idempotency-key`, `x-request-id`, and the `x-stainless-*`
|
|
91
|
+
headers the OpenAI and Anthropic SDKs set.
|
|
92
|
+
|
|
93
|
+
To change what the provider receives, add a header policy—`set-headers-inbound`,
|
|
94
|
+
`remove-headers-inbound`, or `clear-headers-inbound`—to the app's
|
|
95
|
+
[policy chain](./policy-chains.mdx). Headers are copied after the chain runs, so
|
|
96
|
+
a policy that adds one puts it on the provider request too. Setting
|
|
97
|
+
`Authorization`, `x-api-key`, or `api-key` in a policy doesn't override the
|
|
98
|
+
credential from your provider configuration.
|
|
99
|
+
|
|
100
|
+
[Bedrock Runtime](./bedrock-runtime.mdx) isn't part of the Universal API and
|
|
101
|
+
forwards a different set, described on its own page.
|
|
@@ -228,6 +228,40 @@ the top of the hour. Every distinct `x-organization` value gets its own $50 per
|
|
|
228
228
|
day, resetting at 00:00 UTC. The `app` rule caps the whole app at $2,500 per
|
|
229
229
|
month, warning at $2,000, resetting on the first of the month.
|
|
230
230
|
|
|
231
|
+
### Checking what a rule resolved to
|
|
232
|
+
|
|
233
|
+
A **By metadata** rule whose expression resolves to nothing budgets nothing, and
|
|
234
|
+
the request still succeeds. A rule that silently does nothing therefore looks
|
|
235
|
+
exactly like a rule that's working. Your gateway's request logs say which it is.
|
|
236
|
+
|
|
237
|
+
Every request that runs at least one **By metadata** rule logs
|
|
238
|
+
`AI Gateway budget dimensions resolved`, listing the rules that resolved and the
|
|
239
|
+
rules that didn't. Each rule that didn't carries a reason:
|
|
240
|
+
|
|
241
|
+
| Reason | What to change |
|
|
242
|
+
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
243
|
+
| `no-such-property` | The expression is valid but found no value. The message names the property that broke the chain—check that the header or claim is really on the request. |
|
|
244
|
+
| `invalid-syntax` | The expression is outside the grammar. The message gives the position of the problem. |
|
|
245
|
+
| `per-request-identifier` | The value is unique to every request, so no two requests share a budget and no threshold is ever reached. Key on something that repeats, like a user or an organization. |
|
|
246
|
+
| `internal-field` | The value comes from a field the proxy populates, such as client certificate material. |
|
|
247
|
+
| `value-not-scalar` / `value-not-safe-integer` | The value isn't a string or a whole number. |
|
|
248
|
+
| `value-too-long` | The value is longer than 256 bytes. |
|
|
249
|
+
| `value-has-control-characters` | The value contains control characters. |
|
|
250
|
+
|
|
251
|
+
Resolved entries record the length of each value, not the value itself. A rule
|
|
252
|
+
whose expression names a credential—anything whose text contains `token`,
|
|
253
|
+
`secret`, `authorization`, `password`, `cookie`, `credential`, `bearer`, or
|
|
254
|
+
`api_key` in any spelling—never logs its value at all. Budgeting per API key is
|
|
255
|
+
a reasonable thing to do, and the key stays out of the log.
|
|
256
|
+
|
|
257
|
+
A rule that can never work, because its expression is unusable or the rule fails
|
|
258
|
+
validation, also logs a warning. Those warnings arrive once per configuration
|
|
259
|
+
rather than once per request, so a broken rule doesn't flood the log.
|
|
260
|
+
|
|
261
|
+
Verdicts name their rule too. A block or warning from a **By metadata** rule
|
|
262
|
+
carries the expression and its `ruleId`; one from an app-wide budget carries
|
|
263
|
+
neither. That's how you tell which rule refused a request.
|
|
264
|
+
|
|
231
265
|
## Setting team and gateway limits
|
|
232
266
|
|
|
233
267
|
A team budget covers the combined usage of its apps and sub-teams. A gateway
|
|
@@ -41,7 +41,7 @@ Wrap Markdown lists with the `Stepper` component to create a vertical stepper li
|
|
|
41
41
|
</div>
|
|
42
42
|
<div className="flex-1">
|
|
43
43
|
|
|
44
|
-
<Stepper>
|
|
44
|
+
<Stepper toc={false}>
|
|
45
45
|
|
|
46
46
|
1. **Identify the Problem**
|
|
47
47
|
1. **Plan Your Project**
|
|
@@ -52,6 +52,39 @@ Wrap Markdown lists with the `Stepper` component to create a vertical stepper li
|
|
|
52
52
|
</div>
|
|
53
53
|
</div>
|
|
54
54
|
|
|
55
|
+
## Table of Contents
|
|
56
|
+
|
|
57
|
+
Steps are anchorable and show up in the "On this page" sidebar, nested one level below the heading
|
|
58
|
+
they follow:
|
|
59
|
+
|
|
60
|
+
| Stepper placed after… | Steps appear as… |
|
|
61
|
+
| ------------------------------------- | ----------------------------------------------- |
|
|
62
|
+
| The page title (or no heading at all) | Top-level entries |
|
|
63
|
+
| An `h2` | Sub-entries of that `h2` |
|
|
64
|
+
| An `h3` or deeper | Nothing — the same cut-off an `h4` heading hits |
|
|
65
|
+
|
|
66
|
+
That means a page that is nothing but a stepper (a tutorial, for example) gets a usable table of
|
|
67
|
+
contents automatically, while a stepper buried inside an existing section doesn't flood it.
|
|
68
|
+
|
|
69
|
+
Each step's title — the first line of the list item — becomes the entry. A step whose first block
|
|
70
|
+
isn't text (a code block, for instance) is skipped.
|
|
71
|
+
|
|
72
|
+
### Opting out
|
|
73
|
+
|
|
74
|
+
Set `toc={false}` for a stepper that is a quick checklist rather than a section of the page worth
|
|
75
|
+
linking to:
|
|
76
|
+
|
|
77
|
+
<!-- prettier-ignore -->
|
|
78
|
+
```tsx
|
|
79
|
+
<Stepper toc={false}>
|
|
80
|
+
|
|
81
|
+
1. **Find the reason code**
|
|
82
|
+
1. **Look up the code**
|
|
83
|
+
1. **Re-file**
|
|
84
|
+
|
|
85
|
+
</Stepper>
|
|
86
|
+
```
|
|
87
|
+
|
|
55
88
|
## Advanced Example
|
|
56
89
|
|
|
57
90
|
<Stepper>
|
|
@@ -112,6 +112,10 @@ dist/
|
|
|
112
112
|
└── ...
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
::if{mode=opensource}
|
|
116
|
+
|
|
117
|
+
::
|
|
118
|
+
|
|
115
119
|
**Important:** Individual `.md` files are only kept in the final build if `publishMarkdown: true`.
|
|
116
120
|
If only `llmsTxt` or `llmsTxtFull` is enabled, the `.md` files are generated temporarily during the
|
|
117
121
|
build but deleted after the `llms.txt` files are created.
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# AI Gateway Authentication
|
|
2
|
-
|
|
3
1
|
The AI Gateway Authentication policy authenticates requests with an AI Gateway
|
|
4
2
|
application API key. After a key is accepted, the calling application is
|
|
5
3
|
identified on `request.user` — `sub` is the application name and `data` its
|
|
@@ -114,6 +112,11 @@ curl https://gateway.example.com/v1/chat/completions \
|
|
|
114
112
|
Authentication scheme matching is case-insensitive. A missing key, an invalid
|
|
115
113
|
scheme, or a key that is not authorized receives a `401 Unauthorized` response.
|
|
116
114
|
|
|
115
|
+
Once the key is accepted, the policy removes the `authHeader` header from the
|
|
116
|
+
request. The application key never reaches an upstream provider or a policy
|
|
117
|
+
later in the chain, whatever header name it arrived in. Read the caller's
|
|
118
|
+
identity from `request.user` instead.
|
|
119
|
+
|
|
117
120
|
## Use a custom header
|
|
118
121
|
|
|
119
122
|
Set `authHeader` to accept the key from another header. Set `authScheme` to an
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# AI Gateway Configuration Executor
|
|
2
|
-
|
|
3
1
|
The AI Gateway Configuration Executor loads each application's configuration
|
|
4
2
|
(when auth or `ai-gateway-configuration-loader-v2-inbound` has not already) and
|
|
5
3
|
runs its ordered inbound policy chain. Configuration comes from route-level
|
|
@@ -408,6 +406,32 @@ The corresponding expression is `request.headers.get("x-budget-customer")`.
|
|
|
408
406
|
Configuration code should create that ordinary string and pass the enclosing
|
|
409
407
|
object to `JSON.stringify`; the serializer adds the JSON transport escaping.
|
|
410
408
|
|
|
409
|
+
Headers you set or remove in the inbound chain control what reaches your AI
|
|
410
|
+
provider. For example, you can add a session header or remove an internal
|
|
411
|
+
customer header before sending the request. Gateway-owned headers are excluded,
|
|
412
|
+
including credentials, `Content-Type`, `Accept`, and headers starting with `zp-`
|
|
413
|
+
or `cf-`; setting a credential header does not override your configured provider
|
|
414
|
+
credential.
|
|
415
|
+
|
|
416
|
+
That includes headers you add only for the gateway's own use. The
|
|
417
|
+
`x-budget-customer` header above carries the caller's user id, and unless you
|
|
418
|
+
remove it after the metering policy has read it, the provider receives it on
|
|
419
|
+
every request. Add a `remove-headers-inbound` entry after the metering entry:
|
|
420
|
+
|
|
421
|
+
```json
|
|
422
|
+
[
|
|
423
|
+
{ "name": "set-budget-dimension" },
|
|
424
|
+
{ "name": "ai-gateway-metering-v2-inbound" },
|
|
425
|
+
{
|
|
426
|
+
"name": "remove-headers-inbound",
|
|
427
|
+
"options": { "headers": ["x-budget-customer"] }
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Declare `remove-headers-inbound` in `config/policies.json` so the chain can
|
|
433
|
+
select it.
|
|
434
|
+
|
|
411
435
|
Invalid budget rules and unsupported expressions are logged and skipped. Other
|
|
412
436
|
valid rules continue to apply.
|
|
413
437
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# AI Gateway Configuration Loader
|
|
2
|
-
|
|
3
1
|
The AI Gateway Configuration Loader loads each application's configuration into
|
|
4
2
|
the request-scoped app-configuration channel. Configuration comes from
|
|
5
3
|
route-level `ai-gateway-auth-v2-inbound` when that policy already ran, otherwise
|
|
@@ -43,16 +43,28 @@
|
|
|
43
43
|
"pii": {
|
|
44
44
|
"action": "mask"
|
|
45
45
|
},
|
|
46
|
-
"
|
|
47
|
-
"action": "
|
|
46
|
+
"contact-email": {
|
|
47
|
+
"action": "off"
|
|
48
|
+
},
|
|
49
|
+
"finance-credit-card": {
|
|
50
|
+
"action": "mask"
|
|
51
|
+
},
|
|
52
|
+
"finance-iban": {
|
|
53
|
+
"action": "mask"
|
|
48
54
|
}
|
|
49
55
|
},
|
|
50
56
|
"outbound": {
|
|
51
57
|
"pii": {
|
|
52
|
-
"action": "
|
|
58
|
+
"action": "mask"
|
|
53
59
|
},
|
|
54
|
-
"
|
|
55
|
-
"action": "
|
|
60
|
+
"contact-email": {
|
|
61
|
+
"action": "off"
|
|
62
|
+
},
|
|
63
|
+
"finance-credit-card": {
|
|
64
|
+
"action": "mask"
|
|
65
|
+
},
|
|
66
|
+
"finance-iban": {
|
|
67
|
+
"action": "mask"
|
|
56
68
|
}
|
|
57
69
|
}
|
|
58
70
|
}
|
|
@@ -6349,16 +6361,28 @@
|
|
|
6349
6361
|
"pii": {
|
|
6350
6362
|
"action": "mask"
|
|
6351
6363
|
},
|
|
6352
|
-
"
|
|
6353
|
-
"action": "
|
|
6364
|
+
"contact-email": {
|
|
6365
|
+
"action": "off"
|
|
6366
|
+
},
|
|
6367
|
+
"finance-credit-card": {
|
|
6368
|
+
"action": "mask"
|
|
6369
|
+
},
|
|
6370
|
+
"finance-iban": {
|
|
6371
|
+
"action": "mask"
|
|
6354
6372
|
}
|
|
6355
6373
|
},
|
|
6356
6374
|
"outbound": {
|
|
6357
6375
|
"pii": {
|
|
6358
|
-
"action": "
|
|
6376
|
+
"action": "mask"
|
|
6359
6377
|
},
|
|
6360
|
-
"
|
|
6361
|
-
"action": "
|
|
6378
|
+
"contact-email": {
|
|
6379
|
+
"action": "off"
|
|
6380
|
+
},
|
|
6381
|
+
"finance-credit-card": {
|
|
6382
|
+
"action": "mask"
|
|
6383
|
+
},
|
|
6384
|
+
"finance-iban": {
|
|
6385
|
+
"action": "mask"
|
|
6362
6386
|
}
|
|
6363
6387
|
}
|
|
6364
6388
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# AI Gateway Semantic Cache
|
|
2
|
-
|
|
3
1
|
AI Gateway Semantic Cache answers a request from a previous response when the
|
|
4
2
|
new prompt is semantically similar to one already seen. A hit skips the provider
|
|
5
3
|
call. One inbound policy handles both directions: it checks the cache on the way
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# AI Gateway Smart Router
|
|
2
|
-
|
|
3
1
|
Use this policy to classify the last user prompt on Chat Completions, Responses,
|
|
4
2
|
and Anthropic Messages requests. Using the classification results, configure
|
|
5
3
|
where to route the request based on its complexity.
|
|
@@ -268,3 +266,10 @@ Invalid options, classifier timeouts, empty classifier responses, and smart
|
|
|
268
266
|
routing catalog errors are logged and the original request continues. Chat
|
|
269
267
|
Completions, Responses, and Anthropic Messages are classified automatically.
|
|
270
268
|
Embeddings and other non-AI paths are skipped.
|
|
269
|
+
|
|
270
|
+
One shape is refused rather than skipped. A Bedrock Runtime request
|
|
271
|
+
(`/model/{modelId}/{operation}`) carries a model-native body that the gateway
|
|
272
|
+
does not parse, so there is no prompt to classify, and letting it continue would
|
|
273
|
+
present your routing as in effect when it was not. The policy answers a
|
|
274
|
+
`400 ValidationException` in the AWS error envelope, naming the policy. Remove
|
|
275
|
+
the policy from that route to use Bedrock Runtime through it.
|
|
@@ -111,7 +111,7 @@ is working as expected.
|
|
|
111
111
|
|
|
112
112
|
You have now set up Auth0 JWT Authentication on your API Gateway.
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
### OAuth 2.0 Protected Resource Metadata
|
|
115
115
|
|
|
116
116
|
The Auth0 JWT Auth policy supports OAuth protected resource metadata discovery.
|
|
117
117
|
To enable this feature, set the `oAuthResourceMetadataEnabled` option to `true`
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"clientSecret": {
|
|
54
54
|
"type": "string",
|
|
55
|
-
"examples": ["$env(
|
|
55
|
+
"examples": ["$env(AUTH0_CLIENT_SECRET)"],
|
|
56
56
|
"description": "The Auth0 client\\_secret. Use $env(...) to source from a secret environment variable."
|
|
57
57
|
},
|
|
58
58
|
"scope": {
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
"sessionTtlSeconds": 28800,
|
|
210
210
|
"stateTtlSeconds": 900
|
|
211
211
|
},
|
|
212
|
-
"clientSecret": "$env(
|
|
212
|
+
"clientSecret": "$env(AUTH0_CLIENT_SECRET)",
|
|
213
213
|
"gateway": {
|
|
214
214
|
"accessTokenTtlSeconds": 900,
|
|
215
215
|
"cimdEnabled": true,
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"type": "string",
|
|
74
74
|
"format": "uri",
|
|
75
75
|
"examples": ["https://my-tenant.us.auth0.com/authorize"],
|
|
76
|
-
"description": "The IdP /authorize endpoint to redirect the user to. For local development on loopback, use http://127.0.0.1:9000/oauth/dev-login."
|
|
76
|
+
"description": "The IdP /authorize endpoint to redirect the user to. For local development on loopback, use http://127.0.0.1:9000/\\_\\_zuplo/oauth/dev-login (the gateway's default basePath is /\\_\\_zuplo; adjust if the plugin is configured with a different one)."
|
|
77
77
|
},
|
|
78
78
|
"tokenUrl": {
|
|
79
79
|
"type": "string",
|
|
@@ -108,6 +108,52 @@ as Entra tenant admin consent. If connections start requiring frequent
|
|
|
108
108
|
reauthorization after setting `prompt: false`, remove the option so the consent
|
|
109
109
|
grant is requested again.
|
|
110
110
|
|
|
111
|
+
## Authorization Server Issuer Mismatch
|
|
112
|
+
|
|
113
|
+
Some providers publish inconsistent OAuth metadata: the protected-resource
|
|
114
|
+
document advertises a path-scoped authorization server, but that server's own
|
|
115
|
+
metadata reports a different (usually root-origin) `issuer`. ServiceNow is one —
|
|
116
|
+
its protected-resource metadata names
|
|
117
|
+
`https://<instance>.service-now.com/sncapps/...` while the metadata served there
|
|
118
|
+
reports `https://<instance>.service-now.com`. Atlassian and Okta
|
|
119
|
+
org-versus-custom authorization servers publish the same shape.
|
|
120
|
+
|
|
121
|
+
RFC 8414 section 3.3 requires a client to reject a metadata document whose
|
|
122
|
+
`issuer` differs from the identifier the well-known URL was built from, so the
|
|
123
|
+
gateway hard-fails discovery against these providers. Set
|
|
124
|
+
`skipIssuerMetadataValidation` to `true` on that connection to proceed anyway:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"name": "mcp-token-exchange-servicenow",
|
|
129
|
+
"policyType": "mcp-token-exchange-inbound",
|
|
130
|
+
"handler": {
|
|
131
|
+
"module": "$import(@zuplo/runtime/mcp-gateway)",
|
|
132
|
+
"export": "McpTokenExchangeInboundPolicy",
|
|
133
|
+
"options": {
|
|
134
|
+
"displayName": "ServiceNow",
|
|
135
|
+
"authMode": "user-oauth",
|
|
136
|
+
"skipIssuerMetadataValidation": true
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Warning**: this puts the connection out of spec. The issuer check is an
|
|
143
|
+
unqualified MUST in both RFC 8414 section 3.3 and the MCP authorization spec,
|
|
144
|
+
whose own example is the attack it prevents — a document served from
|
|
145
|
+
`https://attacker.example/.well-known/oauth-authorization-server` claiming
|
|
146
|
+
`"issuer": "https://honest.example"` must be rejected. The check binds the
|
|
147
|
+
metadata to the TLS-authenticated host it came from, and with it off the gateway
|
|
148
|
+
trusts the authorization, token and JWKS endpoints from a document that never
|
|
149
|
+
proved it speaks for that issuer.
|
|
150
|
+
|
|
151
|
+
Only enable it for a specific upstream known to publish a mismatched issuer, and
|
|
152
|
+
only when the metadata URL's host is itself trusted. Never enable it as a
|
|
153
|
+
default. It relaxes the discovery-time check only: the RFC 9207
|
|
154
|
+
authorization-response check and the callback-leg authorization-server binding
|
|
155
|
+
still apply.
|
|
156
|
+
|
|
111
157
|
## ID-JAG Token Exchange (Cross-App Access)
|
|
112
158
|
|
|
113
159
|
Set `authMode` to `id-jag` when the upstream is protected by an Identity
|
|
@@ -128,8 +174,8 @@ Use this mode in enterprise deployments where the gateway and the upstream trust
|
|
|
128
174
|
the same identity provider (for example, Okta Cross-App Access), so upstream
|
|
129
175
|
access follows the caller's identity without a per-user upstream consent flow.
|
|
130
176
|
The `user-oauth`/`shared-oauth` options — `scopes`, `scopeDelimiter`, `prompt`,
|
|
131
|
-
and `clientRegistration` — do not apply; the
|
|
132
|
-
entirely through the `idJag` option.
|
|
177
|
+
`skipIssuerMetadataValidation`, and `clientRegistration` — do not apply; the
|
|
178
|
+
`id-jag` mode is configured entirely through the `idJag` option.
|
|
133
179
|
|
|
134
180
|
```json
|
|
135
181
|
{
|
|
@@ -56,6 +56,9 @@
|
|
|
56
56
|
{
|
|
57
57
|
"required": ["prompt"]
|
|
58
58
|
},
|
|
59
|
+
{
|
|
60
|
+
"required": ["skipIssuerMetadataValidation"]
|
|
61
|
+
},
|
|
59
62
|
{
|
|
60
63
|
"required": ["clientRegistration"]
|
|
61
64
|
}
|
|
@@ -120,6 +123,11 @@
|
|
|
120
123
|
}
|
|
121
124
|
]
|
|
122
125
|
},
|
|
126
|
+
"skipIssuerMetadataValidation": {
|
|
127
|
+
"type": "boolean",
|
|
128
|
+
"x-advanced": true,
|
|
129
|
+
"description": "Skips the RFC 8414 section 3.3 issuer-echo check when discovering the upstream authorization server. That check requires the `issuer` in the authorization server's metadata to equal the `authorization_servers` entry the protected-resource metadata advertised. Some providers publish a path-scoped authorization server whose metadata reports the root origin instead — for example ServiceNow advertises `https://<instance>.service-now.com/sncapps/...` but its metadata reports `https://<instance>.service-now.com` — so discovery hard-fails without this option. Warning: enabling this puts the connection out of spec. The check is an unqualified MUST in both RFC 8414 section 3.3 and the MCP authorization spec, which require rejecting a metadata document whose `issuer` differs from the identifier the well-known URL was built from; the spec's own example is a document served from `https://attacker.example/.well-known/oauth-authorization-server` claiming `\"issuer\": \"https://honest.example\"`. The echo check binds the metadata to the TLS-authenticated host it came from, and with it off the gateway trusts the authorization, token and JWKS endpoints from a document that never proved it speaks for that issuer. Only enable it for a specific upstream known to publish a mismatched issuer, and only when the metadata URL's host is itself trusted. It relaxes the discovery-time check only — the RFC 9207 authorization-response check and the callback-leg authorization-server binding still apply."
|
|
130
|
+
},
|
|
123
131
|
"clientRegistration": {
|
|
124
132
|
"x-advanced": true,
|
|
125
133
|
"description": "OAuth client registration mode. Defaults to `auto`, which uses Client ID Metadata Documents when the upstream advertises support and falls back to Dynamic Client Registration otherwise.",
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# Monetization Metering Policy
|
|
2
|
-
|
|
3
1
|
The Monetization policy validates subscriptions and records usage. Meter usage
|
|
4
2
|
is sent in a final response hook after status-code filtering.
|
|
5
3
|
|
|
@@ -9,6 +7,16 @@ is sent in a final response hook after status-code filtering.
|
|
|
9
7
|
- `meterOnStatusCodes`: status codes/ranges that trigger metering.
|
|
10
8
|
- auth/cache settings: `authHeader`, `authScheme`, `cacheTtlSeconds`.
|
|
11
9
|
|
|
10
|
+
## Error responses
|
|
11
|
+
|
|
12
|
+
- Missing, malformed, invalid, or expired API-key credentials return
|
|
13
|
+
`401 Unauthorized`. When `authScheme` is set, the response includes a
|
|
14
|
+
`WWW-Authenticate` challenge for that scheme. An empty `authScheme` is
|
|
15
|
+
raw-header mode: the entire header value is the key, and `WWW-Authenticate` is
|
|
16
|
+
omitted.
|
|
17
|
+
- A valid API key whose subscription, payment status, or entitlements deny
|
|
18
|
+
access returns `403 Forbidden`.
|
|
19
|
+
|
|
12
20
|
### Static meter configuration
|
|
13
21
|
|
|
14
22
|
```json
|
|
@@ -74,7 +74,7 @@ is working as expected.
|
|
|
74
74
|
|
|
75
75
|
You have now setup PropelAuth JWT Authentication on your API Gateway.
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
### OAuth 2.0 Protected Resource Metadata
|
|
78
78
|
|
|
79
79
|
The Propel JWT Auth policy supports OAuth protected resource metadata discovery.
|
|
80
80
|
To enable this feature, set the `oAuthResourceMetadataEnabled` option to `true`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuplo",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The official Zuplo CLI for local development and platform management",
|
|
6
6
|
"homepage": "https://zuplo.com/docs/cli/overview",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"zuplo": "zuplo.js"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@zuplo/cli": "7.
|
|
36
|
-
"@zuplo/core": "7.
|
|
37
|
-
"@zuplo/runtime": "7.
|
|
38
|
-
"@zuplo/test": "7.
|
|
35
|
+
"@zuplo/cli": "7.8.0",
|
|
36
|
+
"@zuplo/core": "7.8.0",
|
|
37
|
+
"@zuplo/runtime": "7.8.0",
|
|
38
|
+
"@zuplo/test": "7.8.0"
|
|
39
39
|
}
|
|
40
40
|
}
|