@adtl/gateway 0.6.0 → 0.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.
Files changed (60) hide show
  1. package/README.md +146 -11
  2. package/dist/audit.d.ts +3 -1
  3. package/dist/audit.d.ts.map +1 -1
  4. package/dist/audit.js.map +1 -1
  5. package/dist/cedar.d.ts +27 -0
  6. package/dist/cedar.d.ts.map +1 -0
  7. package/dist/cedar.js +134 -0
  8. package/dist/cedar.js.map +1 -0
  9. package/dist/config.d.ts +235 -0
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +54 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/gateway.d.ts +11 -5
  14. package/dist/gateway.d.ts.map +1 -1
  15. package/dist/gateway.js +23 -12
  16. package/dist/gateway.js.map +1 -1
  17. package/dist/hooks/agentLinks.d.ts +18 -0
  18. package/dist/hooks/agentLinks.d.ts.map +1 -0
  19. package/dist/hooks/agentLinks.js +48 -0
  20. package/dist/hooks/agentLinks.js.map +1 -0
  21. package/dist/hooks/claudeCode.d.ts +3 -0
  22. package/dist/hooks/claudeCode.d.ts.map +1 -1
  23. package/dist/hooks/claudeCode.js +27 -7
  24. package/dist/hooks/claudeCode.js.map +1 -1
  25. package/dist/hooks/index.d.ts +1 -0
  26. package/dist/hooks/index.d.ts.map +1 -1
  27. package/dist/hooks/index.js +1 -0
  28. package/dist/hooks/index.js.map +1 -1
  29. package/dist/hooks/run.d.ts.map +1 -1
  30. package/dist/hooks/run.js +3 -1
  31. package/dist/hooks/run.js.map +1 -1
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +1 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/init/suggest.d.ts +3 -1
  37. package/dist/init/suggest.d.ts.map +1 -1
  38. package/dist/init/suggest.js +8 -0
  39. package/dist/init/suggest.js.map +1 -1
  40. package/dist/invoker.d.ts +54 -6
  41. package/dist/invoker.d.ts.map +1 -1
  42. package/dist/invoker.js +115 -19
  43. package/dist/invoker.js.map +1 -1
  44. package/dist/startup.d.ts +15 -1
  45. package/dist/startup.d.ts.map +1 -1
  46. package/dist/startup.js +80 -7
  47. package/dist/startup.js.map +1 -1
  48. package/package.json +15 -4
  49. package/src/audit.ts +3 -1
  50. package/src/cedar.ts +140 -0
  51. package/src/config.ts +57 -0
  52. package/src/gateway.ts +26 -14
  53. package/src/hooks/agentLinks.ts +58 -0
  54. package/src/hooks/claudeCode.ts +31 -7
  55. package/src/hooks/index.ts +1 -0
  56. package/src/hooks/run.ts +3 -1
  57. package/src/index.ts +1 -0
  58. package/src/init/suggest.ts +10 -1
  59. package/src/invoker.ts +146 -21
  60. package/src/startup.ts +75 -8
package/README.md CHANGED
@@ -75,6 +75,20 @@ One file holds both the labels and the gateway settings.
75
75
 
76
76
  Tools without a rule use the defaults (`untrusted`, `private`, egress `unknown`). That's safe but strict, so describe the tools you use. Resources and prompts use the server's `defaults`.
77
77
 
78
+ #### Tools that describe themselves: action-metadata
79
+
80
+ Some servers annotate their tools with the Tool Annotations IG's draft [`action-metadata`](https://github.com/modelcontextprotocol/experimental-ext-tool-annotations/blob/main/specification/draft/action-metadata.mdx) extension (`annotations["io.modelcontextprotocol/action-metadata"]`). The gateway reads it as the server's own claim:
81
+
82
+ | Field | Used as | From any server | Only with `acceptServerClaims: true` |
83
+ |---|---|---|---|
84
+ | `returnMetadata.source` | result integrity | `open-world` makes it `untrusted` | `first-party` makes it `trusted` |
85
+ | `returnMetadata.sensitivity` | result confidentiality | anything but `public` makes it at least `private` (`credentials` / `secret`: `secret`) | `public` |
86
+ | `inputMetadata.destination` | egress | `external` / `public`, or anything stricter than your rule | `local` / `internal` (as `internal`), `none` |
87
+ | `outcome` | policy condition `tool.outcome` | yes | |
88
+ | `requiresReview` | `ask` before the call | yes | |
89
+
90
+ Your rules still come first: a claim can make them stricter, never looser. Unknown values are ignored. The extension is a draft, so its values may change.
91
+
78
92
  #### Drafting rules with `init`
79
93
 
80
94
  List your servers under `gateway.upstreams`, then let `init` draft a rule for every tool:
@@ -87,7 +101,7 @@ adtl-gateway init -c adtl.yaml --write # update the file (original kept as adt
87
101
  `init` connects to each upstream and lists its tools.
88
102
 
