zuplo 6.73.18 → 6.73.20

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.
@@ -45,6 +45,16 @@ certificates, you can add the following script:
45
45
  }
46
46
  ```
47
47
 
48
+ ## mTLS client certificates don't load
49
+
50
+ On `managed-dedicated` and `self-hosted` projects, `zuplo dev` loads client
51
+ certificates from `.zuplo-local/mtls/<cert-name>/tls.crt` and `tls.key`, or from
52
+ the directory passed to `--mtls-certs`. If the startup output reports no loaded
53
+ certificates, check the warnings listed in
54
+ [Local certificates fail to load](./securing-backend-mtls.mdx#local-certificates-fail-to-load).
55
+
56
+ Local mTLS isn't available on `managed-edge` projects.
57
+
48
58
  ## "unknown format ... ignored" warnings
49
59
 
50
60
  When the development server prints warnings like
@@ -71,7 +71,7 @@ project. You can upload multiple certificates, each with a unique name.
71
71
  zuplo mtls-certificate create \
72
72
  --cert cert.pem \
73
73
  --key key.pem \
74
- --name my-backend-cert \
74
+ --name my_backend_cert \
75
75
  --account your-account \
76
76
  --project your-project \
77
77
  --environment-type development \
@@ -113,7 +113,7 @@ import { ZuploContext, ZuploRequest } from "@zuplo/runtime";
113
113
  export default async function (request: ZuploRequest, context: ZuploContext) {
114
114
  const response = await fetch("https://secure-backend.example.com/api", {
115
115
  zuplo: {
116
- mtlsCertificate: "my-backend-cert",
116
+ mtlsCertificate: "my_backend_cert",
117
117
  },
118
118
  });
119
119
 
@@ -132,7 +132,7 @@ that make outbound requests:
132
132
  "module": "$import(@zuplo/runtime)",
133
133
  "options": {
134
134
  "baseUrl": "https://secure-backend.example.com",
135
- "mtlsCertificate": "my-backend-cert"
135
+ "mtlsCertificate": "my_backend_cert"
136
136
  }
137
137
  }
138
138
  ```
@@ -145,13 +145,13 @@ For better flexibility across environments, store the certificate name as an
145
145
  **Production environment:**
146
146
 
147
147
  ```text
148
- BACKEND_MTLS_CERT=my-backend-prod-cert
148
+ BACKEND_MTLS_CERT=my_backend_prod_cert
149
149
  ```
150
150
 
151
151
  **Staging environment:**
152
152
 
153
153
  ```text
154
- BACKEND_MTLS_CERT=my-backend-staging-cert
154
+ BACKEND_MTLS_CERT=my_backend_staging_cert
155
155
  ```
156
156
 
157
157
  Then reference it in your code:
@@ -195,13 +195,43 @@ zuplo mtls-certificate list \
195
195
  --project your-project
196
196
  ```
197
197
 
198
+ Each entry includes the certificate ID, which the other commands take as
199
+ `--cert-id`.
200
+
201
+ ### Inspecting a Certificate
202
+
203
+ To see the subject, issuer, validity dates, and enabled environment types for
204
+ one certificate:
205
+
206
+ ```bash
207
+ zuplo mtls-certificate describe \
208
+ --cert-id cert_abc123 \
209
+ --account your-account \
210
+ --project your-project
211
+ ```
212
+
213
+ ### Changing Environment Types
214
+
215
+ To change which environment types can use a certificate, pass the complete set
216
+ of environment types you want enabled — the command replaces the existing set
217
+ rather than adding to it:
218
+
219
+ ```bash
220
+ zuplo mtls-certificate update \
221
+ --cert-id cert_abc123 \
222
+ --environment-type preview \
223
+ --environment-type production \
224
+ --account your-account \
225
+ --project your-project
226
+ ```
227
+
198
228
  ### Deleting Certificates
199
229
 
200
230
  To remove a certificate:
201
231
 
202
232
  ```bash
203
233
  zuplo mtls-certificate delete \
204
- --cert-id my-cert-id \
234
+ --cert-id cert_abc123 \
205
235
  --account your-account \
206
236
  --project your-project
