attenu-guard 0.9.0 → 0.11.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.
- package/CHANGELOG.md +37 -0
- package/dist/cjs/authority.d.ts.map +1 -1
- package/dist/cjs/authority.js +87 -1
- package/dist/cjs/authority.js.map +1 -1
- package/dist/cjs/evidence.d.ts +27 -3
- package/dist/cjs/evidence.d.ts.map +1 -1
- package/dist/cjs/evidence.js +159 -7
- package/dist/cjs/evidence.js.map +1 -1
- package/dist/cjs/guard.d.ts +26 -0
- package/dist/cjs/guard.d.ts.map +1 -1
- package/dist/cjs/guard.js +58 -0
- package/dist/cjs/guard.js.map +1 -1
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +4 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/reasons.d.ts +22 -0
- package/dist/cjs/reasons.d.ts.map +1 -1
- package/dist/cjs/reasons.js +22 -1
- package/dist/cjs/reasons.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.d.ts.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.js.map +1 -1
- package/dist/cjs/wire.d.ts.map +1 -1
- package/dist/cjs/wire.js +42 -0
- package/dist/cjs/wire.js.map +1 -1
- package/dist/esm/authority.d.ts.map +1 -1
- package/dist/esm/authority.js +88 -2
- package/dist/esm/authority.js.map +1 -1
- package/dist/esm/evidence.d.ts +27 -3
- package/dist/esm/evidence.d.ts.map +1 -1
- package/dist/esm/evidence.js +160 -8
- package/dist/esm/evidence.js.map +1 -1
- package/dist/esm/guard.d.ts +26 -0
- package/dist/esm/guard.d.ts.map +1 -1
- package/dist/esm/guard.js +59 -1
- package/dist/esm/guard.js.map +1 -1
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/reasons.d.ts +22 -0
- package/dist/esm/reasons.d.ts.map +1 -1
- package/dist/esm/reasons.js +21 -0
- package/dist/esm/reasons.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.d.ts.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/esm/wire.d.ts.map +1 -1
- package/dist/esm/wire.js +42 -0
- package/dist/esm/wire.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,43 @@ Versions follow semantic versioning.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.11.0] - 2026-09-20
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **`export_bundle(strict=True)` raised `EvidenceLeakError` on any run that refused a delegation.** `detail` is written by the library itself on a refusal (`max_depth`, `max_fanout`, `chain_revoked`, `agent_banned`, `integrity`, `ttl_expired`, and the aggregate-ceiling case) and carries only structural values — an agent or parent id, a numeric limit, a ceiling key — but it was missing from `LEDGER_FIELDS`. That list gates custody mode, so the export an enterprise uses reported a field this library wrote as though it were customer data that must not leave the premises, and refused to produce a bundle. Broken for a whole class of ordinary runs. Found when the verify-side check above failed on our own omnigent example, which is the one place CI drives a depth refusal end to end
|
|
13
|
+
- **`verify_bundle` reported success on ledger entries it only partly read.** `LEDGER_FIELDS` existed but was enforced only on the EXPORT path (`redaction_report`, via `export_bundle(strict=True)`) as a custody check — never on verify. So the verifier read entries by projection: it picked the fields it knows and never looked at the rest. A producer could add `deny_scopes` and `critical` to a `spawn` entry, rehash the chain from genesis exactly as an honest producer does, and `verify_bundle` returned `ok: True` with zero failures while those fields stayed invisible in `delegation_graph`. Demonstrated with a control that re-chains an unmodified bundle and still verifies, so the result isolates the read and is not just the hash covering the field. This is the same defect as the token-side one above, on the offline-verifiable audit trail — the artefact whose whole purpose is that a third party can check it without us. Envelopes (`ENVELOPE_MEMBERS`) already read whole, and an anchor is covered by its signature. **The boundary is stated rather than implied, because a first version of this claimed more than it closed:** in scope are an entry's top-level keys, the `authority` / `granted` wire objects inside it (`Authority.from_wire` now refuses a member outside `scopes`/`constraints`/`ttl` — the token path built that dict itself and was safe only by accident, the bundle path hands over the raw untrusted object), and each constraint within those. Deliberately OUT of scope: `context`, which is free-form by contract and redacted for transport, and the bundle's own top-level members, which no signature covers. New check `checks["ledger_fields"]` and reason `unknown_ledger_fields`, naming every offending field; reported as a failure rather than raised, because an unknown field is a property of the bundle and not an error in the call. Identical rule and identical reason string in the TypeScript port
|
|
14
|
+
- **A Delegation Token carrying authorization data the verifier does not understand verified clean, with that data silently discarded.** Three levels, one defect. (1) `load()` read `authorization_details[0]` and never looked at the rest. (2) Worse, and missed by the first fix: inside a **single** `agent_delegation` entry the loader cherry-picked `scopes` and `constraints` and discarded every other member — so `actions`, `locations`, `datatypes`, `identifier` and `privileges`, the RFC 9396 §2 common members of *any* authorization detail object, were dropped in silence, as was `critical`, whose entire meaning is "do not ignore me". The draft's token format cites RFC 9396 by name, so an issuer restricting this way was doing the sanctioned thing. (3) A constraint object carrying two typed values kept one and dropped the other: `{"key":"max_rows","max":100,"min":9999}` became `{"key":"max_rows","max":100}`, byte-identical to one that never carried a floor. Fail-open in exactly one direction, and it is the dangerous one: ignored data that RESTRICTS authority is lost, while data that GRANTS extra authority is harmless because ignoring it leaves the chain more restrictive. `load()` now refuses, as `malformed`, a token carrying more than one authorization detail, a detail carrying members beyond `type`/`scopes`/`constraints`, or a constraint carrying a member this build does not read; every message names what could not be evaluated. An unrecognised constraint **type** is deliberately still accepted into the fail-closed `UnknownCeiling` path the draft requires, rather than becoming a parse error. RFC 8785 canonicalization is not the protection — re-serializing through the canonicaliser produces a token the old verifier accepted — and a test asserts the refusal reason is never `non_canonical` so it cannot quietly stand in for the real rule. **This is a wire-behaviour change: a token that verified before is now refused.** No fixture under `test/fixtures/vectors/` carries more than one entry or an unread member. Lands together with the identical rules in the Python port, and both were checked to accept and refuse the same eight cases byte-for-byte through `load()`
|
|
15
|
+
|
|
16
|
+
## [0.10.0] - 2026-09-08
|
|
17
|
+
|
|
18
|
+
Parity with attenu-guard (Python) 0.16.0.
|
|
19
|
+
|
|
20
|
+
- Ledger: a new allow-only field `policy` (`unlisted` is the only v1 value) marks an un-gated
|
|
21
|
+
passthrough; the verifier skips containment only for a defined value, reports the count as
|
|
22
|
+
`ungated`, and rejects `policy` on any non-allow entry or with an undefined value, on every
|
|
23
|
+
chain version (`invalid_policy`, `policy_on_non_allow`).
|
|
24
|
+
- `Guard.recordPassthrough` writes such an entry (capture `pre_hook_only`, never pending).
|
|
25
|
+
- `ReasonCode.DELEGATION_REFUSED`; `Policy.UNLISTED`.
|
|
26
|
+
- `denials()` folds `spawn_denied` entries alongside `deny` (`node` = the parent, `requested`
|
|
27
|
+
= the refused sub-agent, `event` on every row).
|
|
28
|
+
- Bundle corpus `bundle_vectors_v1.4` (20 cases): `valid_bundle_v2_ungated_allow` with
|
|
29
|
+
`expect_report`, `reject_unknown_policy_value`, `reject_policy_on_spawn`; the scorer honours
|
|
30
|
+
`expect_report`. Envelope and token corpora unchanged.
|
|
31
|
+
- An `outcome` carrying a `receipt` (`framework_refusal`) verifies and reports the call `observed`.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- `policy` on an `allow` entry (`Policy.UNLISTED`, `"unlisted"`), matching Python's `attenu_guard.reasons.Policy`: an adapter running with `allowUnlisted` let a tool with no declared policy run WITHOUT a `check()`, and the entry records that it did. `LEDGER_FIELDS` accepts it, so `exportBundle({strict: true})` no longer treats it as a leak; it is allow-only, and a `deny` carrying it is `invalid_deny` on a v2 chain. Not a v2-only field
|
|
36
|
+
- `verifyBundle()` reports `ungated`: allow entries carrying `policy` are NOT containment-checked and NOT counted in `actions_checked`. Their `scope` is a label, not a claim of held authority, so running one through containment rejects an honest bundle; dropping it silently understates the run. The count is reported instead, so a reader sees how much of the run was actually measured
|
|
37
|
+
- `ReasonCode.DELEGATION_REFUSED` (`"delegation_refused"`): an adapter refused a delegation before it reached the chain, so nothing was minted and there is no `spawn_denied`; the refusal is recorded as a deny
|
|
38
|
+
- Vendored bundle corpus moved to revision `bundle_vectors_v1.3`, eighteen cases, still copied byte for byte from the Python repository. The new row, `valid_bundle_v2_ungated_allow`, is an ACCEPTING case whose whole content is a report counter: `expect_report: {actions_checked: 2, ungated: 1}`. A case may now declare `expect_report`, named report counters a conformant verifier reproduces exactly; it exists for a rule accept/reject cannot distinguish. The seventeen earlier rows are byte-identical and `version` stays `bundle_vectors_v1`
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- **A `policy` value the format does not define no longer excuses containment.** `policy` was validated in exactly one place, `validateAllow`, which runs inside execution binding and returns early on a `schemaVersion: 1` bundle. On a v1 chain any non-null value therefore bought the exemption: an allow of a scope the node does not hold, stamped `"policy": "totally-made-up"` (or `0`, or `""`), verified clean and reported `containment: true`. Containment is the check the bundle exists to make. Two rules now hold on EVERY bundle version, in `policyFailures`: `policy` may appear only on an `allow` (`policy_on_non_allow` — on a `spawn`, `root` or `outcome` it was accepted silently, because only `deny` carried the allow-only-field rule), and its value must be one `Policy` defines (`invalid_policy`). Containment skips only a value the format knows, so an unknown one is both reported AND still measured. On v2 the existing `invalid_allow`/`invalid_deny` strings are the published contract, so the new check stands down for exactly those two entry shapes and no message is emitted twice. `isKnownPolicy` type-checks before the set lookup: a bundle is untrusted input and can carry any JSON value there, and a truthiness test on a non-string is how a made-up marker buys an exemption. Matches Python's `_policy_failures` failure string for failure string
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
- `denials()` folds `spawn_denied` alongside `deny`, matching Python. A `spawn_denied` is a refused DELEGATION recorded on the PARENT node that asked, so the row's `node` is the entry's `parent` and its new `requested` field names the sub-agent that was refused. Every row gains `event` (`"deny"` or `"spawn_denied"`). A queue that folded only `deny` showed a refused tool call and missed a refused hand-off, which is the larger event of the two
|
|
45
|
+
|
|
9
46
|
## [0.9.0] - 2026-09-06
|
|
10
47
|
|
|
11
48
|
### Added
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authority.d.ts","sourceRoot":"","sources":["../../src/authority.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAA6C,KAAK,KAAK,EAAE,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,
|
|
1
|
+
{"version":3,"file":"authority.d.ts","sourceRoot":"","sources":["../../src/authority.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAA6C,KAAK,KAAK,EAAE,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAGL,KAAK,OAAO,EACZ,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAsB,MAAM,cAAc,CAAC;AAE5D;;;;;;;GAOG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAE1B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAM;CAM/E;AA+ED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;IACpC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAGnB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;CACtB;AAaD,qBAAa,SAAS;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAErC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAEtC,gEAAgE;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEhB,IAAI,GAAE,aAAkB;IAYpC,OAAO,CAAC,KAAK;IAMb,kDAAkD;IAClD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAMzC,4EAA4E;IAC5E,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;IAM5D,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IASvC;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS;IAsCjC;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO;IAkBzC;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,OAAO,GAAG,IAAW,GAAG,QAAQ;IA0B5D,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IAM/B,MAAM,IAAI,aAAa;IAQvB,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,SAAS;IA+BvC,0CAA0C;IAC1C,QAAQ,IAAI,MAAM;IAMlB,QAAQ,IAAI,MAAM;CAGnB"}
|
package/dist/cjs/authority.js
CHANGED
|
@@ -44,6 +44,76 @@ class AuthorityError extends Error {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
exports.AuthorityError = AuthorityError;
|
|
47
|
+
/**
|
|
48
|
+
* `ceilingFromWire`, but refusing a constraint we would only partly read.
|
|
49
|
+
*
|
|
50
|
+
* The test is a MEMBER difference, not value equality. Parse the constraint,
|
|
51
|
+
* re-emit it, and refuse if the input carried a member the re-emission does not
|
|
52
|
+
* -- that member is one this build did not read. That was live and silent:
|
|
53
|
+
*
|
|
54
|
+
* {"key":"max_rows","max":100,"min":9999} -> {"key":"max_rows","max":100}
|
|
55
|
+
*
|
|
56
|
+
* byte-identical to a constraint that never carried a floor. `min` is a
|
|
57
|
+
* first-class type in the draft's constraint vocabulary ("the value MUST NOT be
|
|
58
|
+
* less than it"), so this dropped a signed, spec-defined, *restricting* term.
|
|
59
|
+
* The draft allows one typed value per object, which makes two malformed -- it
|
|
60
|
+
* must be refused, never silently resolved to whichever one this build happens
|
|
61
|
+
* to read first.
|
|
62
|
+
*
|
|
63
|
+
* Comparing whole VALUES instead was tried and reverted before release: it
|
|
64
|
+
* could not tell "we ignored a member" from "we normalised a value", and
|
|
65
|
+
* several built-ins legitimately normalise (`Allow` emits its `one_of` sorted
|
|
66
|
+
* and holds it as a set; `toWire` omits `field` when it equals `key`). RFC 8785
|
|
67
|
+
* canonicalises object member ORDER and never reorders array elements, and the
|
|
68
|
+
* draft puts no ordering or uniqueness requirement on `one_of` -- so
|
|
69
|
+
* `["us-west", "us-east"]` is a conformant constraint from a third-party
|
|
70
|
+
* issuer, and value equality called it malformed. On a release whose whole
|
|
71
|
+
* subject is reading tokens correctly, refusing correct tokens is the worse
|
|
72
|
+
* failure.
|
|
73
|
+
*
|
|
74
|
+
* An unrecognised constraint TYPE is deliberately not caught here:
|
|
75
|
+
* `UnknownCeiling` preserves its whole object, so nothing looks dropped, and it
|
|
76
|
+
* still routes to the fail-closed path the draft requires. Rejecting those here
|
|
77
|
+
* would turn a deny into a parse error and lose that distinction.
|
|
78
|
+
*
|
|
79
|
+
* Kept in step with the Python port (`authority._ceiling_from_wire_whole`).
|
|
80
|
+
*/
|
|
81
|
+
function ceilingFromWireWhole(c) {
|
|
82
|
+
const ceiling = (0, ceilings_js_1.ceilingFromWire)(c);
|
|
83
|
+
const input = (0, canonical_js_1.toPlain)(c);
|
|
84
|
+
if (input !== undefined && input !== null && typeof input === "object") {
|
|
85
|
+
const emitted = (0, canonical_js_1.toPlain)(ceiling.toWire()) ?? {};
|
|
86
|
+
// `key` is the one member whose VALUE is load-bearing: subsumption pairs
|
|
87
|
+
// ceilings by it, so a rewritten key is a different dimension, not a
|
|
88
|
+
// cosmetic difference. `CallLimit` rewrites it ("max_calls" ->
|
|
89
|
+
// "max_calls[fs.write]" when `applies_to` is present), which the member test
|
|
90
|
+
// cannot see because the member set is unchanged. Checked on its own rather
|
|
91
|
+
// than by returning to whole-value equality, which broke conformant tokens a
|
|
92
|
+
// revision ago.
|
|
93
|
+
if (emitted["key"] !== input["key"]) {
|
|
94
|
+
throw new AuthorityError(`constraint ${JSON.stringify(input)} names dimension ${JSON.stringify(input["key"])} ` +
|
|
95
|
+
`but this build reads it as ${JSON.stringify(emitted["key"])}; refusing rather ` +
|
|
96
|
+
"than silently changing which dimension is bounded", "malformed_constraint");
|
|
97
|
+
}
|
|
98
|
+
const dropped = Object.keys(input)
|
|
99
|
+
.filter((k) => !(k in emitted))
|
|
100
|
+
// `field` is read and then not re-emitted when it equals `key`, because at
|
|
101
|
+
// that point it is redundant. Absent from the emission does not mean
|
|
102
|
+
// unread, so exempt it only on EVIDENCE that this ceiling parsed it: the
|
|
103
|
+
// attribute the constructor actually populated. `ctxFieldOf` is not that
|
|
104
|
+
// evidence -- it falls back to a hardcoded `ctxField` and then to `key`,
|
|
105
|
+
// so it returns the input's value by coincidence for the metered built-ins
|
|
106
|
+
// (which never read `field` at all) and unconditionally for a custom
|
|
107
|
+
// ceiling deriving `ctxField` from its own input.
|
|
108
|
+
.filter((k) => !(k === "field" && ceiling.field === input["field"]))
|
|
109
|
+
.sort();
|
|
110
|
+
if (dropped.length > 0) {
|
|
111
|
+
throw new AuthorityError(`constraint ${JSON.stringify(input)} carries members this build does not ` +
|
|
112
|
+
`evaluate and will not ignore: ${dropped.join(", ")}`, "malformed_constraint");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return ceiling;
|
|
116
|
+
}
|
|
47
117
|
const SCOPE_RE = /^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)*\.(?:[a-z][a-z0-9_-]*|\*)$/;
|
|
48
118
|
function validateScope(scope) {
|
|
49
119
|
if (typeof scope !== "string" || !SCOPE_RE.test(scope)) {
|
|
@@ -227,12 +297,28 @@ class Authority {
|
|
|
227
297
|
}
|
|
228
298
|
static fromWire(wire) {
|
|
229
299
|
const d = (0, canonical_js_1.toPlain)(wire) ?? {};
|
|
300
|
+
// Read the authority object WHOLE, for the same reason the constraint inside
|
|
301
|
+
// it is read whole. The token path is safe only by accident --
|
|
302
|
+
// `authorityFromPayload` builds this object itself after checking the
|
|
303
|
+
// detail's members -- but the BUNDLE path hands us the raw untrusted object
|
|
304
|
+
// straight out of a ledger entry (`evidence`, the `authority` and `granted`
|
|
305
|
+
// members). Without this, `verifyBundle` reported success on an authority it
|
|
306
|
+
// had read by projection: a `granted` carrying `deny_scopes` verified clean
|
|
307
|
+
// with `checks.ledger_fields` true, because that check only covers an
|
|
308
|
+
// entry's TOP-LEVEL keys. Kept in step with the Python port.
|
|
309
|
+
const unknownAuthorityMembers = Object.keys(d)
|
|
310
|
+
.filter((k) => k !== "scopes" && k !== "constraints" && k !== "ttl")
|
|
311
|
+
.sort();
|
|
312
|
+
if (unknownAuthorityMembers.length > 0) {
|
|
313
|
+
throw new AuthorityError(`authority ${JSON.stringify(d)} carries members this build does not evaluate ` +
|
|
314
|
+
`and will not ignore: ${unknownAuthorityMembers.join(", ")}`, "malformed_authority");
|
|
315
|
+
}
|
|
230
316
|
const scopes = d["scopes"] ?? [];
|
|
231
317
|
const constraints = d["constraints"] ?? [];
|
|
232
318
|
const ttl = d["ttl"];
|
|
233
319
|
return new Authority({
|
|
234
320
|
scopes,
|
|
235
|
-
ceilings: constraints.map((c) => (
|
|
321
|
+
ceilings: constraints.map((c) => ceilingFromWireWhole(c)),
|
|
236
322
|
ttl: typeof ttl === "number" ? ttl : null,
|
|
237
323
|
});
|
|
238
324
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authority.js","sourceRoot":"","sources":["../../src/authority.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAEH,iDAAkG;AAClG,+
|
|
1
|
+
{"version":3,"file":"authority.js","sourceRoot":"","sources":["../../src/authority.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/evidence.d.ts
CHANGED
|
@@ -173,15 +173,27 @@ export interface DenialRow {
|
|
|
173
173
|
scope: Json;
|
|
174
174
|
disposition: Json;
|
|
175
175
|
reason: Json;
|
|
176
|
+
/** Which refusal event this row folds: `"deny"` (an action) or `"spawn_denied"` (a delegation). */
|
|
177
|
+
event: Json;
|
|
178
|
+
/** The sub-agent a `spawn_denied` refused; `null` on a `deny` row. */
|
|
179
|
+
requested: Json;
|
|
176
180
|
count: number;
|
|
177
181
|
first_seq: number;
|
|
178
182
|
last_seq: number;
|
|
179
183
|
}
|
|
180
184
|
/**
|
|
181
|
-
*
|
|
182
|
-
* queue renders: "should this agent be allowed to <tool>?", with how often it
|
|
183
|
-
*
|
|
185
|
+
* Every refusal on the ledger, grouped by (node, tool, scope, disposition, requested) — the rows
|
|
186
|
+
* a Decisions queue renders: "should this agent be allowed to <tool>?", with how often it asked
|
|
187
|
+
* and why it was refused. A pure fold over the ledger; no engine, no state. Ordered by first
|
|
184
188
|
* occurrence.
|
|
189
|
+
*
|
|
190
|
+
* Two events are refusals, and both are folded here. A `deny` is a refused ACTION: it carries the
|
|
191
|
+
* tool and scope, and `requested` is null. A `spawn_denied` is a refused DELEGATION — the chain
|
|
192
|
+
* would not mint the child (revoked/expired parent, depth/fanout overflow) — recorded once, by
|
|
193
|
+
* `Guard.delegate()`, on the PARENT node that asked; its `requested` names the sub-agent that was
|
|
194
|
+
* refused, and it has no tool or scope because no action was ever authorized. An operator's queue
|
|
195
|
+
* that folded only `deny` would show a refused tool call and miss a refused hand-off, which is the
|
|
196
|
+
* larger event of the two.
|
|
185
197
|
*/
|
|
186
198
|
export declare function denials(bundle: Partial<Bundle>): DenialRow[];
|
|
187
199
|
export interface VerifyChecks {
|
|
@@ -190,6 +202,12 @@ export interface VerifyChecks {
|
|
|
190
202
|
containment: boolean;
|
|
191
203
|
anchor: "not checked" | "verified" | "FAILED";
|
|
192
204
|
version: boolean;
|
|
205
|
+
/**
|
|
206
|
+
* Every entry's top-level fields are within `LEDGER_FIELDS`. False means the bundle carries a
|
|
207
|
+
* field this verifier does not evaluate, so reporting success would be reporting it on an entry
|
|
208
|
+
* that was only partly read.
|
|
209
|
+
*/
|
|
210
|
+
ledger_fields: boolean;
|
|
193
211
|
chain_id: boolean;
|
|
194
212
|
root: boolean;
|
|
195
213
|
expected_anchor: "not checked" | "verified" | "FAILED";
|
|
@@ -341,6 +359,12 @@ export interface VerifyReport {
|
|
|
341
359
|
failure_details: FailureDetail[];
|
|
342
360
|
nodes: number;
|
|
343
361
|
actions_checked: number;
|
|
362
|
+
/**
|
|
363
|
+
* Allow entries carrying `policy` (reasons.Policy): the call happened but the chain never
|
|
364
|
+
* authorized it, so it is NOT containment-checked and NOT counted in `actions_checked`. The
|
|
365
|
+
* count is reported so a reader can see how much of the run was actually measured.
|
|
366
|
+
*/
|
|
367
|
+
ungated: number;
|
|
344
368
|
chain_id: Json;
|
|
345
369
|
execution_binding: ExecutionBinding;
|
|
346
370
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/evidence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAQL,KAAK,KAAK,EACV,KAAK,IAAI,EACV,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAiD,KAAK,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAKpH,OAAO,EAAkC,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAExE;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/evidence.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/evidence.js
CHANGED
|
@@ -87,6 +87,18 @@ exports.LEDGER_FIELDS = new Set([
|
|
|
87
87
|
"strikes",
|
|
88
88
|
"mode",
|
|
89
89
|
"disposition",
|
|
90
|
+
// `detail` is library-written on a refusal (max_depth, max_fanout, chain_revoked, agent_banned,
|
|
91
|
+
// integrity, ttl_expired, aggregate ceiling) and carries only structural values -- an agent or
|
|
92
|
+
// parent id, a numeric limit, a ceiling key. Same class as `node`/`parent`/`agent`; never free
|
|
93
|
+
// text like `task`, never caller-supplied like `context`.
|
|
94
|
+
//
|
|
95
|
+
// Its absence was not cosmetic: LEDGER_FIELDS gates `exportBundle({strict: true})`, so custody
|
|
96
|
+
// mode threw on ANY run that refused a delegation, reporting a field this library wrote as
|
|
97
|
+
// though it were customer data. Kept in step with the Python port.
|
|
98
|
+
"detail",
|
|
99
|
+
// `policy` marks an allow the chain never authorized (an `allowUnlisted` passthrough) — see
|
|
100
|
+
// reasons.Policy and the containment check below. Not a v2-only field.
|
|
101
|
+
"policy",
|
|
90
102
|
// 0.9.0 execution binding (schemaVersion=2 chains): every field named in the spec.
|
|
91
103
|
"call_id",
|
|
92
104
|
"capture",
|
|
@@ -428,10 +440,18 @@ function delegationGraph(bundle) {
|
|
|
428
440
|
return { chain_id: orNull(bundle.chain_id), nodes: meta, edges };
|
|
429
441
|
}
|
|
430
442
|
/**
|
|
431
|
-
*
|
|
432
|
-
* queue renders: "should this agent be allowed to <tool>?", with how often it
|
|
433
|
-
*
|
|
443
|
+
* Every refusal on the ledger, grouped by (node, tool, scope, disposition, requested) — the rows
|
|
444
|
+
* a Decisions queue renders: "should this agent be allowed to <tool>?", with how often it asked
|
|
445
|
+
* and why it was refused. A pure fold over the ledger; no engine, no state. Ordered by first
|
|
434
446
|
* occurrence.
|
|
447
|
+
*
|
|
448
|
+
* Two events are refusals, and both are folded here. A `deny` is a refused ACTION: it carries the
|
|
449
|
+
* tool and scope, and `requested` is null. A `spawn_denied` is a refused DELEGATION — the chain
|
|
450
|
+
* would not mint the child (revoked/expired parent, depth/fanout overflow) — recorded once, by
|
|
451
|
+
* `Guard.delegate()`, on the PARENT node that asked; its `requested` names the sub-agent that was
|
|
452
|
+
* refused, and it has no tool or scope because no action was ever authorized. An operator's queue
|
|
453
|
+
* that folded only `deny` would show a refused tool call and miss a refused hand-off, which is the
|
|
454
|
+
* larger event of the two.
|
|
435
455
|
*/
|
|
436
456
|
function denials(bundle) {
|
|
437
457
|
const entries = bundle.entries ?? [];
|
|
@@ -444,10 +464,20 @@ function denials(bundle) {
|
|
|
444
464
|
}
|
|
445
465
|
const rows = new Map();
|
|
446
466
|
for (const e of entries) {
|
|
447
|
-
|
|
467
|
+
const ev = (0, canonical_js_1.toPlain)(e["event"]);
|
|
468
|
+
if (ev !== "deny" && ev !== "spawn_denied")
|
|
448
469
|
continue;
|
|
449
|
-
|
|
450
|
-
|
|
470
|
+
// `spawn_denied` names the acting node in `parent` (there is no child node to name — that is
|
|
471
|
+
// what was refused), so it is folded onto the node that asked.
|
|
472
|
+
const node = ev === "spawn_denied" ? orNull(e["parent"]) : orNull(e["node"]);
|
|
473
|
+
const requested = ev === "spawn_denied" ? orNull(e["agent"]) : null;
|
|
474
|
+
const key = JSON.stringify([
|
|
475
|
+
node,
|
|
476
|
+
orNull(e["tool"]),
|
|
477
|
+
orNull(e["scope"]),
|
|
478
|
+
orNull(e["disposition"]),
|
|
479
|
+
requested,
|
|
480
|
+
]);
|
|
451
481
|
const seq = (0, canonical_js_1.toPlain)(e["seq"]);
|
|
452
482
|
const existing = rows.get(key);
|
|
453
483
|
if (existing === undefined) {
|
|
@@ -458,6 +488,8 @@ function denials(bundle) {
|
|
|
458
488
|
scope: orNull(e["scope"]),
|
|
459
489
|
disposition: orNull(e["disposition"]),
|
|
460
490
|
reason: orNull(e["reason"]),
|
|
491
|
+
event: ev,
|
|
492
|
+
requested,
|
|
461
493
|
count: 1,
|
|
462
494
|
first_seq: seq,
|
|
463
495
|
last_seq: seq,
|
|
@@ -493,6 +525,14 @@ function pyRepr(value) {
|
|
|
493
525
|
}
|
|
494
526
|
return JSON.stringify(value);
|
|
495
527
|
}
|
|
528
|
+
/**
|
|
529
|
+
* Python's `str()` where a failure string interpolates a value bare (`{x}`) rather than through
|
|
530
|
+
* `repr` (`{x!r}`) — the same rendering for every type except a string, which `str` leaves
|
|
531
|
+
* unquoted. Both implementations report the same failure bytes, so the distinction is load-bearing.
|
|
532
|
+
*/
|
|
533
|
+
function pyStr(value) {
|
|
534
|
+
return typeof value === "string" ? value : pyRepr(value);
|
|
535
|
+
}
|
|
496
536
|
// =============================================================================================
|
|
497
537
|
// Observer envelopes (envelope v1) — the TypeScript half of `attenu_guard.evidence`'s.
|
|
498
538
|
//
|
|
@@ -1137,13 +1177,25 @@ function validateAllow(e) {
|
|
|
1137
1177
|
return `adapter[${JSON.stringify(k)}] must be a non-empty string`;
|
|
1138
1178
|
}
|
|
1139
1179
|
}
|
|
1180
|
+
if (presentButNull(e, "policy")) {
|
|
1181
|
+
return "policy is explicitly null (omit it, or give a valid Policy value)";
|
|
1182
|
+
}
|
|
1183
|
+
if ("policy" in e && !isKnownPolicy((0, canonical_js_1.toPlain)(e["policy"]))) {
|
|
1184
|
+
return `policy ${pyRepr((0, canonical_js_1.toPlain)(e["policy"]))} not a known value`;
|
|
1185
|
+
}
|
|
1140
1186
|
err = validHashField(e, "authorized_params_hash");
|
|
1141
1187
|
if (err)
|
|
1142
1188
|
return err;
|
|
1143
1189
|
return validParamsHashReason(e, "authorized_params_hash");
|
|
1144
1190
|
}
|
|
1145
1191
|
/** Fields that only ever belong on an `allow` entry — illegal on a `deny`, on any schema version. */
|
|
1146
|
-
const ALLOW_ONLY_FIELDS = [
|
|
1192
|
+
const ALLOW_ONLY_FIELDS = [
|
|
1193
|
+
"capture",
|
|
1194
|
+
"adapter",
|
|
1195
|
+
"authorized_params_hash",
|
|
1196
|
+
"params_hash_reason",
|
|
1197
|
+
"policy",
|
|
1198
|
+
];
|
|
1147
1199
|
function validateDeny(e) {
|
|
1148
1200
|
const err = validCallId(e);
|
|
1149
1201
|
if (err)
|
|
@@ -1278,6 +1330,61 @@ const V2_ONLY_FIELDS = [
|
|
|
1278
1330
|
* Every v2-only field found on any entry of a `schemaVersion: 1` bundle — mixed-version data,
|
|
1279
1331
|
* invalid regardless of which field it is (merge-gate item 4/(c)).
|
|
1280
1332
|
*/
|
|
1333
|
+
/**
|
|
1334
|
+
* A `policy` the format defines. Type-checked first: a bundle is untrusted input and can carry
|
|
1335
|
+
* any JSON value there (`0`, `""`, `[]`, `{}`), and a bare truthiness or membership test on a
|
|
1336
|
+
* non-string is how a made-up marker buys an exemption it was never entitled to.
|
|
1337
|
+
*/
|
|
1338
|
+
function isKnownPolicy(value) {
|
|
1339
|
+
return typeof value === "string" && reasons_js_1.POLICIES.has(value);
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* `policy` is checked on EVERY bundle version, because it is what excuses an entry from
|
|
1343
|
+
* containment.
|
|
1344
|
+
*
|
|
1345
|
+
* An `allow` carrying `policy` is not tested for containment: it says the chain never authorized
|
|
1346
|
+
* the call, so there is nothing to contain. That exemption is only sound if the value naming it
|
|
1347
|
+
* is one the format actually defines. It was validated in `validateAllow`, which runs inside
|
|
1348
|
+
* `executionBinding` — and that returns early on a `schemaVersion: 1` bundle. So on a v1 chain
|
|
1349
|
+
* ANY non-null value bought the exemption: `"anything"`, `0`, `""`. An out-of-authority action
|
|
1350
|
+
* stamped with a made-up policy verified clean and reported `containment: true`, which is the one
|
|
1351
|
+
* thing this bundle exists to say honestly.
|
|
1352
|
+
*
|
|
1353
|
+
* Two rules, both version-independent:
|
|
1354
|
+
*
|
|
1355
|
+
* - `policy` may appear ONLY on an `allow`. It answers "how did this allow come to be"; on a
|
|
1356
|
+
* `spawn`, `root` or `outcome` it means nothing and was accepted silently (only `deny` was
|
|
1357
|
+
* checked, by `validateDeny`'s allow-only-field rule).
|
|
1358
|
+
* - its value must be one `reasons.Policy` defines (`unlisted` is the only one in v1).
|
|
1359
|
+
*
|
|
1360
|
+
* On a v2 bundle `validateAllow`/`validateDeny` already report these two entry shapes and their
|
|
1361
|
+
* strings are the published contract, so this check stands down for exactly those cases and
|
|
1362
|
+
* covers what they do not reach. No message is ever emitted twice.
|
|
1363
|
+
*/
|
|
1364
|
+
function policyFailures(entries, bundleV) {
|
|
1365
|
+
const failures = new FailureLog();
|
|
1366
|
+
for (const e of entries) {
|
|
1367
|
+
if (!("policy" in e))
|
|
1368
|
+
continue;
|
|
1369
|
+
const ev = (0, canonical_js_1.toPlain)(e["event"]);
|
|
1370
|
+
const position = { seq: orNull(e["seq"]), node: orNull(e["node"]) };
|
|
1371
|
+
if (ev === "allow") {
|
|
1372
|
+
if (bundleV === 2)
|
|
1373
|
+
continue; // validateAllow owns this entry's message
|
|
1374
|
+
if (!isKnownPolicy((0, canonical_js_1.toPlain)(e["policy"]))) {
|
|
1375
|
+
failures.add("invalid_policy", `invalid_policy: seq=${pyStr(orNull(e["seq"]))} allow carries policy ` +
|
|
1376
|
+
`${pyRepr(orNull(e["policy"]))}, not a value this format defines`, position);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
else {
|
|
1380
|
+
if (ev === "deny" && bundleV === 2)
|
|
1381
|
+
continue; // validateDeny owns this entry's message
|
|
1382
|
+
failures.add("policy_on_non_allow", `policy_on_non_allow: seq=${pyStr(orNull(e["seq"]))} event=${pyRepr(orNull(e["event"]))} ` +
|
|
1383
|
+
"carries `policy`, which is an allow-only field", position);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return failures;
|
|
1387
|
+
}
|
|
1281
1388
|
function v2FieldLeaksOnV1(entries) {
|
|
1282
1389
|
const failures = new FailureLog();
|
|
1283
1390
|
for (const e of entries) {
|
|
@@ -1554,6 +1661,7 @@ function verifyBundle(bundle, signer = null, options = {}) {
|
|
|
1554
1661
|
containment: false,
|
|
1555
1662
|
anchor: "not checked",
|
|
1556
1663
|
version: false,
|
|
1664
|
+
ledger_fields: false,
|
|
1557
1665
|
chain_id: false,
|
|
1558
1666
|
root: false,
|
|
1559
1667
|
expected_anchor: "not checked",
|
|
@@ -1596,6 +1704,33 @@ function verifyBundle(bundle, signer = null, options = {}) {
|
|
|
1596
1704
|
log.add("mixed_entry_versions", `mixed_entry_versions: entries declare v in [${mixed.map((v) => pyRepr(v)).join(", ")}], bundle v=${pyRepr(bundleV)}`, { seq: orNull(mixedEntries[0]["seq"]), node: orNull(mixedEntries[0]["node"]) });
|
|
1597
1705
|
}
|
|
1598
1706
|
checks.version = versionOk;
|
|
1707
|
+
// (0b2) every entry must be read WHOLE. `LEDGER_FIELDS` was enforced only on the EXPORT path
|
|
1708
|
+
// (`redactionReport`, via `exportBundle({strict: true})`), never on verify, so the verifier read
|
|
1709
|
+
// entries by projection: it picked the fields it knows and never looked at the rest. A producer
|
|
1710
|
+
// could add `deny_scopes` and `critical` to a `spawn` entry, rehash the chain from genesis as any
|
|
1711
|
+
// honest producer does, and `verifyBundle` returned ok=true with zero failures while those fields
|
|
1712
|
+
// stayed invisible in `delegationGraph`.
|
|
1713
|
+
//
|
|
1714
|
+
// That is the same defect as the token-side one this release fixes -- reporting success on input
|
|
1715
|
+
// we did not fully read -- but on the offline-verifiable audit trail, which is the thing the
|
|
1716
|
+
// bundle exists to be. Envelopes and anchors already read whole; this was the one structure left.
|
|
1717
|
+
//
|
|
1718
|
+
// Reported as a failure rather than thrown: `verifyBundle` returns a report, and an unknown field
|
|
1719
|
+
// is a property of the bundle, not an error in the call.
|
|
1720
|
+
//
|
|
1721
|
+
// Kept in step with the Python port (`evidence._verify_bundle`, same check, same reason string).
|
|
1722
|
+
let unknownOk = true;
|
|
1723
|
+
for (const e of entries) {
|
|
1724
|
+
const extra = Object.keys(e)
|
|
1725
|
+
.filter((f) => !exports.LEDGER_FIELDS.has(f))
|
|
1726
|
+
.sort();
|
|
1727
|
+
if (extra.length > 0) {
|
|
1728
|
+
unknownOk = false;
|
|
1729
|
+
log.add("unknown_ledger_fields", `unknown_ledger_fields: entry carries fields this verifier does not evaluate and will ` +
|
|
1730
|
+
`not ignore: ${extra.join(", ")}`, { seq: orNull(e["seq"]), node: orNull(e["node"]) });
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
checks.ledger_fields = unknownOk;
|
|
1599
1734
|
// (0c) independently retained expected anchor/head: verified against the BUNDLE's actual
|
|
1600
1735
|
// computed head, never against its own (possibly forged) enclosed anchor.
|
|
1601
1736
|
const { expectedAnchor = null, expectedHead = null } = options;
|
|
@@ -1681,11 +1816,25 @@ function verifyBundle(bundle, signer = null, options = {}) {
|
|
|
1681
1816
|
}
|
|
1682
1817
|
checks.monotonicity = mono && afail.length === 0;
|
|
1683
1818
|
// (3) containment: every allowed action's scope within the acting node's authority.
|
|
1819
|
+
// An allow carrying `policy` (reasons.Policy) is one the chain never authorized — an adapter
|
|
1820
|
+
// running with `allowUnlisted` passed the call through un-gated and recorded that it did. Its
|
|
1821
|
+
// `scope` is a label, not a claim of held authority, so testing it for containment would report
|
|
1822
|
+
// a violation the entry never asserted. Such entries are counted as UNGATED and reported as
|
|
1823
|
+
// their own number instead: a reader sees how much of the run was actually measured, which is
|
|
1824
|
+
// the honest answer and never a silent one.
|
|
1684
1825
|
let contained = true;
|
|
1685
1826
|
let actions = 0;
|
|
1827
|
+
let ungated = 0;
|
|
1686
1828
|
for (const e of entries) {
|
|
1687
1829
|
if ((0, canonical_js_1.toPlain)(e["event"]) !== "allow")
|
|
1688
1830
|
continue;
|
|
1831
|
+
if (isKnownPolicy((0, canonical_js_1.toPlain)(e["policy"]))) {
|
|
1832
|
+
// Only a policy value the format DEFINES buys the exemption. An entry carrying anything
|
|
1833
|
+
// else is reported by `policyFailures` and still measured here, so a made-up marker cannot
|
|
1834
|
+
// excuse an out-of-authority action.
|
|
1835
|
+
ungated += 1;
|
|
1836
|
+
continue;
|
|
1837
|
+
}
|
|
1689
1838
|
actions += 1;
|
|
1690
1839
|
const node = (0, canonical_js_1.toPlain)(e["node"]);
|
|
1691
1840
|
const scope = (0, canonical_js_1.toPlain)(e["scope"]);
|
|
@@ -1712,6 +1861,7 @@ function verifyBundle(bundle, signer = null, options = {}) {
|
|
|
1712
1861
|
: [{ status: "not applicable" }, new FailureLog()];
|
|
1713
1862
|
if (eb.failures !== undefined && eb.failures.length > 0)
|
|
1714
1863
|
log.extend(ebFailures);
|
|
1864
|
+
log.extend(policyFailures(entries, bundleV));
|
|
1715
1865
|
// (4) observer envelopes. Never required — an absent envelope is the status quo and changes
|
|
1716
1866
|
// nothing — but a PRESENT one has to verify, and a broken one lands in this same list. The
|
|
1717
1867
|
// per-entry state is reported either way, so a reader sees which hops were covered before
|
|
@@ -1731,6 +1881,7 @@ function verifyBundle(bundle, signer = null, options = {}) {
|
|
|
1731
1881
|
checks.monotonicity &&
|
|
1732
1882
|
checks.containment &&
|
|
1733
1883
|
checks.version &&
|
|
1884
|
+
checks.ledger_fields &&
|
|
1734
1885
|
checks.chain_id &&
|
|
1735
1886
|
checks.root &&
|
|
1736
1887
|
log.length === 0;
|
|
@@ -1741,6 +1892,7 @@ function verifyBundle(bundle, signer = null, options = {}) {
|
|
|
1741
1892
|
failure_details: log.details,
|
|
1742
1893
|
nodes: auth.size,
|
|
1743
1894
|
actions_checked: actions,
|
|
1895
|
+
ungated,
|
|
1744
1896
|
chain_id: orNull(bundle.chain_id),
|
|
1745
1897
|
execution_binding: eb,
|
|
1746
1898
|
envelopes: envelopeSummary,
|