zuplo 6.71.25 → 6.71.27

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.
@@ -10,13 +10,6 @@ account, helping you track changes and maintain compliance requirements. Every
10
10
  change performed in your account is logged with detailed information about who
11
11
  performed the change, when it occurred, and what resources were affected.
12
12
 
13
- :::note{title="Beta Feature"}
14
-
15
- Audit logs are currently in beta. During the beta period not all events may be
16
- captured. Full coverage is in progress.
17
-
18
- :::
19
-
20
13
  ## Overview
21
14
 
22
15
  Audit logs capture critical events across your Zuplo infrastructure, including:
@@ -3,13 +3,6 @@ title: "Managed Dedicated: Federated Gateways"
3
3
  sidebar_label: Federated Gateways
4
4
  ---
5
5
 
6
- :::note{title="Beta Feature"}
7
-
8
- This feature is in Beta - please use with care and provide feedback to the team
9
- if you encounter any issues.
10
-
11
- :::
12
-
13
6
  <EnterpriseFeature name="Federated Gateways" />
14
7
 
15
8
  With a managed dedicated Zuplo instance you can create a federated gateway that
@@ -30,7 +23,7 @@ Federated gateways are useful for several reasons:
30
23
  - **Versioning**: You can use federated gateways to manage different versions of
31
24
  your API. For example, if you wanted to run a new version of your API
32
25
  alongside the old one and route requests to specific versions based on user or
33
- other context you can keep different versions in separate Zuplo projects.
26
+ other context, you can keep different versions in separate Zuplo projects.
34
27
  - **Risk Minimization**: By separating different parts of your API into
35
28
  different Zuplo projects, you can reduce the risk of causing inadvertent
36
29
  changes to other parts of the API. This can be especially useful if your API
@@ -8,13 +8,6 @@ description: |
8
8
  have to.
9
9
  ---
10
10
 
11
- :::note{title="Beta"}
12
-
13
- Cross App Access support is in beta. The configuration model and policy options
14
- may change before general availability.
15
-
16
- :::
17
-
18
11
  Cross App Access (XAA) lets one application reach another application's API on a
19
12
  user's behalf **through the identity provider both apps already trust** —
20
13
  instead of running a separate, point-to-point OAuth connection between them. For
@@ -21,13 +21,6 @@ emits the UI.
21
21
 
22
22
  :::
23
23
 
24
- :::warning
25
-
26
- The OpenAI Apps SDK and support for it in Zuplo's `mcpServerHandler` is in beta
27
- and subject to change!
28
-
29
- :::
30
-
31
24
  ## `tools`
32
25
 
33
26
  MCP tools define the functionality of your app. Zuplo MCP servers support tools
@@ -17,7 +17,7 @@
17
17
  | archive-request-gcp-storage-inbound | Archive Request to GCP Storage | Archive the incoming request to Google Cloud Storage. | api-gateway |
18
18
  | archive-response-aws-s3-outbound | Archive Response to AWS S3 | Archive the outgoing response body to AWS S3 storage | api-gateway |
19
19
  | archive-response-azure-storage-outbound | Archive Response to Azure Storage | Archive the outgoing response to Azure blob storage. | api-gateway |
20
- | audit-log-inbound | Audit Logs | Capture detailed logs of requests for auditing purposes. | api-gateway |
20
+ | audit-log-inbound | Audit Logs | Capture detailed logs of requests for auditing purposes. The policy emits one structured CloudEvent per request, and user code can emit additional events via the static `log()` method. | api-gateway |
21
21
  | auth0-jwt-auth-inbound | Auth0 JWT Auth | Authenticate users using Auth0 issued JWT tokens. | api-gateway |
22
22
  | authzen-inbound | AuthZEN Authorization | Authorize requests using an AuthZEN compatible PDP | api-gateway |
23
23
  | cognito-jwt-auth-inbound | AWS Cognito JWT Auth | Authenticate requests with JWT tokens issued by AWS Cognito. | api-gateway |
@@ -1,78 +1,137 @@
1
- ## Adding Custom Metadata
1
+ ## Automatic Request Auditing
2
2
 
3
- You can add any additional data to the audit logs with a custom function.
3
+ Once the Audit Logs policy is added to a route, it automatically records one
4
+ structured audit event for every request that passes through it — no extra code
5
+ required. Each event captures who made the request, what they called, and the
6
+ outcome.
4
7
 
5
- :::note
8
+ The automatic event uses the type `com.zuplo.api.request` and includes the
9
+ actor, the HTTP method and path, the response status, and the geolocation of the
10
+ caller.
6
11
 