89
103
  - **Presets** cover the GitHub (`github-mcp-server`), filesystem and fetch servers, Slack (`@modelcontextprotocol/server-slack` and `slack-mcp-server`) and Gmail (`@gongrzhe/server-gmail-autoauth-mcp`). A preset says what the server's reads and writes mean; the server's `readOnlyHint` decides which tools are reads, so new tools are covered too. For servers without hints, the preset lists their tools by name. Slack messages and email are untrusted and private; posting to Slack is external egress, and so are sending email and Gmail filters, which can forward mail.
90
- - **Other servers** get rules from their MCP tool hints. Hints only set egress, because they are the server's own claims: every result stays `untrusted` and `private` until you change it. These lines are marked `review`.
104
+ - **Other servers** get rules from their action-metadata destination if they declare one, else their MCP tool hints. Either only sets egress, because they are the server's own claims: every result stays `untrusted` and `private` until you change it. These lines are marked `review`.
91
105
  - Entries already in the file are never changed, so you can re-run `init` after a server adds tools. Tools the server no longer offers are reported, not removed.
92
106
  - Edits keep your comments, but the YAML may be reformatted slightly (e.g. `[a]` becomes `[ a ]`).
93
107
 
@@ -114,7 +128,7 @@ Labels only get stricter as a session goes on. A session that has read anything
114
128
 
115
129
  ### Policy
116
130
 
117
- Rules match on `session.integrity`, `session.confidentiality`, `tool.egress` and `tool` (`server.tool` globs such as `github.*`), and decide `allow`, `ask` or `block`. When several rules match, the most restrictive decision wins. `ask` waits for a human when [approvals](#approvals) are configured, and blocks otherwise.
131
+ Rules match on `session.integrity`, `session.confidentiality`, `tool.egress`, `tool` (`server.tool` globs such as `github.*`), and, for tools that declare [action-metadata](#tools-that-describe-themselves-action-metadata), `tool.outcome` (`benign`, `consequential`, `irreversible`) and `tool.requiresReview`. They decide `allow`, `ask` or `block`. When several rules match, the most restrictive decision wins. A tool that says `requiresReview: true` gets `ask` when no rule blocks it; set `policy.honorRequiresReview: false` to turn that off. `ask` waits for a human when [approvals](#approvals) are configured, and blocks otherwise.
118
132
 
119
133
  ### Gateway
120
134
 
@@ -125,13 +139,47 @@ Rules match on `session.integrity`, `session.confidentiality`, `tool.egress` and
125
139
  | `gateway.mode` | `active` (default) or `audit`. See [Trying a policy safely](#trying-a-policy-safely). |
126
140
  | `gateway.approvals` | Ask a human about `ask` decisions. See [Approvals](#approvals). |
127
141
  | `gateway.interceptorTimeoutMs` | Timeout per interceptor call (default 5000) |
142
+ | `gateway.interceptorOverrides.<name>` | Execution policy for one interceptor, overriding what it declares: `failOpen`, `priorityHint`, `mode`, `timeoutMs`, and `hooks` (may only narrow the declared hooks). See [Interceptor overrides](#interceptor-overrides). |
128
143
  | `gateway.interceptors` | Custom interceptor server command (default: the bundled `@adtl/interceptors`) |
144
+ | `gateway.interceptorServers.<id>` | More interceptor servers, stdio or Streamable HTTP, whose interceptors run beside ADTL's. See [More interceptor servers](#more-interceptor-servers). |
129
145
  | `gateway.principal` | Who runs this gateway: `{ id, type?, claims? }`. `${VAR}` works in `id`. Default: anonymous. See [Authorization](#authorization). |
130
146
  | `gateway.roles` | Who may use which tools, prompts and resources. Deny by default once present. See [Authorization](#authorization). |
147
+ | `gateway.cedar` | Cedar policies for attribute-based rules: `{ policies, entities?, timeZone? }`. See [Cedar policies](#cedar-policies). |
148
+
149
+ #### More interceptor servers
150
+
151
+ SEP-2624 interceptors can come from several servers. Add other people's interceptors (PII redaction, logging, your own checks) next to ADTL's:
152
+
153
+ ```yaml
154
+ gateway:
155
+ interceptorServers:
156
+ pii: { command: npx, args: ["-y", "some-pii-interceptor"] } # local, over stdio
157
+ audit: { url: https://audit.example.com/mcp, headers: { Authorization: "Bearer ${AUDIT_TOKEN}" } }
158
+ ```
159
+
160
+ - Each interceptor is invoked on the server that advertised it, in the usual order: mutators by priority then name, validators in parallel.
161
+ - They get the call's payload and context, but not ADTL's per-call `config` (server, tool, annotations), so their own defaults apply.
162
+ - The gateway refuses to start if a server can't be reached, doesn't declare the interceptors extension, or offers an interceptor whose name another server already uses.
163
+ - `adtl.label` runs late (priority 1000) so its label describes the final content. A mutator that would run after it gets a warning at startup, since it could change or drop the label; give it a lower `priorityHint` with an [override](#interceptor-overrides).
164
+
165
+ #### Interceptor overrides
166
+
167
+ SEP-2624 lets the gateway owner change how each interceptor runs without changing the interceptor server. Key them by interceptor name:
168
+
169
+ ```yaml
170
+ gateway:
171
+ interceptorOverrides:
172
+ adtl.label: { timeoutMs: 10000 } # a slow ledger on a network drive
173
+ pii.redact: # a third-party interceptor
174
+ priorityHint: -50000
175
+ hooks: [{ events: ["tools/call"], phase: response }]
176
+ ```
177
+
178
+ The gateway refuses to start if an override names an interceptor the server doesn't offer, or lists hooks the interceptor doesn't declare: hooks can only be narrowed. `gateway.mode: audit` (or `--audit`) still wins over a per-interceptor `mode`. Overrides that relax `adtl.policy` or `adtl.label` (`mode: audit`, `failOpen: true`, or narrowed `hooks`) are allowed, with a warning at startup.
131
179
 
132
180
  #### Checking the audit log
133
181
 
134
- Each entry lists the interceptors that ran for the call, with the execution policy each ran under, as SEP-2624 asks: `interceptors: [{ name, phase, mode, modeFrom, failOpen, failOpenFrom, failed? }]`, where `*From` is `override` (set by the gateway, e.g. `--audit`) or `default` (declared by the interceptor).
182
+ Each entry lists the interceptors that ran for the call, with the execution policy each ran under, as SEP-2624 asks: `interceptors: [{ name, phase, mode, modeFrom, failOpen, failOpenFrom, timeoutMs, failed? }]`, where `*From` is `override` (set by the gateway: `--audit` or `interceptorOverrides`) or `default` (declared by the interceptor).
135
183
 
136
184
  Each line carries `prev`, the hash of the line before, and `hash`, over the line itself. Check the chain with:
137
185
 
@@ -251,7 +299,8 @@ The gateway only sees MCP calls. Claude Code's own tools (`WebFetch`, `WebSearch
251
299
  "hooks": {
252
300
  "PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "npx -y @adtl/gateway hook --config /absolute/path/to/adtl.yaml", "timeout": 10, "onFailure": "block" }] }],
253
301
  "PostToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "npx -y @adtl/gateway hook --config /absolute/path/to/adtl.yaml", "timeout": 10 }] }],
254
- "PostToolUseFailure": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "npx -y @adtl/gateway hook --config /absolute/path/to/adtl.yaml", "timeout": 10 }] }]
302
+ "PostToolUseFailure": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "npx -y @adtl/gateway hook --config /absolute/path/to/adtl.yaml", "timeout": 10 }] }],
303
+ "SubagentStart": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "npx -y @adtl/gateway hook --config /absolute/path/to/adtl.yaml", "timeout": 10 }] }]
255
304
  }
