zuplo 7.3.2 → 7.4.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.
Files changed (71) hide show
  1. package/docs/ai-gateway/apps.mdx +2 -2
  2. package/docs/ai-gateway/custom-policies.mdx +3 -3
  3. package/docs/ai-gateway/fallback.mdx +13 -12
  4. package/docs/ai-gateway/getting-started.mdx +10 -9
  5. package/docs/ai-gateway/introduction.mdx +19 -18
  6. package/docs/ai-gateway/managing-apps.mdx +9 -7
  7. package/docs/ai-gateway/policy-chains.mdx +13 -7
  8. package/docs/ai-gateway/policy-templates.mdx +4 -3
  9. package/docs/ai-gateway/usage-limits.mdx +8 -10
  10. package/docs/api-management/introduction.md +1 -1
  11. package/docs/articles/api-key-api.mdx +3 -3
  12. package/docs/articles/custom-ci-cd-azure.mdx +1 -1
  13. package/docs/articles/custom-ci-cd-bitbucket.mdx +2 -2
  14. package/docs/articles/custom-code-patterns.md +1 -1
  15. package/docs/articles/custom-domains.mdx +2 -2
  16. package/docs/articles/development-options.mdx +4 -4
  17. package/docs/articles/fastly-zuplo-host-setup.mdx +5 -5
  18. package/docs/articles/feature-flags.mdx +442 -0
  19. package/docs/articles/gke-with-upstream-auth-policy.mdx +7 -7
  20. package/docs/articles/health-checks.mdx +4 -4
  21. package/docs/articles/hosting-options.mdx +2 -2
  22. package/docs/articles/local-development-env-variables.mdx +1 -1
  23. package/docs/articles/metrics-plugins.mdx +1 -1
  24. package/docs/articles/migration-overview.md +1 -1
  25. package/docs/articles/monorepo-deployment.mdx +10 -10
  26. package/docs/articles/performance-testing.mdx +10 -9
  27. package/docs/articles/plugin-azure-blob.mdx +1 -1
  28. package/docs/articles/plugin-azure-event-hubs.mdx +2 -2
  29. package/docs/articles/policies.mdx +2 -2
  30. package/docs/articles/securing-backend-mtls.mdx +3 -3
  31. package/docs/articles/securing-the-gateway-with-client-mtls.mdx +3 -3
  32. package/docs/articles/securing-your-backend.mdx +1 -1
  33. package/docs/articles/security.mdx +1 -1
  34. package/docs/articles/terraform.mdx +2 -2
  35. package/docs/articles/testing.mdx +6 -6
  36. package/docs/articles/troubleshooting-slow-responses.mdx +5 -6
  37. package/docs/articles/troubleshooting.md +1 -1
  38. package/docs/articles/use-openapi-extension-data.mdx +4 -4
  39. package/docs/articles/waf-ddos-akamai.md +13 -13
  40. package/docs/articles/waf-ddos-aws-waf-shield.mdx +14 -14
  41. package/docs/articles/waf-ddos-fastly.mdx +14 -14
  42. package/docs/articles/waf-ddos.mdx +7 -7
  43. package/docs/cli/authentication.mdx +1 -1
  44. package/docs/cli/lint.mdx +103 -0
  45. package/docs/concepts/how-zuplo-works.mdx +5 -4
  46. package/docs/concepts/upstream-credentials.mdx +1 -1
  47. package/docs/dedicated/akamai/architecture.mdx +23 -24
  48. package/docs/dedicated/akamai/cdn.mdx +20 -20
  49. package/docs/dedicated/architecture.mdx +28 -27
  50. package/docs/dedicated/custom-domains.mdx +3 -4
  51. package/docs/dedicated/federated-gateways.mdx +1 -1
  52. package/docs/dedicated/networking.mdx +10 -10
  53. package/docs/dedicated/overview.mdx +3 -3
  54. package/docs/dedicated/source-control.mdx +6 -5
  55. package/docs/handlers/custom-handler.mdx +4 -4
  56. package/docs/handlers/mcp-server.mdx +1 -1
  57. package/docs/handlers/url-forward.mdx +1 -2
  58. package/docs/mcp-server/introduction.mdx +4 -4
  59. package/docs/policies/_index.md +2 -2
  60. package/docs/policies/ai-gateway-configuration-executor-v2-inbound/schema.json +1 -1
  61. package/docs/policies/ai-gateway-dlp-inbound/doc.md +79 -45
  62. package/docs/policies/ai-gateway-dlp-inbound/schema.json +5679 -3359
  63. package/docs/policies/ai-gateway-metering-v2-inbound/doc.md +13 -13
  64. package/docs/policies/ai-gateway-metering-v2-inbound/schema.json +1 -1
  65. package/docs/programmable-api/environment.mdx +1 -1
  66. package/docs/programmable-api/jwt-service-plugin.mdx +1 -1
  67. package/docs/programmable-api/logger.mdx +1 -1
  68. package/docs/programmable-api/runtime-errors.mdx +1 -1
  69. package/docs/programmable-api/zuplo-context.mdx +3 -3
  70. package/docs/self-hosted/overview.md +3 -3
  71. package/package.json +5 -5