207
237
  ```
@@ -238,19 +268,112 @@ you rotate the certificate.
238
268
 
239
269
  ## Local Development
240
270
 
241
- :::warning
271
+ `zuplo dev` can present client certificates to your backend, so the same
272
+ `mtlsCertificate` code path you deploy also works on your machine. Support
273
+ depends on your project type, set by
274
+ [`projectType`](../programmable-api/zuplo-json.mdx#projecttype) in
275
+ `zuplo.jsonc`:
276
+
277
+ | `projectType` | Local mTLS support |
278
+ | ---------------------------------- | -------------------------------------------------------- |
279
+ | `managed-dedicated`, `self-hosted` | Supported — certificates load from your local filesystem |
280
+ | `managed-edge` | Not supported |
281
+
282
+ If `zuplo.jsonc` has no `projectType` field, run [`zuplo link`](../cli/link.mdx)
283
+ — it looks up the linked project and writes the matching type into the file.
284
+ Without that field, local development falls back to `managed-edge` and skips
285
+ mTLS, even when the project deploys to a dedicated environment.
286
+
287
+ On `managed-edge` projects, mTLS only works in deployed environments. Test mTLS
288
+ routes in a preview environment, point local runs at a backend endpoint that
289
+ doesn't require client certificates, or branch on
290
+ [`environment`](./environment-variables.mdx) to skip mTLS locally.
291
+
292
+ Certificates never leave your machine — the CLI reads them from disk and passes
293
+ them to the local runtime. It doesn't upload them to Zuplo or read the
294
+ certificates you uploaded with `zuplo mtls-certificate create`.
295
+
296
+ ### Add certificates to your project
297
+
298
+ Place each certificate in its own subdirectory of `.zuplo-local/mtls`, named
299
+ after the certificate. The layout matches the one used for deployments:
300
+
301
+ ```text
302
+ my-project/
303
+ ├── .zuplo-local/
304
+ │ └── mtls/
305
+ │ └── my_backend_cert/
306
+ │ ├── tls.crt
307
+ │ └── tls.key
308
+ ├── config/
309
+ ├── modules/
310
+ └── zuplo.jsonc
311
+ ```
312
+
313
+ The subdirectory name is the name your code references, so use the same name as
314
+ the certificate you uploaded to Zuplo. Then
315
+ `fetch(url, { zuplo: { mtlsCertificate: "my_backend_cert" } })` resolves locally
316
+ and in deployed environments without a code change.
317
+
318
+ Both files must be PEM-encoded, and `tls.key` must be unencrypted — the same
319
+ requirements as an uploaded certificate.
242
320
 
243
- mTLS bindings aren't currently available in local development environments. Your
244
- code using mTLS will only work when deployed to Zuplo's edge infrastructure.
321
+ :::caution{title="Keep private keys out of Git"}
322
+
323
+ `.zuplo-local/` holds unencrypted private keys. Projects created with
324
+ `create-zuplo-api` gitignore it already. For older projects, add `.zuplo-local/`
325
+ to your `.gitignore` — `zuplo dev` prints a warning at startup when it isn't
326
+ covered.
245
327
 
246
328
  :::
247
329
 
248
- For local development, consider:
330
+ Start the server as usual:
249
331
 
250
- - Using conditional logic to bypass mTLS when running locally
251
- - Setting up a separate backend endpoint that doesn't require mTLS for
252
- development
253
- - Testing mTLS functionality in a preview environment
332
+ ```bash
333
+ npx zuplo dev
334
+ ```
335
+
336
+ At startup the CLI reports what it loaded:
337
+
338
+ ```text
339
+ Loaded 1 local mTLS client certificate(s) from /path/to/my-project/.zuplo-local/mtls: my_backend_cert. Reference by name via the mtlsCertificate option.
340
+ ```
341
+
342
+ ### Load certificates from another directory
343
+
344
+ To keep certificates outside the project — a shared team directory, or a path
345
+ your secret manager writes to — pass `--mtls-certs`:
346
+
347
+ ```bash
348
+ npx zuplo dev --mtls-certs ~/certs/zuplo-uat
349
+ ```
350
+
351
+ The flag expects the same `<dir>/<cert-name>/tls.crt` layout and wins over
352
+ `.zuplo-local/mtls`. Unlike auto-detection, it fails fast: if the directory
353
+ doesn't exist or holds no valid certificate, the server doesn't start.
354
+ `--mtls-certificates-dir` is an alias for the same flag.
355
+
356
+ The CLI only looks in these two places, in order:
357
+
358
+ 1. The directory passed to `--mtls-certs`
359
+ 2. `<project>/.zuplo-local/mtls`
360
+
361
+ There is no fallback to a directory in your home folder, because client
362
+ certificates are scoped to a specific gateway.
363
+
364
+ ### Choose the right certificate for local runs
365
+
366
+ The certificate you load locally is presented to whatever backend your route
367
+ calls. Use a certificate issued for a development or staging backend, and keep
368
+ production certificates off developer machines.
369
+
370
+ :::danger
371
+
372
+ Loading a production client certificate locally lets any code on your machine
373
+ authenticate to your production backend as the gateway. Treat these files like
374
+ any other production credential.
375
+
376
+ :::
254
377
 
255
378
  ## Troubleshooting
256
379
 
@@ -333,6 +456,31 @@ If requests fail to connect:
333
456
  preview, production)
334
457
  - Check that your backend's CA certificate is properly configured
335
458
 
459
+ ### Local Certificates Fail to Load
460
+
461
+ If `zuplo dev` starts without reporting a loaded certificate, check the startup
462
+ output for one of these warnings:
463
+
464
+ - **"Local mTLS client certificates are only supported for managed-dedicated
465
+ projects"** — the project resolves to `managed-edge`. If `zuplo.jsonc` has no
466
+ `projectType` field, run [`zuplo link`](../cli/link.mdx) to add it and start
467
+ the server again. If the field says `managed-edge`, local mTLS isn't
468
+ available; see [Local Development](#local-development).
469
+ - **"Ignoring mTLS certificate directory … because it is missing `tls.key`"** —
470
+ a subdirectory has only one of the two files. Add the missing file — each
471
+ certificate directory needs both `tls.crt` and `tls.key`.
472
+ - **"Ignoring `--mtls-certs`: … are set in `.env` or `.env.zuplo`"** — a
473
+ previous manual setup left `__ZUPLO_MTLS_CLIENT_CERT_NAMES` or
474
+ `__ZUPLO_MTLS_CLIENT_CERT_BASE_DIR` in an env file. Those values take
475
+ precedence in the local runtime. Remove them from `.env` and `.env.zuplo` to
476
+ use `--mtls-certs` or `.zuplo-local/mtls`.
477
+ - **"Found local mTLS certificates directory … but it does not contain any valid
478
+ certificate entries"** — the certificates are loose files rather than in a
479
+ `<cert-name>/` subdirectory.
480
+
481
+ If no warning appears at all, confirm the directory is `.zuplo-local/mtls` (not
482
+ `.zuplo/mtls`) at the project root, next to `zuplo.jsonc`.
483
+
336
484
  ### Runtime Errors
337
485
 
338
486
  If you see errors about missing certificates:
package/docs/cli/dev.mdx CHANGED
@@ -116,6 +116,44 @@ sidebar_label: dev
116
116
  usage="$0 dev [options]"
117
117
  >
118
118
 
119
+ ## Local mTLS client certificates
120
+
121
+ On `managed-dedicated` and `self-hosted` projects, `zuplo dev` presents client
122
+ certificates to your backend, so routes that set `mtlsCertificate` work locally
123
+ without a code change.
124
+
125
+ Put each certificate in its own subdirectory of `.zuplo-local/mtls`, named after
126
+ the certificate:
127
+
128
+ ```text
129
+ .zuplo-local/mtls/my_backend_cert/tls.crt
130
+ .zuplo-local/mtls/my_backend_cert/tls.key
131
+ ```
132
+
133
+ `zuplo dev` finds that directory automatically and logs the certificates it
134
+ loaded. To read certificates from somewhere else, pass `--mtls-certs`:
135
+
136
+ ```bash
137
+ npx zuplo dev --mtls-certs ~/certs/zuplo-uat
138
+ ```
139
+
140
+ The flag takes precedence over `.zuplo-local/mtls` and fails fast when the
141
+ directory is missing or holds no valid certificate.
142
+
143
+ `.zuplo-local/` contains unencrypted private keys. Projects created with
144
+ `create-zuplo-api` gitignore it already; for older projects, add
145
+ `.zuplo-local/` to your `.gitignore`. `zuplo dev` warns at startup when it
146
+ isn't covered.
147
+
148
+ The project type comes from `projectType` in `zuplo.jsonc`. If that field is
149
+ missing, run [`zuplo link`](./link.mdx) to have the CLI detect and write it —
150
+ otherwise local development falls back to `managed-edge` and skips mTLS.
151
+
152
+ On `managed-edge` projects, local mTLS isn't available and `--mtls-certs` is
153
+ ignored with a warning. For the full workflow, including certificate
154
+ requirements and troubleshooting, see
155
+ [Gateway to Origin mTLS Authentication](../articles/securing-backend-mtls.mdx#local-development).
156
+
119
157
  </CliCommand>
120
158
 
121
159
  ## Global options
@@ -124,3 +162,9 @@ The following global options are available for all commands:
124
162
 
125
163
  - [`--help`](./global-options.mdx#help)
126
164
  - [`--api-key`](./global-options.mdx#api-key)
165
+
166
+ ## Additional resources
167
+
168
+ - [Local Development Troubleshooting](../articles/local-development-troubleshooting.mdx)
169
+ - [Testing your API](../articles/testing.mdx)
170
+ - [Gateway to Origin mTLS Authentication](../articles/securing-backend-mtls.mdx)
@@ -0,0 +1,47 @@
1
+ ---
2
+ additional-resources:
3
+ - name: Local Development Troubleshooting
4
+ href: ../articles/local-development-troubleshooting.mdx
5
+ - name: Testing your API
6
+ href: ../articles/testing.mdx
7
+ - name: Gateway to Origin mTLS Authentication
8
+ href: ../articles/securing-backend-mtls.mdx
9
+ ---
10
+
11
+ ## Local mTLS client certificates
12
+
13
+ On `managed-dedicated` and `self-hosted` projects, `zuplo dev` presents client
14
+ certificates to your backend, so routes that set `mtlsCertificate` work locally
15
+ without a code change.
16
+
17
+ Put each certificate in its own subdirectory of `.zuplo-local/mtls`, named after
18
+ the certificate:
19
+
20
+ ```text
21
+ .zuplo-local/mtls/my_backend_cert/tls.crt
22
+ .zuplo-local/mtls/my_backend_cert/tls.key
23
+ ```
24
+
25
+ `zuplo dev` finds that directory automatically and logs the certificates it
26
+ loaded. To read certificates from somewhere else, pass `--mtls-certs`:
27
+
28
+ ```bash
29
+ npx zuplo dev --mtls-certs ~/certs/zuplo-uat
30
+ ```
31
+
32
+ The flag takes precedence over `.zuplo-local/mtls` and fails fast when the
33
+ directory is missing or holds no valid certificate.
34
+
35
+ `.zuplo-local/` contains unencrypted private keys. Projects created with
36
+ `create-zuplo-api` gitignore it already; for older projects, add
37
+ `.zuplo-local/` to your `.gitignore`. `zuplo dev` warns at startup when it
38
+ isn't covered.
39
+
40
+ The project type comes from `projectType` in `zuplo.jsonc`. If that field is
41
+ missing, run [`zuplo link`](./link.mdx) to have the CLI detect and write it —
42
+ otherwise local development falls back to `managed-edge` and skips mTLS.
43
+
44
+ On `managed-edge` projects, local mTLS isn't available and `--mtls-certs` is
45
+ ignored with a warning. For the full workflow, including certificate
46
+ requirements and troubleshooting, see
47
+ [Gateway to Origin mTLS Authentication](../articles/securing-backend-mtls.mdx#local-development).
@@ -72,9 +72,9 @@ to integrate Azure AD with your Dev Portal documentation site.
72
72
  export default {
73
73
  // ... other configuration
74
74
  authentication: {
75
- type: "openid",
75
+ type: "entra",
76
76
  clientId: "<your-application-client-id>",
77
- issuer: "https://login.microsoftonline.com/<your-tenant-id>/v2.0",
77
+ tenantId: "<your-tenant-id>",
78
78
  scopes: ["openid", "profile", "email"], // Optional: customize scopes
79
79
  },
80
80
  // ... other configuration
@@ -91,20 +91,21 @@ For single tenant (organization-only access):
91
91
 
92
92
  ```typescript