256
305
  }
257
306
  ```
@@ -265,7 +314,7 @@ What it does:
265
314
  - **It shares one session label with the gateway.** Claude Code gives the gateway its session id (`CLAUDE_CODE_SESSION_ID`), and both keep the label in `~/.adtl/sessions/<id>.jsonl` (set `ADTL_SESSIONS_DIR` to change the directory). A `WebFetch` taints the session for `github__add_comment`, and a private file read through the gateway counts against a later `Bash` call. Labels last for the whole Claude Code session, including `--resume`.
266
315
  - **It covers MCP servers that skip the gateway**, such as connectors added in claude.ai. Their tools are labeled as their server name (`mcp__<server>__<tool>` → server `<server>`), so you can describe them under `servers:` like any upstream. The gateway's own tools (`mcp__adtl__*`) are skipped; pass `--gateway-server <name>` if you registered the gateway under another name.
267
316
  - **It fails closed.** If the hook can't decide, it exits with code 2, which stops the call.
268
- - **It writes to the same audit log** (`gateway.audit`) with `"via": "claude-code-hook"`, and follows `gateway.mode: audit`. Calls made inside a subagent (the Task tool) carry `"agent": { id, type }`. Their labels count toward the session id Claude Code gives the hook; if that is the parent's id, as the hook docs suggest, a subagent that reads untrusted content taints the parent too. The config needs no `gateway:` section if you only use the hooks.
317
+ - **It writes to the same audit log** (`gateway.audit`) with `"via": "claude-code-hook"`, and follows `gateway.mode: audit`. Calls made inside a sub-agent carry `"agent": { id, type }`; see [Sub-agents](#sub-agents). The config needs no `gateway:` section if you only use the hooks.
269
318
 
270
319
  Built-in labels, which you can override under `servers.claude-code.tools`:
271
320
 
@@ -282,6 +331,20 @@ With the [example policy](../../examples/adtl.yaml), `Bash` and `WebFetch` run f
282
331
 
283
332
  The hook also refuses `Write` and `Edit` on your ADTL config and session files, and asks before shell commands that mention the session directory, so the agent can't simply clear its own taint. This is a speed bump, not a sandbox.
284
333
 
334
+ ### Sub-agents
335
+
336
+ A sub-agent starts from its parent's label, and what it reads taints the parent: the two share one session label. A sub-agent's answer mixes everything it saw, so sharing is the only safe default.
337
+
338
+ - **Claude Code.** MCP calls from sub-agents go through the same gateway, so they share its session anyway. For built-in tools, the `SubagentStart` hook (in the snippet above) records which session each sub-agent was started in. Its later hook calls then use the parent's label even if Claude Code reports a session id of its own for them; Claude Code's docs suggest it reports the parent's. Audit entries name the sub-agent: `"agent": { "id": "…", "type": "Explore" }`.
339
+ - **Other frameworks.** Start each sub-agent's gateway with its parent's session id and its own agent id:
340
+
341
+ ```sh
342
+ ADTL_SESSION_ID=<parent session> ADTL_AGENT_ID=researcher-1 ADTL_AGENT_TYPE=researcher \
343
+ npx -y @adtl/gateway --config adtl.yaml
344
+ ```
345
+
346
+ `ADTL_SESSION_ID` keeps the label in the shared session files (`ADTL_SESSIONS_DIR`, default `~/.adtl/sessions`), like `CLAUDE_CODE_SESSION_ID` does, and wins over it. `ADTL_AGENT_ID` and `ADTL_AGENT_TYPE` go on every audit entry.
347
+
285
348
  ## Label ledger
286
349
 
287
350
  Session labels end with the session. If an agent writes untrusted content to a file, a fresh session reading that file tomorrow would trust it. The ledger remembers who wrote what:
@@ -321,16 +384,35 @@ Some memory servers extract facts with a model on write, or merge records, so wh
321
384
  servers:
322
385
  memory:
323
386
  tools:
324
- add_memory: { egress: internal, labelInto: metadata, scopeInto: run_id }
325
- update_memory: { egress: internal, labelInto: metadata }
326
- search_memory: { integrity: trusted, confidentiality: public, egress: none, readLabels: true }
387
+ add_memory: { egress: internal, labelInto: metadata, scopeInto: run_id, recordIds: id }
388
+ update_memory: { egress: internal, labelInto: metadata, recordIds: id }
389
+ search_memory: { integrity: trusted, confidentiality: public, egress: none, readLabels: true, recordIds: id }
327
390
  ```