7
- Custom metadata functions cannot be asynchronous. Due to the frequency of their
8
- calls, asynchronous functions will add significant latency to your API.
12
+ For very high-volume routes you can record only a fraction of requests by
13
+ setting the `samplingRate` option (for example `0.1` to capture 10% of
14
+ requests).
9
15
 
10
- :::
16
+ ## Controlling What's Logged
11
17
 
12
- ```ts
13
- //module - ./modules/audit-logs.ts
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:
14
22
 
15
- import { ZuploRequest } from "@zuplo/runtime";
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.
27
+ - `ipAddress` — the caller's IP address.
28
+ - `geolocation` — the caller's country, region, and city.
16
29
 
17
- export function auditLogMetadata(request: ZuploRequest): any {
18
- const metadata = {
19
- accountId: request.user.data.account,
20
- customTraceId: request.headers.get("custom-trace-id"),
21
- };
22
- return metadata;
30
+ ```json
31
+ {
32
+ "name": "audit-logs-inbound",
33
+ "policyType": "audit-logs",
34
+ "handler": {
35
+ "export": "AuditLogInboundPolicy",
36
+ "module": "$import(@zuplo/runtime)",
37
+ "options": {
38
+ "include": {
39
+ "queryParams": false,
40
+ "ipAddress": false
41
+ }
42
+ }
43
+ }
23
44
  }
24
45
  ```
25
46
 
26
- ## Log Data
47
+ These settings also apply to the geolocation automatically added to custom
48
+ events emitted via `log()`.
27
49
 
28
- The structure of an audit log is shown below.
50
+ ## Emitting Custom Events
29
51
 
30
- ```json
31
- {
32
- "route": "/customers/:customerId",
33
- "method": "GET",
34
- "query": {
35
- "a": 1,
36
- "b": 2
37
- },
38
- "params": {
39
- "customerId": "12345"
40
- },
41
- "headers": {
42
- "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36"
43
- },
44
- "user": {
45
- "sub": "user|12356"
46
- },
47
- "geolocation": {
48
- "country": "US",
49
- "city": "Seattle",
50
- "continent": "NA",
51
- "latitude": "1.123",
52
- "longitude": "4.567",
53
- "postalCode": "29700",
54
- "metroCode": "100",
55
- "region": "Washington",
56
- "timezone": "America/LosAngeles"
57
- },
58
- "metadata": {
59
- // Custom data
60
- }
52
+ Beyond the automatic per-request event, you can record your own domain events —
53
+ for example "account deleted" or "API key rotated" — from a handler or custom
54
+ policy using the static `AuditLogInboundPolicy.log()` method. Any fields you
55
+ don't supply are filled in automatically from the request context (event id,
56
+ time, actor, request id, IP address, user agent, and so on).
57
+
58
+ ```ts
59
+ // module - ./modules/audit.ts
60
+
61
+ import {
62
+ AuditLogInboundPolicy,
63
+ ZuploContext,
64
+ ZuploRequest,
65
+ } from "@zuplo/runtime";
66
+
67
+ export async function deleteAccount(
68
+ request: ZuploRequest,
69
+ context: ZuploContext
70
+ ) {
71
+ const accountId = request.params.accountId;
72
+
73
+ // ...perform the delete...
74
+
75
+ AuditLogInboundPolicy.log(context, {
76
+ type: "com.acme.account.deleted",
77
+ subject: `account|${accountId}`,
78
+ resources: [{ type: "account", id: accountId }],
79
+ success: true,
80
+ data: { reason: "user-initiated" },
81
+ });
82
+
83
+ return new Response(null, { status: 204 });
61
84
  }
62
85
  ```
63
86
 
64
- ## Audit Logs in the Portal
87
+ The event object passed to `log()` supports the following fields. Only `type` is
88
+ required.
65
89
 