93
93
  authentication: {
94
- type: "openid",
94
+ type: "entra",
95
95
  clientId: "<your-application-client-id>",
96
- issuer: "https://login.microsoftonline.com/<your-tenant-id>/v2.0",
96
+ tenantId: "<your-tenant-id>",
97
97
  scopes: ["openid", "profile", "email"],
98
98
  }
99
99
  ```
100
100
 
101
- For multitenant (any Azure AD organization):
101
+ For multitenant (any Azure AD organization), use `tenantId: "common"` or `"organizations"` — this is
102
+ also the default if `tenantId` is omitted:
102
103
 
103
104
  ```typescript
104
105
  authentication: {
105
- type: "openid",
106
+ type: "entra",
106
107
  clientId: "<your-application-client-id>",
107
- issuer: "https://login.microsoftonline.com/common/v2.0",
108
+ tenantId: "common",
108
109
  scopes: ["openid", "profile", "email"],
109
110
  }
110
111
  ```
@@ -115,9 +116,9 @@ Request additional Microsoft Graph API scopes:
115
116
 
116
117
  ```typescript
117
118
  authentication: {
118
- type: "openid",
119
+ type: "entra",
119
120
  clientId: "<your-application-client-id>",
120
- issuer: "https://login.microsoftonline.com/<your-tenant-id>/v2.0",
121
+ tenantId: "<your-tenant-id>",
121
122
  scopes: [
122
123
  "openid",
123
124
  "profile",
@@ -136,7 +137,7 @@ Protect specific documentation routes using the `protectedRoutes` configuration:
136
137
  {
137
138
  // ... other configuration
138
139
  authentication: {
139
- type: "openid",
140
+ type: "entra",
140
141
  // ... Azure AD config
141
142
  },
142
143
  protectedRoutes: [
@@ -217,16 +218,17 @@ Azure AD provides rich user profile data through OpenID Connect:
217
218
  2. **Redirect URI Mismatch**: The redirect URI must exactly match one configured in Azure AD,
218
219
  including protocol and path.
219
220
 
220
- 3. **Tenant Access Issues**: For single-tenant apps, ensure users are from the correct tenant. For
221
- multi-tenant, verify the issuer URL uses "common" or "organizations".
221
+ 3. **Tenant Access Issues**: For single-tenant apps, ensure users are from the correct tenant and
222
+ `tenantId` is set to that tenant's GUID. For multi-tenant, use `tenantId: "common"` or
223
+ `"organizations"`.
222
224
 
223
225
  4. **Missing User Information**: Check that required API permissions are granted and admin consent
224
226
  is provided if needed.
225
227
 
226
- 5. **Token Validation Errors**: Ensure your issuer URL is correct and includes the `/v2.0` endpoint
227
- for the Microsoft identity platform.
228
+ 5. **Token Validation Errors**: Ensure your `tenantId` is correct. If you use a CIAM tenant
229
+ (`*.ciamlogin.com`), set the `issuer` option to override the default issuer instead.
228
230
 
229
- 6. **Authentication Not Working**: Verify your issuer URL and client ID are correct, and that your
231
+ 6. **Authentication Not Working**: Verify your `tenantId` and client ID are correct, and that your
230
232
  app registration is configured as a Single-page application (SPA) with the correct redirect URIs.
231
233
 
232
234
  ## Security Best Practices
@@ -15,8 +15,8 @@ authentication provider you use.
15
15
 
16
16
  ## Authentication Providers
17
17
 
18
- Dev Portal supports Clerk, Auth0, Supabase, Firebase, Azure B2C, and any OpenID Connect provider
19
- (including Okta, Keycloak, Authentik, and PingFederate).
18
+ Dev Portal supports Clerk, Auth0, Supabase, Firebase, Microsoft Entra ID, Azure B2C, and any OpenID
19
+ Connect provider (including Okta, Keycloak, Authentik, and PingFederate).
20
20
 
21
21
  Not seeing your authentication provider? [Let us know](https://github.com/zuplo/zudoku/issues)
22
22
 
@@ -99,6 +99,26 @@ providing your own array of scopes.
99
99
  For provider-specific guides (Okta, Keycloak, etc.), see the
100
100
  [OpenID Connect setup page](./authentication-openid.md).
101
101
 
102
+ ### Microsoft Entra ID
103
+
104
+ For Microsoft Entra ID (formerly Azure AD), you will need the `clientId` from your app registration
105
+ and your `tenantId`.
106
+
107
+ ```typescript
108
+ {
109
+ // ...
110
+ authentication: {
111
+ type: "entra",
112
+ clientId: "<your-application-client-id>",
113
+ tenantId: "<your-tenant-id>", // Or "common" for multitenant. Defaults to "common".
114
+ },
115
+ // ...
116
+ }
117
+ ```
118
+
119
+ For full setup instructions, see the
120
+ [Azure AD / Entra ID setup guide](./authentication-azure-ad.md).
121
+
102
122
  ### Firebase
103
123
 
104
124
  For Firebase authentication, you will need your Firebase project configuration. You can find this in
@@ -83,7 +83,7 @@
83
83
  | 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 |
84
84
  | require-origin-inbound | Require Origin | Sets an allow-list for an origin header | api-gateway |
85
85
  | 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 |
86
- | secret-masking-outbound | Secret Masking | Masks common secrets like Zuplo API keys, GitHub tokens, or SSH private key in the response body. | api-gateway |
86
+ | secret-masking-outbound | Secret Masking (deprecated) | Masks common secrets like Zuplo API keys, GitHub tokens, or SSH private key in the response body. | api-gateway |
87
87
  | 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 |
88
88
  | 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 |
89
89
  | set-headers-outbound | Set Headers | Adds or sets headers on the on the outgoing response. | api-gateway |
@@ -39,3 +39,56 @@ Apply this policy to outbound requests in your route configuration:
39
39
  - Zuplo API keys (i.e. `zpka_xxx`)
40
40
  - GitHub Tokens and Personal Access Tokens (i.e. `ghp_xxx`)
41
41
  - Private key blocks (i.e. `BEGIN PRIVATE KEY` and `END PRIVATE KEY`)
42
+
43
+ ## Migrating to Data Loss Prevention
44
+
45
+ The [Data Loss Prevention](/docs/policies/data-loss-prevention-outbound) policy
46
+ supersedes this one. Its `secret-zuplo`, `secret-github`, and
47
+ `secret-private-key` recognizers use the same patterns this policy does, so
48
+ selecting them reproduces the current behavior exactly:
49
+
50
+ ```json
51
+ {
52
+ "policies": [
53
+ {
54
+ "name": "dlp-outbound",
55
+ "policyType": "data-loss-prevention-outbound",
56
+ "handler": {
57
+ "export": "DataLossPreventionOutboundPolicy",
58
+ "module": "$import(@zuplo/runtime)",
59
+ "options": {
60
+ "action": "mask",
61
+ "entities": ["secret-zuplo", "secret-github", "secret-private-key"],
62
+ "mask": "<SECRET MASKED>",
63
+ "customPatterns": [
64
+ { "name": "key-value-pair", "pattern": "\\b(\\w+)=\\w+\\b" }
65
+ ]
66
+ }
67
+ }
68
+ }
69
+ ]
70
+ }
71
+ ```
72
+
73
+ Option mapping:
74
+
75
+ - `mask` becomes `mask`, which only applies when `action` is `mask`.
76
+ - `additionalPatterns` becomes `customPatterns`, where each entry takes a `name`
77
+ and a `pattern` plus optional `confidence` and `context` words.
78
+
79
+ Two behavior differences to be aware of when you migrate:
80
+
81
+ - Drop `entities` entirely to enable the whole built-in catalog — 60+
82
+ recognizers covering PII, payment and bank identifiers, national IDs, and API
83
+ keys for dozens of vendors — rather than only the three secret types this
84
+ policy handles.
85
+ - Data Loss Prevention only inspects text content types (JSON, XML,
86
+ form-encoded, `text/*`) and passes binary bodies through untouched, whereas
87
+ this policy reads every response body as text. Set `contentTypes` to override
88
+ the allow-list.
89
+
90
+ Use `action` to do more than mask: `block` replaces the response with a `422`
91
+ that lists only the detected entity names, and `log` records a warning and
92
+ returns the response unchanged. Pair it with
93
+ [Data Loss Prevention - Inbound](/docs/policies/data-loss-prevention-inbound) to
94
+ scan incoming requests as well.
@@ -1,3 +1,10 @@
1
+ > **Deprecated:** Use the
2
+ > [Data Loss Prevention](/docs/policies/data-loss-prevention-outbound) policy
3
+ > instead. It detects everything this policy does plus 60+ more entity types,
4
+ > and can block or log a response rather than only masking it. This policy will
5
+ > continue to work but will be removed in a future version of Zuplo. See
6
+ > [Migrating to Data Loss Prevention](#migrating-to-data-loss-prevention) below.
7
+
1
8
  The Secret Masking policy searches for and masks common secrets and replaces
2
9
  them with a placeholder. Secrets that are automatically masked include:
3
10
 
@@ -3,7 +3,7 @@
3
3
  "$id": "https://cdn.zuplo.com/policies/runtime/schemas/secret-masking-outbound.json",
4
4
  "type": "object",
5
5
  "title": "Secret Masking",
6
- "isDeprecated": false,
6
+ "isDeprecated": true,
7
7
  "isPaidAddOn": false,
8
8
  "isEnterprise": false,
9
9
  "isInternal": false,
@@ -12,7 +12,7 @@
12
12
  "requiresAI": false,
13
13
  "products": ["api-gateway"],
14
14
  "description": "Masks common secrets like Zuplo API keys, GitHub tokens, or SSH private key in the response body.",
15
- "deprecatedMessage": "",
15
+ "deprecatedMessage": "Use the [Data Loss Prevention](https://zuplo.com/docs/policies/data-loss-prevention-outbound) policy instead. It detects the same Zuplo API keys, GitHub tokens, and private key blocks via its `secret-zuplo`, `secret-github`, and `secret-private-key` recognizers, plus 60+ more built-in entities, and can block or log a response rather than only masking it. This policy will continue to work but will be removed in a future version of Zuplo.",
16
16
  "required": ["handler"],
17
17
  "properties": {
18
18
  "handler": {
@@ -31,19 +31,20 @@
31
31
  "options": {
32
32
  "title": "SecretMaskingOutboundPolicyOptions",
33
33
  "type": "object",
34
- "description": "The options for the secret masking policy.",
34
+ "description": "The options for the secret masking policy. Deprecated - use the Data Loss Prevention outbound policy instead.",
35
35
  "additionalProperties": false,
36
36
  "required": [],
37
+ "deprecated": true,
37
38
  "properties": {
38
39
  "mask": {
39
40
  "type": "string",
40
- "description": "The string to replace detected secrets with.",
41
+ "description": "The string to replace detected secrets with. Equivalent to the Data Loss Prevention policy's `mask` option.",
41
42
  "examples": ["[REDACTED]"],
42
43
  "default": "[REDACTED]"
43
44
  },
44
45
  "additionalPatterns": {
45
46
  "type": "array",
46
- "description": "Extra regex patterns for secrets to mask.",
47
+ "description": "Extra regex patterns for secrets to mask. Replaced by the Data Loss Prevention policy's `customPatterns` option, which also accepts a name, confidence, and context words per pattern.",
47
48
  "items": {
48
49
  "type": "string"
49
50
  }
@@ -64,6 +64,14 @@ deployed. This configuration defaults to `managed-edge`.
64
64
  }
65
65
  ```
66
66
 
67
+ When the field is absent, [`zuplo link`](../cli/link.mdx) looks up the linked
68
+ project and writes the matching `projectType` into your `zuplo.jsonc`, so the
69
+ local runtime matches the deployed one. The CLI only adds the field — it never
70
+ changes a `projectType` you already set, and it leaves your comments and
71
+ formatting intact. If it can't confirm the type (for example, when it can't
72
+ reach the Zuplo API), it leaves the file alone and local development falls back
73
+ to `managed-edge`.
74
+
67
75
  ## `allowDuplicateRoutes`
68
76
 
69
77
  Allow duplicate routes (same method and path) to be defined. This isn't
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuplo",
3
- "version": "6.73.18",
3
+ "version": "6.73.20",
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.73.18",
23
- "@zuplo/core": "6.73.18",
24
- "@zuplo/runtime": "6.73.18",
22
+ "@zuplo/cli": "6.73.20",
23
+ "@zuplo/core": "6.73.20",
24
+ "@zuplo/runtime": "6.73.20",
25
25
  "@zuplo/test": "1.4.0"
26
26
  }
27
27
  }