328
391
 
329
392
  - **Write.** On every `add_memory` and `update_memory` call the gateway sets `metadata["adtl/record-label/<session>"]` to `{ integrity, confidentiality, writer }` from the session, and drops any stamp keys the agent put there. Each session stamps under its own key, so on a server that lays an update's metadata over the stored metadata (as Mem0 does), a clean session's update adds a stamp instead of replacing a tainted one.
330
393
  - **Read.** On `search_memory`, stamps anywhere in the result (also inside JSON text) are joined into its label, with the origin `adtl-record` / `session:<id>`, and the audit entry gets `records: { stamps, writers }`. Anyone who can write to the store can write a stamp, so stamps only make a label stricter. A malformed stamp counts as the global defaults.
331
394
  - **Scope.** Some servers also keep recent raw messages per scope, without metadata, and resolve references in a new message from them: Mem0 keeps the last 10 per `user_id`/`agent_id`/`run_id` and asks its extractor to resolve pronouns from them. A clean session writing under a tainted session's ids could then store a fact built from the tainted text, with a clean stamp. With `scopeInto: run_id`, a tainted session's writes get a run id named after its label (`<run_id>:adtl-untrusted-public`, or just `adtl-untrusted-public`), so the server keeps its messages apart. Clean sessions keep their ids, minus any label mark the agent added, so they can't join a tainted scope. Mem0 filters searches by the ids a call sets: a read that names the agent's own `run_id` doesn't see the tainted session's records, and one by `user_id` alone sees them all, with their stamps.
332
395
  - **Tools that return text without metadata** can't carry the label, and the ledger can't recognize extracted text. Give them a strict rule, such as `integrity: untrusted`. In Mem0 that is `history()`, and on Mem0's hosted MCP server `list_events` and `get_event_status`.