66
- Audit logs are not currently surfaced in the Zuplo portal, but the feature is
67
- planned soon.
90
+ - `type` the event type, in reverse-DNS form (e.g. `com.acme.account.deleted`).
91
+ - `subject` — the primary entity the event is about.
92
+ - `resources` — an array of `{ type, id, metadata? }` describing the resources
93
+ the event affected.
94
+ - `success` — whether the operation succeeded.
95
+ - `data` — any additional structured data to attach to the event.
96
+ - `actor` — override the automatically-derived actor
97
+ (`{ 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.
68
101
 
69
- ## Audit Log API
102
+ Audit logging is best-effort: `log()` never throws and never blocks or fails the
103
+ request, even if an event cannot be recorded.
70
104
 
71
- Audit logs can be retrieved using the Zuplo Management API. Logs can be
72
- retrieved by time span and can be filtered by `tenant`.
105
+ ## Audit Log Event Shape
73
106
 
74
- ```http
75
- GET /deployments/:deploymentId/auditlogs?tenant=TENANT
76
- content-type: application/json
77
- authorization: Bearer YOUR_TOKEN
107
+ Every audit event follows the [CloudEvents](https://cloudevents.io/) 1.0
108
+ envelope. An automatically-emitted request event looks like this:
109
+
110
+ ```json
111
+ {
112
+ "specversion": "1.0",
113
+ "id": "e6c8b1f2-2c3d-4a5b-9e10-1f2a3b4c5d6e",
114
+ "type": "com.zuplo.api.request",
115
+ "time": "2026-07-05T18:12:04.123Z",
116
+ "actorsub": "user|12356",
117
+ "actortype": "user",
118
+ "requestid": "b1a2c3d4-e5f6-7890-abcd-ef1234567890",
119
+ "httpmethod": "GET",
120
+ "httpurl": "/customers/12345?expand=orders",
121
+ "httpstatus": 200,
122
+ "success": true,
123
+ "ipaddress": "203.0.113.7",
124
+ "useragent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
125
+ "country": "US",
126
+ "region": "Washington",
127
+ "city": "Seattle"
128
+ }
78
129
  ```
130
+
131
+ Custom events emitted via `log()` share the same envelope; the `type`,
132
+ `subject`, `resources`, `success`, and `data` fields come from the event you
133
+ pass, and the remaining fields are populated from the request context.
134
+
135
+ ## Viewing Audit Logs
136
+
137
+ In-portal viewing and retrieval of audit logs is coming soon.
@@ -4,14 +4,14 @@
4
4
  "type": "object",
5
5
  "title": "Audit Logs",
6
6
  "isDeprecated": false,
7
- "isPaidAddOn": true,
8
- "isEnterprise": false,
7
+ "isPaidAddOn": false,
8
+ "isEnterprise": true,
9
9
  "isInternal": false,
10
10
  "isBeta": false,
11
11
  "isHidden": false,
12
12
  "requiresAI": false,
13
13
  "products": ["api-gateway"],
14
- "description": "Capture detailed logs of requests for auditing purposes.",
14
+ "description": "Capture detailed logs of requests for auditing purposes. The policy emits one structured CloudEvent per request, and user code can emit additional events via the static `log()` method.",
15
15
  "deprecatedMessage": "",
16
16
  "required": ["handler"],
17
17
  "properties": {
@@ -21,7 +21,7 @@
21
21
  "required": ["export", "module", "options"],
22
22
  "properties": {
23
23
  "export": {
24
- "const": "AuditLogsInboundPolicy",
24
+ "const": "AuditLogInboundPolicy",
25
25
  "description": "The name of the exported type"
26
26
  },
27
27
  "module": {
@@ -29,51 +29,71 @@
29
29
  "description": "The module containing the policy"
30
30
  },
31
31
  "options": {
32
- "title": "AuditLogsInboundPolicyOptions",
32
+ "title": "AuditLogInboundPolicyOptions",
33
33
  "type": "object",
34
- "description": "The options for this policy.",
34
+ "description": "The options for the Audit Log Inbound policy.",
35
35
  "additionalProperties": false,
36
- "required": ["blockScoresBelow"],
36
+ "required": [],
37
37
  "properties": {
38
- "logIpAddress": {
39
- "default": true,
40
- "description": "if the IP address should be logged."
38
+ "eventTypeBase": {
39
+ "type": "string",
40
+ "description": "Reverse-DNS prefix used for the auto-emitted request event type. Defaults to 'com.zuplo.api'.",
41
+ "default": "com.zuplo.api"
41
42
  },
42
- "logUser": {
43
- "default": true,
44
- "description": "if the user's `sub` should be logged."
43
+ "bucketId": {
44
+ "type": "string",
45
+ "description": "Override the bucket id this policy emits events for. Defaults to the bucket id from the runtime environment."
45
46
  },
46
- "logGeolocation": {
47
- "default": true,
48
- "description": "if the geolocation information should be logged (i.e. state, country, longitude, latitude, etc.)."
47
+ "samplingRate": {
48
+ "type": "number",
49
+ "description": "Fraction of requests to capture, between 0 (none) and 1 (all). Defaults to 1.",
50
+ "default": 1,
51
+ "minimum": 0,
52
+ "maximum": 1
49
53
  },
50
- "logQueryParameters": {
51
- "default": true,
52
- "description": "log the values of query parameters."
53
- },
54
- "logRouteParameters": {
55
- "default": true,
56
- "description": "The parameters in the route to log."
57
- },
58
- "tenant": {
59
- "description": "if the route parameters should be logged (i.e. the value of `customerId` in the route `/customers/:customerId`)."
60
- },
61
- "metadata": {
62
- "description": "A function to add additional data to the audit logs."
54
+ "include": {
55
+ "type": "object",
56
+ "description": "Controls which potentially-sensitive parts of each request are captured in the auto-emitted audit event. Disable fields to satisfy your own data-handling and PII policies. All are enabled by default.",
57
+ "additionalProperties": false,
58
+ "properties": {
59
+ "queryParams": {
60
+ "type": "boolean",
61
+ "default": true,
62
+ "description": "Include the request's query-string parameters in the logged URL. Disable if query strings may contain sensitive data."
63
+ },
64
+ "user": {
65
+ "type": "boolean",
66
+ "default": true,
67
+ "description": "Include the authenticated user / actor identity (subject, email, connection)."
68
+ },
69
+ "ipAddress": {
70
+ "type": "boolean",
71
+ "default": true,
72
+ "description": "Include the caller's IP address."
73
+ },
74
+ "geolocation": {
75
+ "type": "boolean",
76
+ "default": true,
77
+ "description": "Include the caller's geolocation (country, region, city)."
78
+ }
79
+ }
63
80
  }
64
81
  }
65
82
  }
66
83
  },
67
84
  "examples": [
68
85
  {
69
- "export": "AuditLogsInboundPolicy",
86
+ "export": "AuditLogInboundPolicy",
70
87
  "module": "$import(@zuplo/runtime)",
71
88
  "options": {
72
- "logGeolocation": true,
73
- "logIpAddress": true,
74
- "logQueryParameters": true,
75
- "logRouteParameters": true,
76
- "logUser": true
89
+ "eventTypeBase": "com.zuplo.api",
90
+ "include": {
91
+ "geolocation": true,
92
+ "ipAddress": true,
93
+ "queryParams": true,
94
+ "user": true
95
+ },
96
+ "samplingRate": 1
77
97
  }
78
98
  }
79
99
  ]
@@ -59,6 +59,32 @@ import { MonetizationInboundPolicy } from "@zuplo/runtime";
59
59
  const meters = MonetizationInboundPolicy.getMeters(context);
60
60
  ```
61
61
 
62
+ ### Flush (send) runtime meters mid-request
63
+
64
+ Use `flushMeters` to send accumulated runtime meters immediately and reset their
65
+ counts. This is useful for long-running requests where you want to report
66
+ partial usage before the response is sent (for example, after each chunk of work
67
+ in a streaming handler).
68
+
69
+ ```typescript
70
+ import { MonetizationInboundPolicy } from "@zuplo/runtime";
71
+
72
+ MonetizationInboundPolicy.addMeters(context, { input_tokens: 500 });
73
+ await MonetizationInboundPolicy.flushMeters(context);
74
+
75
+ // Runtime meters are cleared before the backend send begins so a concurrent
76
+ // final hook cannot double-count them. Meters are restored if the send fails.
77
+ MonetizationInboundPolicy.addMeters(context, { input_tokens: 300 });
78
+ ```
79
+
80
+ Static `options.meters` are not included in `flushMeters`; they are still sent
81
+ in the final response hook.
82
+
83
+ Note: `flushMeters` performs entitlement validation and will throw if called
84
+ before the monetization policy runs on the request or if you send meters that
85
+ aren’t present/accessible on the subscription. Wrap in `try/catch` if you want
86
+ to continue the request when a flush fails.
87
+
62
88
  ## Subscription data (for customization)
63
89
 
64
90
  The monetization policy validates the API key and attaches the **subscription**
@@ -163,4 +189,7 @@ For a meter key like `api` with `options.meters.api = 1`:
163
189
 
164
190
  - `setMeters` replaces current request meter increments.
165
191
  - `addMeters` accumulates values across multiple calls.
192
+ - `flushMeters` sends runtime meters immediately, clears them before the backend
193
+ send (to avoid duplicate sends if the final hook runs concurrently), and
194
+ restores them if the send fails.
166
195
  - Entitlements are validated before usage is sent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuplo",
3
- "version": "6.71.25",
3
+ "version": "6.71.27",
4
4
  "type": "module",
5
5
  "description": "The programmable API Gateway",
6
6
  "author": "Zuplo, Inc.",
@@ -19,9 +19,9 @@
19
19
  "zuplo": "zuplo.js"
20
20
  },
21
21
  "dependencies": {
22
- "@zuplo/cli": "6.71.25",
23
- "@zuplo/core": "6.71.25",
24
- "@zuplo/runtime": "6.71.25",
22
+ "@zuplo/cli": "6.71.27",
23
+ "@zuplo/core": "6.71.27",
24
+ "@zuplo/runtime": "6.71.27",
25
25
  "@zuplo/test": "1.4.0"
26
26
  }
27
27
  }