zuplo 6.72.2 → 6.72.3

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.
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: Audit Logs
2
+ title: Account Audit Logs
3
3
  sidebar_label: Audit Logs
4
4
  ---
5
5
 
@@ -211,8 +211,8 @@ exceed 30 days. The start date must be before the end date.
211
211
 
212
212
  :::tip
213
213
 
214
- Account audit logs track administrative activities. To log API request and
215
- response data flowing through the gateway, see the
216
- [Audit Log Plugin](../../programmable-api/audit-log.mdx).
214
+ Account audit logs track administrative activities in your Zuplo account. To
215
+ record an audit trail of the requests flowing through your gateway, see
216
+ [Audit Logging](../audit-logging.mdx).
217
217
 
218
218
  :::
@@ -0,0 +1,65 @@
1
+ ---
2
+ title: Audit Logging
3
+ sidebar_label: Overview
4
+ ---
5
+
6
+ Audit logs answer the question "who did what through your API, and when?" —
7
+ providing a structured, searchable record of every request and business event
8
+ that flows through your gateway. This page explains why audit logging matters
9
+ and the options Zuplo gives you for capturing an audit trail.
10
+
11
+ ## Why audit logging?
12
+
13
+ Audit logging plays a critical role in API security: it lets you detect and
14
+ investigate issues such as unauthorized access or permission elevation, and
15
+ reconstruct exactly what happened to a resource after the fact. It's also a hard
16
+ requirement for many compliance certifications (SOC 2, ISO 27001, HIPAA, PCI
17
+ DSS) and a common buying criterion for enterprise customers of your API.
18
+
19
+ Unlike request logs — which are optimized for debugging and operational
20
+ visibility — audit logs are structured business records: each event identifies
21
+ an actor, an action, and the resources affected, in a stable format you can
22
+ retain, search, and hand to an auditor.
23
+
24
+ ## Options for audit logging
25
+
26
+ Zuplo supports two approaches, depending on where you want your audit trail to
27
+ live:
28
+
29
+ 1. **[Zuplo Audit Logs](./audit-logs.mdx)** — the recommended approach for most
30
+ APIs. Add a single policy and Zuplo records, stores, and indexes a structured
31
+ audit event for every request. You can search the events in the Zuplo Portal,
32
+ query them through the Zuplo API, and export them to a SIEM. No code or
33
+ infrastructure required.
34
+ 2. **[Custom audit logs](./custom-audit-logs.mdx)** — write a small custom
35
+ policy that sends audit events to any external audit provider (for example,
36
+ WorkOS Audit Logs). Full control over the event format and destination.
37
+
38
+ | | Zuplo Audit Logs | Custom audit logs |
39
+ | ------------ | ------------------------- | --------------------- |
40
+ | Setup | Add a policy | Write policy code |
41
+ | Storage | Managed by Zuplo | Your provider |
42
+ | Viewing | Zuplo Portal + Zuplo API | Your provider's tools |
43
+ | Availability | Enterprise (free to test) | All plans |
44
+
45
+ Zuplo Audit Logs is an enterprise feature, but anyone can use it for free for
46
+ development and testing purposes.
47
+
48
+ ## Gateway audit logs versus account audit logs
49
+
50
+ Zuplo has two separate audit trails — the options above all concern the first:
51
+
52
+ - **Gateway audit logs** record traffic through _your API_: requests from your
53
+ API's consumers and custom events emitted by your gateway code.
54
+ - **[Account audit logs](./accounts/audit-logs.mdx)** record administrative
55
+ activity in _your Zuplo account_: project changes, team member management,
56
+ deployments, and other portal and API operations.
57
+
58
+ ## Next steps
59
+
60
+ - [Zuplo Audit Logs](./audit-logs.mdx) — enable the built-in feature and start
61
+ recording events
62
+ - [Custom audit logs](./custom-audit-logs.mdx) — send audit events to an
63
+ external provider
64
+ - [Audit Logs policy reference](../policies/audit-log-inbound.mdx) — all
65
+ configuration options and the full event shape
@@ -0,0 +1,222 @@
1
+ ---
2
+ title: Zuplo Audit Logs
3
+ sidebar_label: Zuplo Audit Logs
4
+ ---
5
+
6
+ Zuplo's Audit Logs feature gives you a complete
7
+ [audit trail](./audit-logging.mdx) of your API with a single policy: the gateway
8
+ records a structured event for every request, stores the events for you, and
9
+ makes them available in the Zuplo Portal and through the Zuplo API.
10
+
11
+ <EnterpriseFeature name="Audit Logs" />
12
+
13
+ ## How it works
14
+
15
+ <Diagram height="h-56">
16
+ <DiagramNode id="client">Client</DiagramNode>
17
+ <DiagramGroup id="zuplo" label="Zuplo">
18
+ <DiagramNode id="gateway" variant="zuplo">
19
+ Gateway (Audit Logs policy)
20
+ </DiagramNode>
21
+ <DiagramNode id="store">Audit log storage</DiagramNode>
22
+ </DiagramGroup>
23
+ <DiagramNode id="consumers">Portal / API / SIEM</DiagramNode>
24
+ <DiagramEdge from="client" to="gateway" />
25
+ <DiagramEdge from="gateway" to="store" label="events" />
26
+ <DiagramEdge from="store" to="consumers" />
27
+ </Diagram>
28
+
29
+ Add the [Audit Logs policy](../policies/audit-log-inbound.mdx) to any route and
30
+ the gateway records one structured audit event per request — who made the
31
+ request, what they called, and the outcome. You can also emit your own custom
32
+ events (for example, "account deleted") from handlers and custom policies. Every
33
+ event follows the [CloudEvents](https://cloudevents.io/) 1.0 format.
34
+
35
+ Zuplo stores the events for you, grouped by environment — production, preview,
36
+ and working copy environments each write to their own bucket. You don't need a
37
+ database, logging service, or any other infrastructure of your own.
38
+
39
+ ## Recording audit events
40
+
41
+ ### Automatic request events
42
+
43
+ Add the policy to the routes you want audited. Typically that's any route that
44
+ modifies data, though depending on your API you may want it on sensitive read
45
+ operations as well (for example, retrieving a secret).
46
+
47
+ ```json title="config/policies.json"
48
+ {
49
+ "name": "audit-logs",
50
+ "policyType": "audit-log-inbound",
51
+ "handler": {
52
+ "export": "AuditLogInboundPolicy",
53
+ "module": "$import(@zuplo/runtime)"
54
+ }
55
+ }
56
+ ```
57
+
58
+ Each request through the policy produces an event of type
59
+ `com.zuplo.api.request` capturing the actor, HTTP method and path, response
60
+ status, caller IP address, and geolocation.
61
+
62
+ Audit events can contain personal or sensitive data. The policy's `include`
63
+ options let you turn off individual fields (query parameters, user identity, IP
64
+ address, geolocation) to satisfy your own data-handling and personally
65
+ identifiable information (PII) policies, and `samplingRate` lets you capture
66
+ only a fraction of requests on high-volume routes. See the
67
+ [policy reference](../policies/audit-log-inbound.mdx) for all options.
68
+
69
+ ### Custom events
70
+
71
+ Beyond the automatic per-request event, record your own domain events from a
72
+ handler or custom policy with the static `AuditLogInboundPolicy.log()` method.
73
+ Only `type` is required — Zuplo fills in the actor, timestamp, request ID, and
74
+ other context fields automatically from the request:
75
+
76
+ ```ts title="modules/handlers.ts"
77
+ import {
78
+ AuditLogInboundPolicy,
79
+ ZuploContext,
80
+ ZuploRequest,
81
+ } from "@zuplo/runtime";
82
+
83
+ export async function deleteAccount(
84
+ request: ZuploRequest,
85
+ context: ZuploContext,
86
+ ) {
87
+ const accountId = request.params.accountId;
88
+
89
+ // ...perform the delete...
90
+
91
+ AuditLogInboundPolicy.log(context, {
92
+ type: "com.acme.account.deleted",
93
+ subject: `account|${accountId}`,
94
+ resources: [{ type: "account", id: accountId }],
95
+ success: true,
96
+ });
97
+
98
+ return new Response(null, { status: 204 });
99
+ }
100
+ ```
101
+
102
+ Audit logging is best-effort by design: `log()` never throws and never blocks or
103
+ fails the request, even if an event can't be recorded.
104
+
105
+ ## Viewing audit logs in the portal
106
+
107
+ To browse and search your audit logs, open your project's
108
+ [**Services**](https://portal.zuplo.com/+/account/project/services) page in the
109
+ Zuplo Portal and select the **Audit Logs** tile for the environment you want to
110
+ inspect.
111
+
112
+ The events view lets you:
113
+
114
+ - Filter events by time range, event type, actor, and subject
115
+ - See the top event types and most active actors for the selected window
116
+ - Select any event to inspect the full event payload
117
+
118
+ ## Querying audit logs with the API
119
+
120
+ Everything in the portal is backed by the Zuplo API, so you can query audit logs
121
+ programmatically — for scripting, custom dashboards, or exporting to other
122
+ systems. Authenticate with a [Zuplo API key](./accounts/zuplo-api-keys.mdx) and
123
+ query events by bucket name. Your bucket name is shown on the **Services** page
124
+ in the portal (it's the same bucket used by the API Key Service).
125
+
126
+ ```bash
127
+ curl "https://dev.zuplo.com/v1/audit-logs/$BUCKET_NAME/events?type=com.zuplo.api.request&startDate=2026-07-01T00:00:00Z&endDate=2026-07-07T00:00:00Z" \
128
+ -H "Authorization: Bearer $ZUPLO_API_KEY"
129
+ ```
130
+
131
+ ```json
132
+ {
133
+ "data": [
134
+ {
135
+ "specversion": "1.0",
136
+ "id": "e6c8b1f2-2c3d-4a5b-9e10-1f2a3b4c5d6e",
137
+ "type": "com.zuplo.api.request",
138
+ "time": "2026-07-05T18:12:04.123Z",
139
+ "actorsub": "user|12356",
140
+ "actortype": "user",
141
+ "requestid": "b1a2c3d4-e5f6-7890-abcd-ef1234567890",
142
+ "httpmethod": "GET",
143
+ "httpurl": "/customers/12345?expand=orders",
144
+ "httpstatus": 200,
145
+ "success": true,
146
+ "ipaddress": "203.0.113.7",
147
+ "useragent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
148
+ "country": "US",
149
+ "region": "Washington",
150
+ "city": "Seattle"
151
+ }
152
+ ],
153
+ "pagination": { "limit": 20, "offset": 0, "total": 1, "hasMore": false }
154
+ }
155
+ ```
156
+
157
+ Two endpoints are available:
158
+
159
+ - **Query events** — `GET /v1/audit-logs/{bucketId}/events` returns matching
160
+ events, filterable by `type`, `actorSub`, `subject`, and time range.
161
+ - **Aggregated stats** — `GET /v1/audit-logs/{bucketId}/stats` returns top-N
162
+ event counts grouped by event type or actor.
163
+
164
+ The `bucketId` path parameter accepts either the bucket ID or the bucket name.
165
+ Each query is limited to a 30-day window between `startDate` and `endDate`. See
166
+ the [Audit Logs API reference](../api/audit-logs) for all parameters and
167
+ response schemas.
168
+
169
+ ## Exporting audit logs to a SIEM
170
+
171
+ Many organizations centralize audit trails in a SIEM (Splunk, Microsoft
172
+ Sentinel, Datadog Cloud SIEM, and so on) to correlate API activity with other
173
+ security signals, drive alerting, and meet long-term retention requirements. The
174
+ events API is designed for this: run a scheduled job that pulls new events since
175
+ its last checkpoint and forwards them to your SIEM.
176
+
177
+ ```ts
178
+ const BASE_URL = "https://dev.zuplo.com/v1/audit-logs";
179
+
180
+ // Run on a schedule (e.g. every 5 minutes). `startDate` is the checkpoint
181
+ // saved by the previous run; `endDate` is the current time.
182
+ async function exportAuditLogs(
183
+ bucketName: string,
184
+ startDate: string,
185
+ endDate: string,
186
+ ) {
187
+ let offset = 0;
188
+ let hasMore = true;
189
+ while (hasMore) {
190
+ const url = new URL(`${BASE_URL}/${bucketName}/events`);
191
+ url.searchParams.set("startDate", startDate);
192
+ url.searchParams.set("endDate", endDate);
193
+ url.searchParams.set("limit", "100");
194
+ url.searchParams.set("offset", String(offset));
195
+ const response = await fetch(url, {
196
+ headers: { Authorization: `Bearer ${process.env.ZUPLO_API_KEY}` },
197
+ });
198
+ const { data, pagination } = await response.json();
199
+ await sendToSiem(data); // e.g. Splunk HEC, Datadog Logs API, S3
200
+ offset += data.length;
201
+ hasMore = pagination.hasMore;
202
+ }
203
+ }
204
+ ```
205
+
206
+ Because every event is a CloudEvents document, most SIEMs can ingest the payload
207
+ directly without transformation.
208
+
209
+ Alternatively, if you'd rather send audit events to an external audit provider
210
+ directly from the gateway instead of storing them in Zuplo, see
211
+ [Custom Audit Logs](./custom-audit-logs.mdx).
212
+
213
+ ## Related resources
214
+
215
+ - [Audit logging overview](./audit-logging.mdx) — why audit logging matters and
216
+ how the options compare
217
+ - [Audit Logs policy reference](../policies/audit-log-inbound.mdx) — all
218
+ configuration options, custom event fields, and the full event shape
219
+ - [Audit Logs API reference](../api/audit-logs) — query events and stats
220
+ programmatically
221
+ - [Account audit logs](./accounts/audit-logs.mdx) — the audit trail of your
222
+ Zuplo account itself
@@ -1,29 +1,22 @@
1
1
  ---
2
- title: Custom Audit Logging Policy
3
- sidebar_label: Audit Logging
2
+ title: Custom Audit Logs
3
+ sidebar_label: Custom Audit Logs
4
4
  ---
5
5
 
6
- This guide explains how to add custom audit logging beyond the scope of the
7
- `AuditLogsInboundPolicy`.
6
+ [Zuplo Audit Logs](./audit-logs.mdx) stores your audit trail in Zuplo and gives
7
+ you a portal viewer and query API out of the box. If your organization uses an
8
+ external audit logging service instead, you can send events to it directly from
9
+ the gateway with a [custom policy](../policies/custom-code-inbound.mdx) that
10
+ uses [runtime hooks](../programmable-api/runtime-extensions.mdx). This approach
11
+ works on any plan — it's just custom code — and gives you full control over
12
+ which events are recorded, the event format, and the destination.
8
13
 
9
- Audit logging is an important part of API security that plays a critical role in
10
- detecting and correcting issues such as unauthorized access or permission
11
- elevations within your system. Audit logging is also a requirement for many
12
- compliance certifications as well as part of the buying criteria for larger
13
- enterprises.
14
-
15
- Adding Audit Logging to your APIs that are secured with Zuplo is as easy as
16
- adding a custom policy. Typically you want to add audit logs to any API that
17
- modifies data, however depending on the API you may want it on read operations
18
- as well (for example retrieve a secret key, etc.)
19
-
20
- ## Example Policy: WorkOS Audit Logs
14
+ ## Example: WorkOS Audit Logs
21
15
 
22
16
  [WorkOS](https://workos.com/) provides various services that help enable
23
- enterprise features on your service such as SSO and Audit Logs. With Zuplo it's
24
- easy to create a [custom policy](../policies/custom-code-inbound.mdx) that uses
25
- [runtime hooks](../programmable-api/runtime-extensions.mdx) to log API calls
26
- using their API.
17
+ enterprise features on your service such as SSO and Audit Logs. The following
18
+ custom policy logs API calls to the WorkOS Audit Logs API after each response is
19
+ sent:
27
20
 
28
21
  ```ts
29
22
  import { ZuploContext, ZuploRequest, environment } from "@zuplo/runtime";
@@ -34,7 +27,7 @@ export async function auditLogPlugin(
34
27
  policyName: string,
35
28
  ) {
36
29
  // Clone the request so the body can be read in the hook
37
- // note: remove this is you don't need content from the body
30
+ // note: remove this if you don't need content from the body
38
31
  const cloned = request.clone();
39
32
  context.addResponseSendingFinalHook(async (response) => {
40
33
  const incomingBody = await cloned.json();
@@ -48,7 +41,7 @@ export async function auditLogPlugin(
48
41
  version: 1,
49
42
  actor: {
50
43
  type: "user",
51
- // Add the use the user sub for authenticated users
44
+ // Use the user sub for authenticated users
52
45
  id: request.user.sub,
53
46
  metadata: {
54
47
  role: "user",
@@ -93,3 +86,14 @@ export async function auditLogPlugin(
93
86
  return request;
94
87
  }
95
88
  ```
89
+
90
+ The same pattern works for any provider with an HTTP API — swap the event shape
91
+ and endpoint for your provider's, and store credentials in
92
+ [environment variables](./environment-variables.mdx).
93
+
94
+ ## Related resources
95
+
96
+ - [Audit logging overview](./audit-logging.mdx) — why audit logging matters and
97
+ how the options compare
98
+ - [Zuplo Audit Logs](./audit-logs.mdx) — the built-in feature with portal
99
+ viewing and a query API
@@ -91,6 +91,7 @@
91
91
  | request-size-limit-inbound | Request Size Limit | Enforces a maximum size in bytes of the incoming request. | api-gateway |
92
92
  | request-validation-inbound | Request Validation | Validates incoming requests against your OpenAPI specification. Checks query parameters, path parameters, headers, and request body to ensure they match the defined schema before processing. | api-gateway |
93
93
  | require-origin-inbound | Require Origin | Sets an allow-list for an origin header | api-gateway |
94
+ | require-user-claims-inbound | Require User Claims | Authorizes requests by validating claims on the authenticated user (`request.user`) against a configurable rule of `and`/`or` combinators and per-claim `eq`, `in`, and `startsWith` checks. Run it after any authentication policy that populates `request.user` — a JWT auth policy, API key auth, mTLS, and so on — to allow only specific callers (service accounts, OAuth clients, tenants, groups) without writing custom code. Every check fails closed: a missing or non-primitive claim never matches, comparisons are strict and type-sensitive, and requests without an authenticated user receive a 401 response. Denied requests receive a 403 response that does not echo claim values or expected values; the failing checks are written to the request log instead. Validation of the options runs lazily inside the policy constructor, which the runtime caches per policy name. Misconfigured options therefore fail on first use with a customer-facing `ConfigurationError` instead of failing at module load. | api-gateway |
94
95
  | secret-masking-outbound | Secret Masking | Masks common secrets like Zuplo API keys, GitHub tokens, or SSH private key in the response body. | api-gateway |
95
96
  | semantic-cache-inbound | Semantic Cache | Respond to matched incoming requests with semantically cached content The Semantic Cache Inbound policy caches responses based on semantic similarity of cache keys rather than exact matches. This allows for more flexible caching where similar requests can return cached responses even if the cache key is not exactly the same. The policy uses Large Language Model (LLM) embeddings to determine semantic similarity between cache keys based on a configurable similarity tolerance. Options: - semanticTolerance: The semantic similarity threshold for semantic cache matches (0-1, default: 0.2). Values closer to 0 require higher similarity. Can be overridden by custom functions. - expirationSecondsTtl: The timeout of the cache in seconds (default: 3600, 1 hour). Can be overridden by custom functions. - namespace: Optional namespace to isolate cache entries (default: "default"). Useful for multi-tenant scenarios or different cache contexts. - cacheBy: Determines how cache keys are generated: 'function' for custom logic or 'propertyPath' to extract from JSON body. | ai-gateway |
96
97
  | set-body-inbound | Set Body | Sets the body of the request in the inbound pipeline - make sure to convert a GET/HEAD request to another method when using this policy. | api-gateway |
@@ -16,14 +16,14 @@ requests).
16
16
  ## Controlling What's Logged
17
17
 
18
18
  Audit events can contain personal or sensitive data. To keep audit logging
19
- compliant with your own data-handling and PII policies, use the `include`
20
- option to turn individual fields off. Everything is captured by default; set a
21
- flag to `false` to omit it:
19
+ compliant with your own data-handling and PII policies, use the `include` option
20
+ to turn individual fields off. Everything is captured by default; set a flag to
21
+ `false` to omit it:
22
22
 
23
23
  - `queryParams` — the request's query-string parameters (in the logged URL).
24
- - `user` — the authenticated user / actor identity (subject, email,
25
- connection). The `user` vs `anonymous` classification is always kept, but the
26
- identifying values are omitted when disabled.
24
+ - `user` — the authenticated user / actor identity (subject, email, connection).
25
+ The `user` vs `anonymous` classification is always kept, but the identifying
26
+ values are omitted when disabled.
27
27
  - `ipAddress` — the caller's IP address.
28
28
  - `geolocation` — the caller's country, region, and city.
29
29
 
@@ -87,7 +87,8 @@ export async function deleteAccount(
87
87
  The event object passed to `log()` supports the following fields. Only `type` is
88
88
  required.
89
89
 
90
- - `type` — the event type, in reverse-DNS form (e.g. `com.acme.account.deleted`).
90
+ - `type` — the event type, in reverse-DNS form (e.g.
91
+ `com.acme.account.deleted`).
91
92
  - `subject` — the primary entity the event is about.
92
93
  - `resources` — an array of `{ type, id, metadata? }` describing the resources
93
94
  the event affected.
@@ -95,9 +96,8 @@ required.
95
96
  - `data` — any additional structured data to attach to the event.
96
97
  - `actor` — override the automatically-derived actor
97
98
  (`{ sub, type, email, connection }`).
98
- - `country`, `region`, `city` — override the caller's geolocation. These
99
- default to the current request's location, so you normally don't need to set
100
- them.
99
+ - `country`, `region`, `city` — override the caller's geolocation. These default
100
+ to the current request's location, so you normally don't need to set them.
101
101
 
102
102
  Audit logging is best-effort: `log()` never throws and never blocks or fails the
103
103
  request, even if an event cannot be recorded.
@@ -134,4 +134,9 @@ pass, and the remaining fields are populated from the request context.
134
134
 
135
135
  ## Viewing Audit Logs
136
136
 
137
- In-portal viewing and retrieval of audit logs is coming soon.
137
+ Audit logs are available in the [Zuplo portal](https://portal.zuplo.com) under
138
+ your project's Audit Log service, where you can browse and search the events
139
+ recorded by this policy.
140
+
141
+ You can also query audit logs programmatically using the
142
+ [Zuplo API](https://zuplo.com/docs/api).
@@ -7,7 +7,7 @@
7
7
  "isPaidAddOn": false,
8
8
  "isEnterprise": true,
9
9
  "isInternal": false,
10
- "isBeta": true,
10
+ "isBeta": false,
11
11
  "isHidden": false,
12
12
  "requiresAI": false,
13
13
  "products": ["api-gateway"],
@@ -7,7 +7,7 @@
7
7
  "isPaidAddOn": false,
8
8
  "isEnterprise": true,
9
9
  "isInternal": false,
10
- "isBeta": true,
10
+ "isBeta": false,
11
11
  "isHidden": false,
12
12
  "requiresAI": false,
13
13
  "products": ["api-gateway"],
@@ -1,7 +1,44 @@
1
- This policy verifies client mTLS results supplied by Zuplo's edge proxy. It
2
- checks the incoming mTLS verification status and, when enforcement is enabled,
3
- rejects requests where no client certificate was presented, certificate
4
- verification failed, or the certificate metadata cannot be parsed.
1
+ The `mtls-auth-inbound` policy is the enforcement step of client mutual TLS
2
+ (mTLS). It reads the verification result that Zuplo's edge proxy attaches to
3
+ each request and, when enforcement is enabled, rejects requests that don't
4
+ present a valid client certificate signed by a CA you trust. It is one part of a
5
+ complete client mTLS setup: you first upload the signing CA to your account,
6
+ then add this policy to the routes you want to protect.
7
+
8
+ For the full walkthrough — CA upload, configuration, testing with `curl`,
9
+ rotation, and troubleshooting (including the common
10
+ `FAILED to get issuer certificate` error) — see the
11
+ [Client mTLS authentication guide](https://zuplo.com/docs/articles/securing-the-gateway-with-client-mtls).
12
+
13
+ ## Setting up client mTLS
14
+
15
+ This policy only enforces a result; it does not, on its own, make Zuplo verify
16
+ client certificates. The end-to-end setup is:
17
+
18
+ 1. **Upload your CA certificate** to your account with the
19
+ [Zuplo CLI](https://zuplo.com/docs/cli/ca-certificate-create):
20
+ `zuplo ca-certificate create --name my_ca --cert ./ca.pem --account your-account`.
21
+ Upload the self-signed **root** CA that anchors the chain, not an
22
+ intermediate. CA certificates are scoped to your **account**, so once
23
+ uploaded, every gateway domain on the account verifies presented client
24
+ certificates against it.
25
+ 2. **Add this policy** (`mtls-auth-inbound`) to each route that should require a
26
+ verified client certificate, and set `certIssuerDN` to pin the issuer (see
27
+ the options described below).
28
+ 3. **Read the certificate** in your request handler or downstream policies from
29
+ `request.user.data.mtlsAuth`.
30
+ 4. **Test** with a client certificate issued by (or chained to) your CA:
31
+ `curl --cert ./client.pem --key ./client.key https://your-gateway.zuplo.app/v1/example`.
32
+
33
+ See the
34
+ [full guide](https://zuplo.com/docs/articles/securing-the-gateway-with-client-mtls)
35
+ for CA management, custom domains, rotation, and troubleshooting.
36
+
37
+ ## How the policy behaves
38
+
39
+ When enforcement is enabled, the policy rejects requests where no client
40
+ certificate was presented, certificate verification failed, or the certificate
41
+ metadata cannot be parsed.
5
42
 
6
43
  When verification passes, the policy parses the client certificate metadata and
7
44
  sets it on `request.user.data.mtlsAuth`. The metadata includes `subject`,
@@ -30,5 +67,15 @@ inspect `request.user.data.mtlsAuth.issuer` from a request signed by the desired
30
67
  CA.
31
68
 
32
69
  Note: this policy does not work with local development since it relies on
33
- metadata from the upstream reverse proxy, it is recommended to test this using a
70
+ metadata from the upstream reverse proxy. It is recommended to test this using a
34
71
  working-copy or preview environment.
72
+
73
+ ## Learn more
74
+
75
+ - [Client mTLS authentication guide](https://zuplo.com/docs/articles/securing-the-gateway-with-client-mtls)
76
+ — full end-to-end setup, CA management, and troubleshooting
77
+ - [`ca-certificate` CLI reference](https://zuplo.com/docs/cli/ca-certificate-create)
78
+ — upload, list, describe, rename, and delete account CA certificates
79
+ - [Gateway to Origin mTLS](https://zuplo.com/docs/articles/securing-backend-mtls)
80
+ — the reverse direction, where Zuplo authenticates to your backend with a
81
+ client certificate
@@ -7,7 +7,7 @@
7
7
  "isPaidAddOn": false,
8
8
  "isEnterprise": true,
9
9
  "isInternal": false,
10
- "isBeta": true,
10
+ "isBeta": false,
11
11
  "isHidden": false,
12
12
  "requiresAI": false,
13
13
  "products": ["api-gateway"],
@@ -7,7 +7,7 @@
7
7
  "isPaidAddOn": false,
8
8
  "isEnterprise": true,
9
9
  "isInternal": false,
10
- "isBeta": true,
10
+ "isBeta": false,
11
11
  "isHidden": false,
12
12
  "requiresAI": false,
13
13
  "products": ["api-gateway"],