333
- - **Servers that drop or replace metadata** (when they consolidate records, or an update overwrites the metadata instead of merging it) can't carry the label either. Give their read tools a strict rule.
396
+ - **Record ids.** With `recordIds: id` (and the ledger on), the gateway also remembers a tainted session's writes by record id: the ids a write tool is given (`update_memory`'s `id`) and returns (`add_memory`'s `ids`). Reads join the label back for any id they return, and the audit entry gets `recordIds: { matched, writers }`. This keeps the label when a server's update replaces the metadata instead of merging it, which [Mem0's hosted docs](https://github.com/probita-ai/adtl/issues/48) describe for `update_memory`.
397
+ - **Merges.** A server that folds records together (Mem0's hosted Merge) keeps the label when the merged text still holds the tainted text as a whole line or paragraph (the ledger recognizes it), and the hidden, merged-away record keeps it by id. A merge that rewords the text loses it: if your account runs Merge, give the read tools a strict rule.
398
+ - **Servers that drop or replace metadata** without returning record ids (when they consolidate records into new ones, for example) can't carry the label either. Give their read tools a strict rule.
399
+
400
+ ## FIDES and fides-gateway
401
+
402
+ ADTL's labels map onto [FIDES](https://arxiv.org/abs/2505.23643), the information-flow model behind Microsoft's [fides-gateway](https://github.com/microsoft/fides-gateway), so the two can sit in one chain.
403
+
404
+ - **Reading** is always on. Results that carry fides-gateway labels (`_meta["com.github.ifc/labels"]`, JSONPath → `{ integrity, confidentiality: [readers] }`) count as the server's own claims, like trust-annotations: they can make a label stricter, and relax it only with `acceptServerClaims: true`. Any reader list without `"public"` means `private`, and the strictest path wins.
405
+ - **Writing** is opt-in:
406
+
407
+ ```yaml
408
+ fides:
409
+ emit: true
410
+ readers: [alice@example.com] # optional: who may read private data; default: gateway.principal.id, else nobody
411
+ ```
412
+
413
+ Results then also carry the label in fides-gateway's form, and every outgoing tool call carries the session's label in its `_meta` (one `"$"` entry, which covers the name and every argument), so a fides-gateway behind ADTL can enforce on it.
414
+
415
+ FIDES has two integrity levels, so `unknown` becomes `untrusted`. It has reader sets instead of levels, so `private` and `secret` both become the configured readers. To check the mapping against fides-gateway's own code, see [examples/fides-interop](../../examples/fides-interop).
334
416
 
335
417
  ## How it works
336
418
 
@@ -361,6 +443,57 @@ gateway:
361
443
  - Over stdio the caller is whoever started the gateway, set with `gateway.principal`. Without it the caller is anonymous and only roles with `members: ["*"]` apply (the gateway warns at startup).
362
444
  - `tools/list`, `prompts/list` and `resources/list` leave out what the caller may not use, so the agent doesn't try it. Hiding is a convenience, not the security boundary: a call to a hidden tool is still checked and denied. Lists are checked without arguments, and nothing is hidden in audit mode. Resource templates are always listed.
363
445
 
446
+ #### Cedar policies
447
+
448
+ For rules roles can't express (working hours, tool arguments, groups kept in another system), write [Cedar](https://www.cedarpolicy.com/) policies:
449
+
450
+ ```sh
451
+ npm install @cedar-policy/cedar-wasm # optional dependency, only needed for gateway.cedar
452
+ ```
453
+
454
+ ```yaml
455
+ gateway:
456
+ principal: { id: "${USER}" }
457
+ cedar:
458
+ policies: ./adtl.cedar # relative to adtl.yaml
459
+ entities: ./entities.json # optional: groups and attributes, in Cedar's entities JSON
460
+ timeZone: Europe/Berlin # optional: for context.time (default: the gateway's)
461
+ ```
462
+
463
+ ```cedar
464
+ // Everyone may read issues.
465
+ permit (principal, action == Action::"tools/call", resource == Tool::"github.issue_read");
466
+
467
+ // Maintainers may use any GitHub tool, on weekdays during working hours.
468
+ permit (principal in Group::"maintainers", action, resource)
469
+ when { context.server == "github" && context.time.hour >= 9 && context.time.hour < 18
470
+ && !["Sat", "Sun"].contains(context.time.weekday) };
471
+
472
+ // Anyone may comment in the orgs listed on their entity (entities.json: "attrs": { "orgs": ["acme"] }).
473
+ permit (principal, action == Action::"tools/call", resource == Tool::"github.add_issue_comment")
474
+ when { context.listing || (principal has orgs && principal.orgs.contains(context.arguments.owner)) };
475
+
476
+ // Nobody reads secrets files.
477
+ forbid (principal, action, resource == Tool::"github.get_file_contents")
478
+ when { context has arguments && context.arguments has path && context.arguments.path like "*secret*" };
479
+ ```
480
+
481
+ Each call becomes a Cedar request:
482
+
483
+ | | |
484
+ |---|---|
485
+ | principal | `User::"<id>"`, `Service::"<id>"`, or `Anonymous::"anonymous"` without an id |
486
+ | action | `Action::"tools/call"`, `Action::"resources/read"` or `Action::"prompts/get"` |
487
+ | resource | `Tool::"<server>.<tool>"`, `Prompt::"<server>.<prompt>"` or `Resource::"<uri>"` |
488
+ | context | `server`, `principalId`, `listing`, `time: { hour, minute, weekday }`, and `arguments` |
489
+
490
+ - Cedar denies unless a policy permits, and a `forbid` beats any `permit`. The denial names the forbidding policy (`policy0`, `policy1`, … in file order).
491
+ - Lists are checked with `listing: true` and no `arguments`. Start a policy that reads arguments with `context.listing ||`, or the tool is hidden from lists (calls are still checked with arguments).
492
+ - Arguments are passed without nulls, with fractions as strings. Objects with `__entity` or `__extn` are passed as JSON strings, so a tool argument can't name an entity.
493
+ - With `roles` set too, both must allow. A policy file that doesn't parse stops the gateway at startup; an evaluation error denies the call.
494
+
495
+ #### Your own authorizer
496
+
364
497
  Or your own `Authorizer` in code:
365
498
 
366
499
  ```ts
@@ -377,6 +510,8 @@ const readOnly: Authorizer = {
377
510
  new AdtlGateway({ upstreams, invoker, authorizer: readOnly, principal: { type: "user", id: "alice" } });
378
511
  ```
379
512
 
513
+ Pass an array (`authorizer: [roles, readOnly]`) to ask several in turn: the first denial wins.
514
+
380
515
  - It runs on every `tools/call`, `resources/read` and `prompts/get`, before the interceptors. A denial stops the call whatever the policy says; an allow still leaves the call to the policy.
381
516
  - It gets `{ principal, event, server, tool | resource | prompt, arguments }`. The principal is also passed to the interceptors as SEP-2624 `context.principal`. Over stdio it is whoever started the gateway (anonymous unless set).
382
517
  - Denials are audited with `denied: { by, reason }`. An authorizer that throws denies the call. In audit mode denials are logged and the call still runs.
@@ -388,8 +523,8 @@ The gateway fails closed. If an interceptor errors or times out, the call is sto
388
523
 
389
524
  - Tools, resources and prompts are proxied. Resource subscriptions, `list_changed` notifications and completions are not passed through yet.
390
525
  - Resources and prompts are labeled by server defaults (`servers.<id>.defaults`) or the global defaults; there are no per-resource or per-prompt rules yet. A resource URI that two upstreams could serve is refused rather than guessed.
391
- - Approvals come from one push channel (ntfy). Other channels, approvals inside the MCP client, and approver roles are planned.
392
- - Session labels last for one agent session: one gateway process, or under Claude Code one Claude Code session (shared with the hooks). The [label ledger](#label-ledger) carries them into files, memory servers and sub-agents by content, but not across machines or other agents yet (v0.7). Memory servers that extract facts or merge records on write return text the ledger cannot recognize; use [labels on records](#labels-on-records), or a strict rule for servers that don't keep metadata.
526
+ - Approvals come from ntfy, email notices and, with `elicitation: true`, the MCP client. Authenticator-app codes (TOTP) are planned.
527
+ - Session labels last for one agent session: one gateway process, or under Claude Code one Claude Code session (shared with the hooks). The [label ledger](#label-ledger) carries them into files, memory servers and sub-agents by content, but not across machines or other agents yet (v0.8). Memory servers that extract facts or merge records on write return text the ledger cannot recognize; use [labels on records](#labels-on-records), or a strict rule for servers that don't keep metadata.
393
528
  - The Claude Code hooks label tools by name, not by what they touched: a `Read` of a downloaded file is as trusted as a `Read` of your own code.
394
529
  - Labels are set by your config. Servers' own trust claims can only make labels stricter, unless you set `acceptServerClaims: true`.
395
530
 
package/dist/audit.d.ts CHANGED
@@ -12,7 +12,7 @@ export interface AuditEntry {
12
12
  outcome: "forwarded" | "blocked" | "asked" | "upstream_error";
13
13
  /** Present when the entry was written by the Claude Code hook rather than the gateway. */
14
14
  via?: "claude-code-hook";
15
- /** Present when a Claude Code subagent made the call: its id and type (e.g. "Explore"). */
15
+ /** Present when a sub-agent made the call: its id and type (e.g. "Explore"), from Claude Code or `ADTL_AGENT_ID`. */
16
16
  agent?: {
17
17
  id: string;
18
18
  type?: string;
@@ -41,6 +41,8 @@ export interface AuditEntry {
41
41
  ledger?: unknown;
42
42
  /** Present when the result carried record label stamps: how many, and which sessions wrote them. */
43
43
  records?: unknown;
44
+ /** Present when the ledger knew record ids in the result (tool rule `recordIds`): how many, and which sessions wrote them. */
45
+ recordIds?: unknown;
44
46
  sessionLabel?: unknown;
45
47
  /** Every interceptor invocation for this call, with its resolved mode and failOpen and where each came from (SEP-2624). */
46
48
  interceptors?: InterceptorRun[];
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,GAAG,gBAAgB,GAAG,aAAa,CAAC;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,gBAAgB,CAAC;IAC9D,0FAA0F;IAC1F,GAAG,CAAC,EAAE,kBAAkB,CAAC;IACzB,2FAA2F;IAC3F,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2GAA2G;IAC3G,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,2FAA2F;IAC3F,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8HAA8H;IAC9H,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oGAAoG;IACpG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2HAA2H;IAC3H,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CAChC;AAED,yCAAyC;AACzC,eAAO,MAAM,aAAa,QAA6B,CAAC;AAsExD,qBAAa,aAAc,YAAW,SAAS;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;IAIjC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAc/B;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CA4B9D;AAED,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,CAAM;IACpC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAG/B"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,GAAG,gBAAgB,GAAG,aAAa,CAAC;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,gBAAgB,CAAC;IAC9D,0FAA0F;IAC1F,GAAG,CAAC,EAAE,kBAAkB,CAAC;IACzB,qHAAqH;IACrH,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2GAA2G;IAC3G,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,2FAA2F;IAC3F,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8HAA8H;IAC9H,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oGAAoG;IACpG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8HAA8H;IAC9H,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2HAA2H;IAC3H,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CAChC;AAED,yCAAyC;AACzC,eAAO,MAAM,aAAa,QAA6B,CAAC;AAsExD,qBAAa,aAAc,YAAW,SAAS;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;IAIjC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAc/B;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CA4B9D;AAED,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,CAAM;IACpC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAG/B"}
package/dist/audit.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { AuthorizationDecision, AuthorizationRequest, Authorizer } from "./authorizer.js";
2
+ import type { CedarConfig } from "./config.js";
3
+ type CedarValue = boolean | number | string | CedarValue[] | {
4
+ [key: string]: CedarValue;
5
+ };
6
+ export declare class CedarAuthorizer implements Authorizer {
7
+ private readonly cedar;
8
+ private readonly policies;
9
+ private readonly entities;
10
+ private readonly timeZone;
11
+ readonly name = "cedar";
12
+ private constructor();
13
+ /** Load the Cedar engine and the policy files. Throws on a missing package or files that don't parse. */
14
+ static load(cfg: CedarConfig, resolvePath: (p: string) => string): Promise<CedarAuthorizer>;
15
+ check(req: AuthorizationRequest): AuthorizationDecision;
16
+ }
17
+ /** Hour, minute and weekday ("Mon") in `timeZone`, for working-hours rules. */
18
+ export declare function clock(now: Date, timeZone: string | undefined): Record<string, CedarValue>;
19
+ /**
20
+ * Tool arguments as Cedar context values. Cedar has no null or fractions, so
21
+ * nulls are left out and fractions become strings. An object with `__entity`
22
+ * or `__extn` would be read as an entity or extension value, so the agent
23
+ * could name any entity; it is passed as a JSON string instead.
24
+ */
25
+ export declare function toCedar(v: unknown): CedarValue | undefined;
26
+ export {};
27
+ //# sourceMappingURL=cedar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cedar.d.ts","sourceRoot":"","sources":["../src/cedar.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC/F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,KAAK,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC;AAY3F,qBAAa,eAAgB,YAAW,UAAU;IAI9C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAN3B,QAAQ,CAAC,IAAI,WAAW;IAExB,OAAO;IAOP,yGAAyG;WAC5F,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAsBjG,KAAK,CAAC,GAAG,EAAE,oBAAoB,GAAG,qBAAqB;CAsCxD;AAED,+EAA+E;AAC/E,wBAAgB,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAIzF;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAe1D"}
package/dist/cedar.js ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Attribute-based authorization with Cedar (`gateway.cedar`):
3
+ *
4
+ * cedar:
5
+ * policies: ./adtl.cedar # Cedar policies, relative to adtl.yaml
6
+ * entities: ./entities.json # optional: Cedar entities (groups, attributes)
7
+ * timeZone: Europe/Berlin # optional: for context.time (default: the gateway's)
8
+ *
9
+ * Every call becomes a Cedar request:
10
+ *
11
+ * principal User::"alice", Service::"ci", or Anonymous::"anonymous" without an id
12
+ * action Action::"tools/call", Action::"resources/read" or Action::"prompts/get"
13
+ * resource Tool::"github.add_issue_comment", Prompt::"github.triage" or Resource::"<uri>"
14
+ * context { server, principalId, listing, time: { hour, minute, weekday }, arguments }
15
+ *
16
+ * Cedar denies unless a policy permits, and a `forbid` wins over any permit.
17
+ * Tool lists are filtered with `listing: true` and no `arguments`, so a policy
18
+ * on arguments should start with `context.listing ||`.
19
+ *
20
+ * Needs the optional dependency @cedar-policy/cedar-wasm, loaded only when
21
+ * `gateway.cedar` is set.
22
+ */
23
+ import { readFileSync } from "node:fs";
24
+ export class CedarAuthorizer {
25
+ cedar;
26
+ policies;
27
+ entities;
28
+ timeZone;
29
+ name = "cedar";
30
+ constructor(cedar, policies, entities, timeZone) {
31
+ this.cedar = cedar;
32
+ this.policies = policies;
33
+ this.entities = entities;
34
+ this.timeZone = timeZone;
35
+ }
36
+ /** Load the Cedar engine and the policy files. Throws on a missing package or files that don't parse. */
37
+ static async load(cfg, resolvePath) {
38
+ let cedar;
39
+ try {
40
+ cedar = (await import("@cedar-policy/cedar-wasm/nodejs"));
41
+ }
42
+ catch {
43
+ throw new Error("gateway.cedar needs the Cedar engine: npm install @cedar-policy/cedar-wasm");
44
+ }
45
+ const policies = readFileSync(resolvePath(cfg.policies), "utf8");
46
+ const parsed = cedar.checkParsePolicySet({ staticPolicies: policies });
47
+ if (parsed.type === "failure")
48
+ throw new Error(`gateway.cedar.policies: ${parsed.errors.map((e) => e.message).join("; ")}`);
49
+ let entities = [];
50
+ if (cfg.entities) {
51
+ const json = JSON.parse(readFileSync(resolvePath(cfg.entities), "utf8"));
52
+ if (!Array.isArray(json))
53
+ throw new Error("gateway.cedar.entities: expected a JSON array of Cedar entities");
54
+ const ok = cedar.checkParseEntities({ entities: json });
55
+ if (ok.type === "failure")
56
+ throw new Error(`gateway.cedar.entities: ${ok.errors.map((e) => e.message).join("; ")}`);
57
+ entities = json;
58
+ }
59
+ return new CedarAuthorizer(cedar, policies, entities, cfg.timeZone);
60
+ }
61
+ check(req) {
62
+ const { principal } = req;
63
+ const [type, id] = req.event === "tools/call"
64
+ ? ["Tool", `${req.server}.${req.tool}`]
65
+ : req.event === "prompts/get"
66
+ ? ["Prompt", `${req.server}.${req.prompt}`]
67
+ : ["Resource", req.resource ?? ""];
68
+ const context = {
69
+ server: req.server,
70
+ listing: req.arguments === undefined,
71
+ time: clock(new Date(), this.timeZone),
72
+ ...(principal.id !== undefined ? { principalId: principal.id } : {}),
73
+ ...(req.arguments !== undefined ? { arguments: toCedar(req.arguments) ?? {} } : {}),
74
+ };
75
+ const answer = this.cedar.isAuthorized({
76
+ principal: principal.id === undefined
77
+ ? { type: "Anonymous", id: "anonymous" }
78
+ : { type: principal.type === "service" ? "Service" : "User", id: principal.id },
79
+ action: { type: "Action", id: req.event },
80
+ resource: { type, id },
81
+ context,
82
+ policies: { staticPolicies: this.policies },
83
+ entities: this.entities,
84
+ });
85
+ if (answer.type === "failure")
86
+ throw new Error(answer.errors.map((e) => e.message).join("; "));
87
+ const { decision, diagnostics } = answer.response;
88
+ if (decision === "allow")
89
+ return { allow: true };
90
+ const who = principal.id ?? "an anonymous caller";
91
+ const what = `the ${type.toLowerCase()} ${id}`;
92
+ return {
93
+ allow: false,
94
+ reason: diagnostics.reason.length
95
+ ? `Cedar policy ${diagnostics.reason.join(", ")} forbids ${who} to use ${what}`
96
+ : `No Cedar policy permits ${who} to use ${what}`,
97
+ };
98
+ }
99
+ }
100
+ /** Hour, minute and weekday ("Mon") in `timeZone`, for working-hours rules. */
101
+ export function clock(now, timeZone) {
102
+ const parts = new Intl.DateTimeFormat("en-US", { timeZone, hourCycle: "h23", hour: "numeric", minute: "numeric", weekday: "short" }).formatToParts(now);
103
+ const part = (t) => parts.find((p) => p.type === t)?.value ?? "";
104
+ return { hour: Number(part("hour")), minute: Number(part("minute")), weekday: part("weekday") };
105
+ }
106
+ /**
107
+ * Tool arguments as Cedar context values. Cedar has no null or fractions, so
108
+ * nulls are left out and fractions become strings. An object with `__entity`
109
+ * or `__extn` would be read as an entity or extension value, so the agent
110
+ * could name any entity; it is passed as a JSON string instead.
111
+ */
112
+ export function toCedar(v) {
113
+ if (v === null || v === undefined)
114
+ return undefined;
115
+ if (typeof v === "boolean" || typeof v === "string")
116
+ return v;
117
+ if (typeof v === "number")
118
+ return Number.isSafeInteger(v) ? v : String(v);
119
+ if (Array.isArray(v))
120
+ return v.map(toCedar).filter((x) => x !== undefined);
121
+ if (typeof v === "object") {
122
+ if ("__entity" in v || "__extn" in v)
123
+ return JSON.stringify(v);
124
+ const out = {};
125
+ for (const [k, x] of Object.entries(v)) {
126
+ const c = toCedar(x);
127
+ if (c !== undefined)
128
+ out[k] = c;
129
+ }
130
+ return out;
131
+ }
132
+ return String(v);
133
+ }
134
+ //# sourceMappingURL=cedar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cedar.js","sourceRoot":"","sources":["../src/cedar.ts"],"names":[],"mappings":"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"}