@@ -8,42 +8,52 @@ Detection runs entirely inside your gateway using the built-in DLP engine (62
8
8
  data types backed by regexes, checksums such as Luhn and IBAN mod-97, and
9
9
  context-word scoring). Nothing is sent to a third-party scanning service.
10
10
 
11
- ## Rules
11
+ ## Rules: inbound and outbound
12
12
 
13
- `rules` is a map. Each key names a built-in data type id (like `id-us-ssn`) or a
14
- group selector (like `pii`, `secret`, or any dash-aligned prefix such as
15
- `secret-aws`); each value is a rule object stating what happens when that type
16
- is found:
13
+ `rules` has exactly two sections `inbound` for what callers send the model
14
+ (system prompt, message text, and tool-call arguments), `outbound` for what the
15
+ model sends back (buffered responses whole, streaming responses as they stream).
16
+ Each direction you include is scanned; each you omit is left untouched. There is
17
+ no "both" shorthand and no mirroring — wanting the same behavior on both sides
18
+ means stating it in both sections.
19
+
20
+ Each section is a map. Keys name a built-in data type id (like `id-us-ssn`), a
21
+ group selector (`pii`, `secret`, or any dash-aligned prefix such as
22
+ `secret-aws`), or one of your custom detectors by name; each value is a rule
23
+ object whose required `action` says what happens on a match:
17
24
 
18
25
  - `mask` — replace the matched text with a placeholder; the conversation
19
26
  continues.
20
27
  - `block` — reject the request (or terminate the response) with an error naming
21
28
  the detected types, never the values.
22
29
  - `log` — record a finding and let the traffic pass; use it to trial a rule.
23
- - `off` — exclude this type from a group enabled by a broader key.
24
-
25
- A rule states its action in one of two ways, never both: a single `action`
26
- applying in both directions, or the directional pair `inboundAction` /
27
- `outboundAction` — `inboundAction` governs what callers send the model,
28
- `outboundAction` governs what the model sends back (buffered responses whole,
29
- streaming responses as they stream). Either directional field alone limits the
30
- rule to that direction.
30
+ - `off` — exclude this type from a group enabled by a broader key in the same
31
+ section.
31
32
 
32
- Types you do not mention are not scanned. The most specific key wins (exact id
33
- beats a prefix beats a group) and the winning entry decides everything for its
34
- type, direction included. When one stretch of text matches multiple rules, the
35
- severest action wins (block > mask > log).
33
+ Because a rule lives inside a direction, everything about it is per-direction
34
+ its action, its detection level, its mask text, its partial masking:
36
35
 
37
36
  ```json
38
37
  "rules": {
39
- "pii": { "inboundAction": "mask", "outboundAction": "block" },
40
- "contact-phone": { "action": "off" },
41
- "secret": { "action": "block" },
42
- "finance-credit-card": { "action": "mask", "partialMask": { "keepLast": 4 } },
43
- "finance-cvv": { "action": "block", "detection": "corroborated" }
38
+ "inbound": {
39
+ "pii": { "action": "mask" },
40
+ "contact-phone": { "action": "off" }
41
+ },
42
+ "outbound": {
43
+ "pii": { "action": "block" },
44
+ "finance-credit-card": { "action": "mask", "partialMask": { "keepLast": 4 } },
45
+ "finance-cvv": { "action": "block", "detection": "corroborated" }
46
+ }
44
47
  }
45
48
  ```
46
49
 
50
+ Within one section: types you do not mention are not scanned; the most specific
51
+ key wins (exact id beats a prefix beats a group) and the winning entry decides
52
+ everything for its type in that direction. When one stretch of text matches
53
+ multiple rules, the severest action wins (block > mask > log). The two sections
54
+ never influence each other, and at least one section must enable at least one
55
+ rule.
56
+
47
57
  ### Detection levels
48
58
 
49
59
  Each type combines its pattern with evidence — a checksum where the format has
@@ -74,29 +84,45 @@ chunks.
74
84
 
75
85
  ## Custom rules
76
86
 
77
- `customRules` adds your own detections: the same rule object plus a `name`, a
78
- regex `pattern`, and optional `context` words (`detection: "corroborated"` then
79
- means "only near one of those words"). Names must be distinct and must not reuse
80
- a built-in type id or group selector built-in types are configured through
81
- `rules`. An invalid pattern or a colliding name fails the configuration at
82
- request time a broken guardrail never degrades into a silent pass-through.
87
+ `customRules` declares your own detectors a `name`, a regex `pattern`, and
88
+ optional `context` words. A detector does nothing by itself: activate it by name
89
+ inside `rules.inbound` or `rules.outbound`, exactly like a built-in type, with
90
+ its own action, detection level, and mask per direction
91
+ (`detection: "corroborated"` then means "only near one of its context words").
92
+ Names must be distinct and must not reuse a built-in type id or group selector.
93
+ A rules key that matches neither a built-in nor a declared detector, an invalid
94
+ pattern, or a colliding name fails the configuration at request time — a broken
95
+ guardrail never degrades into a silent pass-through.
83
96
 
84
97
  ## Streaming responses
85
98
 
86
- Streaming responses are scanned as they stream. Providers emit deltas of a few
87
- characters, so each channel's text is first coalesced into scan pieces of at
88
- least 64 characters (clients concatenate deltas, so only the chunking shifts
89
- never the text). The scanner carries a bounded tail of already-scanned text
90
- (`streaming.maxCarryChars`, default 512) into each new piece's scan, and each
91
- piece is released once the scan including its successor passes so a value
92
- split across pieces is fully maskable before anything is delivered, at the cost
93
- of roughly one coalesced piece (~64 characters) of latency. When a `block` rule
94
- fires mid-stream, the stream terminates with the endpoint's native
95
- content-filter events (OpenAI clients see a `content_filter` error chunk and
96
- `[DONE]`; Anthropic clients see a well-formed `message_delta` with
97
- `stop_reason: "content_filter"` and `message_stop`). Setting
98
- `streaming.boundaryScan: false` scans each chunk alone and releases it
99
- immediately lower cost, but values split across chunks are missed.
99
+ How streaming (SSE) responses are inspected is set by `streaming.mode`:
100
+
101
+ - **`stream` (default)** scan deltas as they flow. Providers emit deltas of a
102
+ few characters, so each channel's text is first coalesced into scan pieces of
103
+ at least 64 characters (clients concatenate deltas, so only the chunking
104
+ shifts never the text). The scanner carries a bounded tail of
105
+ already-scanned text (`streaming.maxCarryChars`, default 512) into each new
106
+ piece's scan, and each piece is released once the scan including its successor
107
+ passes so a value split across pieces is fully maskable before anything is
108
+ delivered, at the cost of roughly one coalesced piece (~64 characters) of
109
+ latency. When a `block` rule fires mid-stream, the stream terminates with the
110
+ endpoint's native content-filter events (OpenAI clients see a `content_filter`
111
+ error chunk and `[DONE]`; Anthropic clients see a well-formed `message_delta`
112
+ with `stop_reason: "content_filter"` and `message_stop`). Setting
113
+ `streaming.boundaryScan: false` scans each chunk alone and releases it
114
+ immediately — lower cost, but values split across chunks are missed.
115
+ - **`buffer`** — collect the entire response first, then scan it exactly like a
116
+ non-streaming response: one pass over each channel's complete text. Detection
117
+ is exact for values of ANY length (no carry window, no chunk boundaries — the
118
+ streaming limits below do not apply), and because nothing has been delivered
119
+ when the verdict lands, a `block` is a clean native 400 error naming the
120
+ detected types instead of a mid-stream termination. The trade is delivery: the
121
+ client receives nothing until the model finishes generating, and the gateway
122
+ holds the whole response in memory. The SSE framing is preserved — clients
123
+ still receive the same events, all at once.
124
+ - **`off`** — streaming responses pass through unscanned, even when rules cover
125
+ the outbound direction. Buffered (non-streaming) responses are still scanned.
100
126
 
101
127
  ## Failure posture
102
128
 
@@ -111,6 +137,10 @@ AI Gateway policies on compatibility dates of 2026-03-01 or later.
111
137
 
112
138
  ## Known limits
113
139
 
140
+ The first four limits apply to `streaming.mode: "stream"` only — `buffer` mode
141
+ scans each channel's complete text in one pass, so chunking cannot hide a value
142
+ from it:
143
+
114
144
  - Masking is complete for streamed values up to two coalesced scan pieces (~128
115
145
  characters) regardless of how finely the provider chunks its deltas, for types
116
146
  whose shape alone is decisive (dashed SSNs, cards, prefixed secrets). A longer
@@ -119,9 +149,13 @@ AI Gateway policies on compatibility dates of 2026-03-01 or later.
119
149
  - A context-gated match (a weak form, or a `corroborated` rule) whose context
120
150
  word arrives only in a LATER piece can leak the characters delivered before
121
151
  the context made it actionable; detection and blocking still fire.
152
+ - Multi-line values such as PEM private key blocks are not detected across
153
+ streaming chunk boundaries.
122
154
  - Streamed `logprobs` are removed from scanned chat streams: masking rewrites
123
- the text they describe, and stale logprobs are worse than absent ones.
155
+ the text they describe, and stale logprobs are worse than absent ones. (This
156
+ also applies to `buffer` mode, which rewrites the same events.)
157
+
158
+ And in every mode:
159
+
124
160
  - Context words must appear in the conversation text itself; JSON field names in
125
161
  your application's data are not visible to the scanner.
126
- - Multi-line values such as PEM private key blocks are not detected across
127
- streaming chunk boundaries.