attenu-guard 0.5.0 → 0.7.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 CHANGED
@@ -1,394 +1,99 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this package are documented here. The format follows
4
- [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this package
5
- follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
3
+ One line per change. The long-form notes for each version (reasoning, review findings, migration detail) are in [`docs/release-notes/`](docs/release-notes/README.md).
4
+
5
+ Versions follow semantic versioning.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.7.0] - 2026-09-03
10
+
11
+ ### Fixed
12
+ - `verifyBundle()` monotonicity: a delegation that widened only ttl or only a ceiling verified CLEAN whenever the child's scopes were literally a subset of the parent's — the check was gated on a literal, non-wildcard-aware scope difference, so a `false` from `isNarrowerThan` was discarded. A child that outlived its parent, raised a ceiling, dropped a ceiling its parent held, or carried no ttl at all now fails, and the message names the dimension (`ttl 7200 > parent 3600`, `ceiling max_rows<=250 looser than parent max_rows<=100`), byte-identical to Python. The scope-widening string is unchanged
13
+
14
+ ### Added
15
+ - Vendored bundle interop vectors updated to revision `bundle_vectors_v1.1` — four appended cases: `reject_widened_scope`, `reject_uncontained_allow`, `reject_increased_ttl` and `reject_loosened_ceiling`; `version` stays `bundle_vectors_v1`
16
+
17
+ ## [0.6.0] - 2026-09-02
18
+
19
+ ### Added
20
+ - Added `test/fixtures/vectors/bundles/bundle_vectors_v1.json`, 8 bundle-verifier interop vectors matching the Python reference byte-for-byte.
21
+ - `verifyBundle()` — reports `failure_details` alongside `failures`, one structured `{reason, seq, node, call_id, detail}` entry per failure.
22
+
23
+ Full notes: https://github.com/attenu-io/attenu-guard-ts/blob/main/docs/release-notes/v0.6.0.md
24
+
9
25
  ## [0.5.0] - 2026-08-31
10
26
 
11
27
  ### Fixed
12
- - **Adapter mirror of the Python batch-1/batch-2 adversarial review.** The TS package ships
13
- exactly one adapter surface (`src/adapters/langgraph.ts` — `package.json`'s `exports` map
14
- declares nothing besides `.` and `./adapters/langgraph`; no A2A, no generic wrapper, no
15
- separate LangChain.js seam), and execution binding was already fully wired into both
16
- `guardNode` and `guardTool` as of 0.4.0. Each Python defect class was checked against this
17
- specific adapter, against pinned `@langchain/core@1.2.9`/`@langchain/langgraph@1.4.13` source
18
- (installed and grepped directly, not assumed), rather than ported by analogy — see the module
19
- doc comment's own "Adversarial review" section for the full per-class evidence. Six of the
20
- seven classes came back genuinely inapplicable to this adapter's architecture (no composable
21
- middleware chain in either pinned framework package; one wrapper per call, no second gate; no
22
- cross-hook correlation map to collide or grow unbounded; `isDeferredResult` already covers
23
- JavaScript's whole lazy-result landscape; no external multi-phase hook dispatch to lose an
24
- event across; `src/`'s only runtime import is a lazy `@langchain/langgraph`, correctly
25
- undeclared as a hard dependency, matching the README's own "zero runtime dependencies" claim).
26
- One real, TS-specific gap was found in the snapshot-commitment family and fixed:
27
- - `snapshotParams`/`snapshotToolParams`'s fallback, taken when `structuredClone` cannot clone
28
- the value being snapshotted (a function, a class instance it refuses, anything sharing an
29
- object graph with one of those), was a bare shallow copy — `{args: [...args]}` makes a fresh
30
- OUTER array, but every element INSIDE it is the same live reference as the real call
31
- arguments. Reproduced directly before fixing: `snapshot.args[0] === liveArg` was `true`, and
32
- a mutation of `liveArg` after the call was visible through the "snapshot" — violating the
33
- adapter's own documented guarantee ("an IMMUTABLE snapshot... taken BEFORE the wrapped
34
- callable runs"). Checked the specific `toJSON` vector first: `structuredClone` does NOT
35
- consult a `toJSON` method or any other user-overridable protocol the way Python's
36
- `copy.deepcopy` consults `__deepcopy__` (verified empirically — a hostile class's own
37
- `toJSON` returning fabricated data is simply ignored, and the clone is never the same
38
- OBJECT reference for that specific case); only the FAILURE path aliased for a hostile
39
- `toJSON`. See the release-gate correction below for why "a successful clone never aliases"
40
- was still wrong as a general claim (a `SharedArrayBuffer` clones "successfully" to a
41
- DIFFERENT object that shares the SAME underlying memory).
42
- - Fixed with a new `freeze()` function (exported for direct testing, the same reason every
43
- Python adapter's own `_freeze()` is imported directly by its tests — the audit log never
44
- exposes the raw snapshot value, only its hash, so "does this alias" is not otherwise
45
- observable): safe JSON-primitive leaves pass through verbatim, plain objects/arrays are
46
- rebuilt fresh and recursively, and anything else (a function, a class instance, a
47
- `Map`/`Set`/`Date`/`RegExp`, a `Symbol`, a `BigInt`) becomes a safe string representation —
48
- never the live reference. This is `structuredClone`'s support matrix happening to overlap
49
- with what the audit log's own JCS canonicalizer (`params.ts`) can hash, unconditionally,
50
- matching the same invariant every Python adapter's `_freeze()` already holds, rather than
51
- scoped narrowly to only the cases proven to reach a hash mismatch. Guards a circular
52
- reference with a `WeakSet` (`structuredClone` handles cycles natively; the whole point of
53
- this function is the cases it could NOT handle, one of which could still be cyclic). A
54
- welcome side effect: because `freeze()` sanitizes an otherwise-unsupported value BEFORE it
55
- is ever handed to `params.ts`'s `commit()`, a call that used to commit no hash at all
56
- (`paramsHashReason: "unsupported"`) now commits a real, verifiable one.
57
- - Tests added in `test/adapter-langgraph.test.ts`: two direct unit tests on `freeze()` itself
58
- (never aliases the unclonable value's own case; never aliases a mutable SIBLING sharing the
59
- same object graph as an unclonable value — the mixed case), one guarding the circular
60
- reference, and one end-to-end test per wrapper (`guardNode`, `guardTool`) driving an
61
- unclonable argument through the real call path and asserting a genuine, matching
62
- `authorizedParamsHash`/`invokedParamsHash` pair is committed rather than `"unsupported"`.
63
- - **Delta review, two more edits to `freeze()` itself:**
64
- - **Medium-low, required:** the plain-object branch built the rebuilt object with an
65
- `out[k] = freeze(v, seen)` accumulation loop. A plain `JSON.parse('{"__proto__": {...}}')`
66
- result genuinely has `"__proto__"` as an OWN, ENUMERABLE data property (`Object.keys`
67
- lists it — JSON has no notion of prototypes, so this is reachable from ordinary untrusted
68
- input, not a contrived shape) — but assigning through `out[k] = v` for that specific key
69
- name does not create a data property at all; it sets the accumulator's own `[[Prototype]]`
70
- via `Object.prototype`'s own `__proto__` accessor instead. The key then vanished from the
71
- rebuilt object's own enumerable keys entirely — a params-commitment completeness gap
72
- (substitution on that key would be invisible to a params mismatch) that `structuredClone`'s
73
- own success path, and Python's `_freeze()`, do not have. Fixed with
74
- `Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, freeze(v, seen)]))`, which
75
- always defines genuine data properties, `__proto__` included. Test added: a
76
- `JSON.parse`-created `__proto__` own key beside an unclonable sibling (forcing the
77
- fallback) — the key survives into the snapshot with its value, and the rebuilt object's
78
- own prototype is unaffected.
79
- - **Nit, ride-along:** the array branch used `value.map((v) => freeze(v, seen))` —
80
- `Array.prototype.map` SKIPS a hole in a sparse array rather than visiting it, so a hole
81
- would survive into the snapshot as a hole too, unlike every other absent value `freeze()`
82
- turns into a plain `null`. Fixed with `Array.from(value, (v) => freeze(v, seen))`, which
83
- visits every index up to `.length`, densifying a hole to `undefined` (then `null`, same as
84
- any other `undefined`). Test added: `freeze([1, , 3])` equals `[1, null, 3]`, with a real
85
- (densified) element at index 1, not a hole.
86
- - **Release-gate correction (CRITICAL + HIGH): `freeze()` was still only a FALLBACK, run only
87
- when `structuredClone` THREW — a successful clone bypassed it entirely, and "a successful
88
- clone never aliases" (asserted above) was not actually true.** Three bypasses, each
89
- reproduced directly before fixing: (1) a circular object clones successfully —
90
- `structuredClone` handles cycles natively — so `freeze()` never ran on it at all; the
91
- circularity then reached `params.ts`'s own hash-commitment walk, which has NO cycle guard,
92
- and crashed with `RangeError: Maximum call stack size exceeded` before authorization or the
93
- tool body ever ran. (2) A sparse array clones successfully too, holes preserved, bypassing
94
- `freeze()`'s own densification (added above) entirely — it reached `params.ts` as
95
- `paramsHashReason: "unsupported"` instead of a real, densified, hashable snapshot. (3) A
96
- `SharedArrayBuffer` clones to a DISTINCT wrapper object that shares the SAME underlying
97
- memory, by design — a "successful" clone that is not independent at all. Fixed by making
98
- `freeze()` the ONLY snapshot path, unconditionally: `structuredClone` is no longer called
99
- anywhere in this adapter. Also fixed in the same pass: the cycle guard (`seen`) was a
100
- single, MUTABLE `WeakSet` shared across the whole call, added to but never removed from —
101
- so a DAG's repeated sibling reference (the SAME object appearing twice as two different
102
- keys' values, never as its own ancestor) was wrongly reported `"<circular>"` on its second
103
- occurrence, reproduced directly: `freeze({a: shared, b: shared})` came back
104
- `{"a": {...}, "b": "<circular>"}`. Renamed to `active`, a PATH-ACTIVE `ReadonlySet` — a
105
- fresh `Set` unioned in at each recursive call, never mutated in place or shared across
106
- sibling branches. Separately, HIGH: the array and object branches (`Array.from`/`.map()` and
107
- `Object.entries()`) invoke the value's OWN protocols — a hostile `[Symbol.iterator]`
108
- override can yield ANYTHING regardless of an array's real indexed properties (reproduced:
109
- `[1, , 3]` with a hostile iterator froze as `[999]`), and a getter is INVOKED by
110
- `Object.entries()`, with no guarantee of being invoked only once (reproduced: with an
111
- unclonable sibling forcing the old fallback, a getter with a side effect was observed three
112
- times across the old clone-attempt/freeze/body sequence, and the committed snapshot was the
113
- SECOND of three observations, not the first). Fixed: both branches now walk
114
- `Object.getOwnPropertyDescriptor` directly (arrays by a `.length`-bounded index loop, objects
115
- by `Object.keys`) — pure introspection, never invoking user code — and an accessor property
116
- (`.get`/`.set` present) is encoded as the literal string `"<accessor>"` rather than read at
117
- all. **A regression caught and fixed before this same commit landed:** rewriting the
118
- object branch's write-back re-introduced the EXACT `out[key] = value` bug the `__proto__`
119
- fix above had already closed (a bracket assignment to the literal key `"__proto__"` sets the
120
- accumulator's prototype instead of a data property) — caught by running the existing
121
- `__proto__` test against the rewrite, not assumed fixed; corrected with
122
- `Object.defineProperty` in the loop instead. Six new tests added, each driving the REAL
123
- wrapper (`guardNode`/`guardTool`), not `freeze()` directly — the earlier circular test
124
- guarded the wrong path (it passed even while the actual wrapper crashed): circular input,
125
- sparse array, hostile custom iterator, getter, `SharedArrayBuffer`, and a DAG's repeated
126
- reference, all via `test/adapter-langgraph.test.ts`.
127
- - **Release-gate correction (HIGH): `isDeferredResult` missed a plain `AsyncIterable`.** The
128
- async branch required the result to have its OWN `.next` method, matching a self-iterating
129
- async generator — but the JavaScript async-iterable protocol only requires a callable
130
- `[Symbol.asyncIterator]()`, which can return a SEPARATE object that has `.next`, without the
131
- iterable itself ever having one. Reproduced directly before fixing: a plain object
132
- implementing only `[Symbol.asyncIterator]()` was recorded `BodyState.RETURNED`, not
133
- `DEFERRED`. Fixed: the async check no longer requires an own `.next` (the sync check, which
134
- DOES require it, was deliberately left alone — dropping it there would misdetect a plain
135
- `Array`/`Set`/`Map` as deferred, since those implement `Symbol.iterator` too without their
136
- contents being lazily produced; there is no equivalent JavaScript built-in that implements
137
- `Symbol.asyncIterator` over already-computed values, so this asymmetry is not itself a gap).
138
- The module doc comment's own "whole lazy-result landscape" claim is narrowed to list exactly
139
- what `isDeferredResult` checks, rather than asserting completeness. Two tests added: the
140
- async-iterable case now scores `DEFERRED`; a plain array result is confirmed to still score
141
- `RETURNED` (pinning that the sync branch's requirement was deliberately kept).
142
- - **Release-gate correction (HIGH): three disagreeing version fields.** `package.json` said
143
- `0.4.0`; `package-lock.json`'s root `"version"` said `0.3.1` (stale since before the 0.4.0
144
- bump — `npm install` never re-synced it); `src/version.ts`'s exported `VERSION` — the
145
- constant `guard.ts` and both `adapters/langgraph.ts` wrappers use to attribute every v2
146
- ledger entry's `adapter.version` field — said `0.3.0`. The release workflow
147
- (`.github/workflows/release.yml`) only ever checks the pushed tag against `package.json`, so
148
- it would have published while shipped ledger attribution was still wrong. Every v2 ledger
149
- entry produced by the shipped 0.4.0 release has been misreporting `adapter.version` as
150
- `"0.3.0"`. Fixed: all three aligned to the CURRENT `0.4.0` (no version bump as part of this
151
- fix — that is the operator's call at release time). Added
152
- `test/version-consistency.test.ts`, a new CI-run test (not only a release-time check)
153
- asserting `package.json`, `package-lock.json`'s root version (both the top-level field and
154
- its `packages[""].version` copy, which have drifted independently before), and the exported
155
- `VERSION` all agree, every run, not only at tag time.
156
- - **Release-gate finding, MEDIUM: `test/wire-vectors.test.ts` enumerated and scored 19 of the
157
- 20 published interop vectors, silently.** `reject_unsafe_integer` — anticipated in this
158
- package's own `[0.3.1]` CHANGELOG entry below ("will show 20 vectors... once the Python
159
- package ships them") and shipped by Python's own `[0.9.0]` — has had its fixture file
160
- present on disk (byte-identical to Python's) since, but `VECTOR_NAMES` itself was never
161
- updated to include it, and the test's own count assertion (`19`) masked the omission rather
162
- than catching it. Fixed: added to `VECTOR_NAMES`, the count and test name updated to `20`,
163
- and the stale `.github/workflows/ci.yml` comment ("19-vector... >=0.8 ships this") corrected
164
- to `20-vector`/`>=0.9` (Python's own `[0.9.0]` CHANGELOG entry is where
165
- `reject_unsafe_integer.json` shipped, matching the pip constraint
166
- `attenu-guard>=0.9,<0.10` already pinned two lines below that comment).
167
- - **Release-gate finding, LOW: the interop matrix had no early warning for the NEXT Python
168
- minor.** `ci.yml`'s `interop` job pins `attenu-guard>=0.9,<0.10` deliberately — the committed
169
- fixtures match that release, and the job's own last step re-generates and diffs them, so
170
- pinning to it is correct, not stale. But nothing in this repo would notice a 0.10.0 that
171
- breaks wire compatibility until someone widened that pin by hand. Added a second job,
172
- `interop-next`, that runs the same cross-language suite against `attenu-guard>=0.10,<0.11` —
173
- gated on a `pip index versions` check so it reports success without running anything while
174
- 0.10.0 is unpublished (confirmed against the live PyPI index: 0.9.0 is current, no 0.10.x
175
- yet), and starts actually exercising the suite the moment the operator ships it, with no
176
- workflow edit required either way.
177
- - **D14 — `Guard.check()` registered a `PRE_HOOK_ONLY` allow as pending, wedging `complete()`
178
- forever.** Mirrors the fix landing in the Python `attenu-guard` reference implementation
179
- (`guard.py`, same defect, same root cause): `registerPending` ran unconditionally for every
180
- `schemaVersion: 2` allow, in both the normal commit path and the `CommittedAuditError` path,
181
- with no regard for `capture`. A bare `check()` (or any explicit `capture: Capture.PRE_HOOK_ONLY`)
182
- is an honest promise of NO terminal observation — nothing is ever going to call `recordOutcome`
183
- for it — yet it was registered pending exactly like a `WRAPPER_SYNC`/`WRAPPER_ASYNC`/
184
- `FRAMEWORK_POST_HOOK` allow, so `complete()` refused forever for a node with only
185
- `PRE_HOOK_ONLY` calls. The offline verifier already treated a missing `PRE_HOOK_ONLY` outcome
186
- as merely `unobserved` (`evidence.ts`'s execution-binding report), so runtime and offline
187
- semantics disagreed. Fixed: a call is now registered pending only when its capture is one of
188
- `WRAPPER_SYNC`/`WRAPPER_ASYNC`/`FRAMEWORK_POST_HOOK` — in both the normal path and the
189
- `CommittedAuditError` path. A bare/`PRE_HOOK_ONLY` allow never enters the pending set, so
190
- `complete()` finalizes immediately, and the verifier's `unobserved` classification for it now
191
- matches the runtime's own view.
192
- - **Re-gate correction (HIGH): `freeze()` still executed attacker-controlled code BEFORE
193
- authorization, on three separate exotic-value paths, all reproduced directly before this fix.**
194
- (1) A `Proxy` is not inert under reflection: `Object.getPrototypeOf`, `Object.keys`, and
195
- `Object.getOwnPropertyDescriptor` are each real, user-definable traps — walking an ordinary
196
- Proxy through the property-descriptor logic fired four of them before authorization was ever
197
- decided, and `Array.isArray` on a REVOKED Proxy throws `TypeError` outright rather than
198
- degrading cleanly. (2) The bottom fallback still called `String(value)` for anything not a
199
- plain object/array — a boxed primitive (`new Number(...)`) with a hostile `Symbol.toPrimitive`,
200
- or a `TypedArray` with a hostile own `toString`, each ran attacker code exactly once per
201
- snapshot. (3) No general safety net: an unanticipated reflection failure would have propagated
202
- an exception out of a snapshot taken before authorization, rather than degrading. None of this
203
- was an aliasing or authorization-bypass defect — ordinary frozen/accessor-only objects never
204
- leaked references, and policy evaluation still fully controlled whether the wrapped body ran —
205
- the defect was pre-authorization code execution and exception propagation. Fixed: `freeze()`
206
- now recognizes a Proxy FIRST, via `util.types.isProxy` (an internal engine-slot check that
207
- invokes nothing, live or revoked — verified directly), before `Array.isArray` or any other
208
- reflection; every value that is not a safe JSON primitive and not a plain object/array (a
209
- Proxy, a boxed primitive, a TypedArray/`ArrayBuffer`/`SharedArrayBuffer`/`DataView`, a
210
- `Map`/`Set`/`Date`/`RegExp`, a function, a `Symbol`, a `BigInt`, or anything else) becomes the
211
- new `FREEZE_UNSUPPORTED` sentinel instead of a string — never `String()`, never any other
212
- protocol; the whole reflective walk runs inside one `try`/`catch`, degrading any other
213
- reflection failure the same way. `paramsHashReason: "unsupported"` for the whole call, never a
214
- partial commitment — the same degradation this library already uses for an out-of-domain
215
- number, not a new failure mode.
216
- - **Re-gate correction (MEDIUM): `"<accessor>"` was a real commitment collision, and so was
217
- `"<circular>"` by the identical reasoning.** Two real wrapper calls — one with an enumerable
218
- getter, one with the literal string `"<accessor>"` as an ordinary value — produced the
219
- IDENTICAL `authorizedParamsHash`, reproduced directly: an evidence-integrity ambiguity in a
220
- supposedly cryptographic commitment (two materially different inputs, one commitment), even
221
- though the getter itself correctly stayed uninvoked and this was never an authorization
222
- bypass. Audited the sibling sentinel `"<circular>"` for the exact same collision class rather
223
- than leaving it unexamined — it has the identical problem (a genuinely circular object and a
224
- plain object holding the literal string `"<circular>"` produce the same commitment); there is
225
- no reason a cycle's position makes the collision infeasible, so it gets the same fix. Both an
226
- accessor property and a genuine cycle now become `FREEZE_UNSUPPORTED` (above) — the same
227
- private `Symbol` every other unrepresentable value degrades to, which cannot equal any real
228
- call argument by construction. Regression test added: zero getter calls, no hash,
229
- `params_hash_reason: "unsupported"`; plus a direct test that a real getter-bearing object and
230
- the old literal-string sentinel no longer freeze to the same shape.
231
- - **Re-gate correction (MEDIUM): `interop-next`'s "is 0.10.x published yet?" check failed OPEN.**
232
- `set -eu` alone does not catch a failing `pip index versions` inside an
233
- `if PIPE | grep -q ...; then` compound command — a compound `if` condition is exempt from
234
- `set -e` by design, and without `pipefail` a pipeline's exit status is its LAST command's
235
- only. Reproduced directly: a simulated exit 42 from the query flowed straight through to
236
- `published=false`, the same output as "0.10.x genuinely does not exist yet" — a PyPI outage
237
- silently read as a fact about what has been released, with the step still exiting 0. Fixed by
238
- extracting the check into `tools/check-next-python-published.sh`, which captures the query's
239
- exit code in its own statement (not inside an `if`) and exits non-zero — without ever printing
240
- `false` — before reaching the match logic. Added a probe step to `ci.yml`'s `interop-next` job,
241
- run on every CI invocation, that points the script at a stub `python3` simulating a query
242
- failure and asserts the script fails rather than reporting `false`.
28
+ - `freeze()` snapshot fallback could alias live call arguments and mishandle `__proto__`/sparse arrays/hostile getters; now the sole snapshot path.
29
+ - `Guard.check()` — a bare/`PRE_HOOK_ONLY` allow no longer registers as pending, so `complete()` no longer wedges forever waiting for an outcome.
30
+ - `freeze()` ran attacker code before authorization on Proxy/boxed values; now checks `Proxy` first, returns `FREEZE_UNSUPPORTED` instead of a string.
31
+ - `freeze()` `"<accessor>"`/`"<circular>"` sentinel strings could collide with a matching real argument; both now use `FREEZE_UNSUPPORTED`.
32
+ - `interop-next` CI check — a failed `pip index versions` query silently read as "not published yet"; now fails the job instead.
243
33
 
244
34
  ### Added
245
- - **Execution binding**, opt-in per chain via `Guard.issue(agentId, authority, {schemaVersion: 2})`
246
- (schema version 1 is unchanged and remains the default): `check`/`recordDenial` now allocate a
247
- `callId` (fail-closed, with meters restored, if `crypto.randomBytes` throws) and return it on
248
- `Decision.callId`; `check` gains `authorizedParams`/`capture`/`adapter` options and refuses
249
- further calls once the node is `complete()`d (`ReasonCode.NODE_FINALIZED`).
250
- `Guard.recordOutcome(callId, bodyState, options)` binds what a body-owning wrapper observed
251
- afterwards — `returned`/`raised`/`abandoned`/`deferred`, with `errorCode` required exactly when
252
- raised. `complete()` returns a plain `boolean` on a `schemaVersion: 1` chain (byte-and-type
253
- unchanged from every prior release) and a `CompletionResult` on `schemaVersion: 2` only — see
254
- its doc comment for the JavaScript limits of its truthiness bridge, since unlike Python's
255
- `__bool__`, `if (guard.complete())` cannot be made to read `false` there — and refuses on v2
256
- while calls are pending; `revoke`/`revokeAgent` snapshot still-pending callIds onto the `kill`
257
- entry as `pending_at_kill` without clearing them, so a late `recordOutcome` after a kill is
258
- still accepted. `check`/`recordOutcome` roll back meters and re-throw on ANY pre-commit failure
259
- (not only a `crypto.randomBytes` failure), and `recordOutcome` marks a call outcomed only after
260
- its append actually commits, so a pre-commit failure leaves the call retryable. `Guard.issue`
261
- refuses `{schemaVersion: 2, auditOverwrite: true}` — the restart rule has no escape hatch on v2.
262
- Arguments are committed via `params_c14n_v1` (`params.ts`): `SHA-256(rawSalt || JCS(params))`,
263
- never the raw value; validated against the language-neutral vector file also published for
264
- Python (`test/fixtures/params_c14n_v1.json`). `verifyBundle` gains `execution_binding`: per-call
265
- observed/unobserved/unaccounted (an outcome must be bound correctly — right node, right order —
266
- to count as observed), per-node finalized/in_progress/revoked/revoked_with_pending, an aggregate
267
- clean/incomplete/failed, and `params_coverage` (computed over every valid allow) as its own axis
268
- — `{status: "not applicable"}` for a schema-version-1 bundle. `verifyBundle` also gains
269
- `root_version_mismatch`/`mixed_entry_versions` checks (a chain is created at one schema version
270
- and never mixes), requires exactly one root event (`checks.root`), does strict, null-aware,
271
- type-checked schema validation on every conditional field (`capture`/`adapter` are now
272
- REQUIRED, not merely paired, on every v2 allow — a bare `check()` with no `capture` supplied
273
- gets a truthful guard-attributed `pre_hook_only` default rather than leaving the ledger silent;
274
- any allow-only field on a `deny`, or any v2-only field on a `schemaVersion: 1` entry
275
- — `v2_field_on_v1` — is invalid), and accepts an optional `expectedAnchor`/`expectedHead` to
276
- verify against an independently retained reference point instead of only the bundle's own
277
- enclosed anchor. `AuditLog` gains `CommittedAuditError` (a
278
- post-commit persistence failure after the entry is already in the in-memory chain) and
279
- overwrite protection (constructing over a `path` that already names a non-empty ledger now
280
- throws unless `overwrite: true`). The LangGraph adapter (`adapters/langgraph`) is the reference
281
- wiring: `guardNode`/`guardTool` snapshot a call's arguments once, immutably, before invocation
282
- and call `recordOutcome` on a `schemaVersion: 2` guard, sync and async, reporting a
283
- generator/promise-like result as `deferred` and an `AbortController`-driven cancellation as
284
- `abandoned`; wrapping an async callable on a `schemaVersion: 1` guard stays byte-and-type
285
- unchanged (never itself becomes an async function). Mirrors attenu-guard (Python) 0.9.0's
286
- execution-binding layer plus its post-review merge-gate hardening, byte-for-byte on every
287
- reason-code string; ported test suite: `test/execution-binding.test.ts` (82 cases),
288
- `test/params-c14n-vectors.test.ts` (the shared parity vectors), plus adapter and evidence
289
- coverage in `test/adapter-langgraph.test.ts`/`test/evidence.test.ts`.
290
-
291
- ## [0.3.1] — 2026-08-30
35
+ - Added execution binding (`schemaVersion: 2`): `check`/`recordOutcome` bind call outcomes via `callId`; `verifyBundle` reports `execution_binding` coverage.
36
+
37
+ Full notes: https://github.com/attenu-io/attenu-guard-ts/blob/main/docs/release-notes/v0.5.0.md
38
+
39
+ ## [0.3.1] - 2026-08-30
292
40
 
293
41
  ### Fixed
294
- - Integers beyond the RFC 8785 safe range (±(2**53-1)) are now rejected — at canonicalization, at
295
- `RowLimit`/`SpendCap`/`CallLimit` construction, and by `load` (as `malformed`) instead of
296
- silently colliding with a neighbouring integer once serialized as a double. The interop suite
297
- will show 20 vectors, and `valid_jcs_big_integer`/`valid_jcs_exponent_form` will pin different
298
- values, once the Python package ships them; until then the affected interop assertions are a
299
- known, expected failure (see the README note on `npm run fixtures`).
300
- - `verifyBundle` and `AuditLog.verifyAnchor` now check the bundle/anchor schema version and chain
301
- identity instead of ignoring them, so a bundle for the wrong version or the wrong chain no
302
- longer verifies.
303
-
304
- ## [0.3.0] — 2026-08-29
42
+ - Integers beyond the RFC 8785 safe range are now rejected (canonicalization, `RowLimit`/`SpendCap`/`CallLimit`, `load`) instead of silently colliding.
43
+ - `verifyBundle` and `AuditLog.verifyAnchor` now check schema version and chain identity, so a bundle for the wrong version or chain fails to verify.
44
+
45
+ Full notes: https://github.com/attenu-io/attenu-guard-ts/blob/main/docs/release-notes/v0.3.1.md
46
+
47
+ ## [0.3.0] - 2026-08-29
305
48
 
306
49
  ### Changed
307
50
  - Scope values now use the same lowercase, dot-separated grammar as Python.
308
- Only a complete terminal `.*` wildcard is valid; it covers any depth below
309
- the segment boundary. Constructors and wire verification reject malformed
310
- scope syntax.
311
51
 
312
52
  ### Added
313
- - The two malformed-wildcard vectors from Python bring the shared interop suite
314
- to 19 vectors.
53
+ - The two malformed-wildcard vectors from Python bring the shared interop suite to 19 vectors.
54
+
55
+ Full notes: https://github.com/attenu-io/attenu-guard-ts/blob/main/docs/release-notes/v0.3.0.md
315
56
 
316
- ## [0.2.1] 2026-08-29
57
+ ## [0.2.1] - 2026-08-29
317
58
 
318
59
  ### Changed
319
60
  - `c14n` is informational; producers still emit it, while verifiers enforce RFC 8785 JCS from canonical bytes and hashes regardless of the label.
320
61
 
321
- ## [0.2.0] — 2026-08-29
62
+ Full notes: https://github.com/attenu-io/attenu-guard-ts/blob/main/docs/release-notes/v0.2.1.md
63
+
64
+ ## [0.2.0] - 2026-08-29
322
65
 
323
66
  ### Changed
324
- - **RFC 8785 JCS is the only canonicalization format.** Delegation Tokens must
325
- declare `c14n: JCS` and already carry canonical header and payload bytes.
326
- Ledger entries, anchors and evidence bundles now carry the same marker and
327
- use JCS for every hash and signature. There is no legacy or dual-format reader.
328
- - The canonicalizer now uses ECMAScript number serialization, raw Unicode and
329
- UTF-16 code-unit member ordering, and rejects non-finite numbers, duplicate
330
- members, lone surrogates, unsupported values and cyclic structures.
331
- - Cross-language fixtures now target Python `attenu-guard` 0.7 and include all
332
- 17 published interop vectors. The new reason codes are `non_finite`,
333
- `duplicate_member`, `canonicalization_required` and `non_canonical`.
67
+ - RFC 8785 JCS is now the only canonicalization format across Delegation Tokens, ledger entries, anchors, and evidence bundles; no dual-format reader.
68
+ - Canonicalizer now uses ECMAScript number serialization and UTF-16 ordering, rejecting non-finite numbers, duplicates, lone surrogates, and cycles.
69
+ - Cross-language fixtures now target Python `attenu-guard` 0.7 and include all 17 published interop vectors.
334
70
 
335
71
  ### Added
336
- - **Delegation-chain verification.** `load(tokens, signer, {rootKeyIds, now})` runs the
337
- Internet-Draft's Offline Verification Algorithm over a chain of Delegation Tokens and
338
- returns a `VerifiedChain` whose `permits` authorises against the leaf authority. All
339
- five in-scope steps are checked in the draft's order — JWS signatures with an
340
- alg-confusion guard, the `par_hash` byte commitment compared in constant time,
341
- `del_depth`/`del_max_depth` bounds, subsumption via `Authority.isNarrowerThan`, and the
342
- time claims including `exp` monotonicity along the chain — denying on the first failure
343
- with a `WireError` carrying a `WireReasonCode`. Holder binding (`cnf`/DPoP) and status-list
344
- revocation, the draft's steps 6 and 7, are out of scope in both implementations.
345
- `b64urlEncode`/`b64urlDecode` are exported alongside it.
346
- - The 17 language-independent interop vectors from the Python repository are generated
347
- into `test/fixtures/vectors/` and asserted here: the valid chain verifies and yields the
348
- leaf authority, and each adversarial chain is rejected for exactly the reason it declares.
349
-
350
- ## [0.1.1] — 2026-08-28
72
+ - Added `load(tokens, signer, {rootKeyIds, now})`, running the offline verification algorithm over a Delegation Token chain and returning a `VerifiedChain`.
73
+ - Added the 17 language-independent interop vectors from the Python repo to `test/fixtures/vectors/`; each adversarial chain is rejected for its declared reason.
74
+
75
+ Full notes: https://github.com/attenu-io/attenu-guard-ts/blob/main/docs/release-notes/v0.2.0.md
76
+
77
+ ## [0.1.1] - 2026-08-28
351
78
 
352
79
  ### Changed
353
- - Releases are published from GitHub Actions with npm trusted publishing (OIDC) and provenance attestations; no tokens are stored anywhere. Node 20 is the minimum.
80
+ - Releases are published from GitHub Actions with npm trusted publishing (OIDC) and provenance attestations; no tokens are stored anywhere.
354
81
 
355
- ## [0.1.0] — 2026-08-28
82
+ Full notes: https://github.com/attenu-io/attenu-guard-ts/blob/main/docs/release-notes/v0.1.1.md
356
83
 
357
- First release: the TypeScript implementation of attenu-guard.
84
+ ## [0.1.0] - 2026-08-28
358
85
 
359
86
  ### Added
360
-
361
- - **Offline verifier.** `verifyBundle` checks integrity, monotonicity
362
- (child ⊆ parent) and containment from an evidence bundle alone, with the
363
- signed anchor verified when a key is supplied. HS256 and Ed25519 anchors are
364
- both read. `AuditLog.verify` checks a raw `.jsonl` ledger's hash chain.
365
- `delegationGraph` and `denials` fold a bundle into the reviewer's view.
366
- - **Core enforcement.** `Authority` (scopes with `family.*` wildcards, typed
367
- ceilings, TTL), `Guard.issue`, `delegate` (the meet: scopes intersect,
368
- ceilings take the tighter bound, TTL the shorter; a request wider than the
369
- parent comes back narrowed), `check` returning a `Decision`, `enforce`
370
- throwing `AuthorityDenied`, `wouldAllow` and `wouldDelegate` as pure dry-runs,
371
- chain depth and fanout ceilings, cascade `revoke` and principal-scoped
372
- `revokeAgent`, strike policy, and a hash-chained audit log.
373
- - **Ceilings:** `RowLimit`, `SpendCap`, `CallLimit` (optionally scoped, and
374
- self-metering), `EgressRank`, `Allow`, `Deny`, `Prefix`, plus
375
- `registerCeiling` for custom ones. An unrecognised constraint type denies
376
- rather than going unenforced.
377
- - **Evidence custody.** `exportBundle` with task redaction and a strict mode
378
- that refuses to carry any field outside the published ledger allow-list.
379
- - **LangGraph.js adapter** (`attenu-guard/adapters/langgraph`): `guardTool`,
380
- `guardTools`, `guardNode`, `addGuardedNode`, `delegateTo`, `toolArgs`. The
381
- adapter never imports the framework.
382
- - **CLI:** `attenu-guard verify <file> [--hs256-key HEX | --pubkey HEX] [--kid KID]`,
383
- matching the Python CLI's output lines and exit codes.
384
- - **Cross-language fixtures.** `tools/gen_fixtures.py` generates
385
- `test/fixtures/` from the Python package; the suite reproduces every vector,
386
- and a further test has the Python CLI verify a ledger and bundle written here.
87
+ - Added `verifyBundle` (integrity, monotonicity, containment, HS256/Ed25519 anchor check), `AuditLog.verify`, and `delegationGraph`/`denials`.
88
+ - Added core enforcement: `Authority`, `Guard.issue`/`delegate`/`check`/`enforce`, chain depth/fanout ceilings, `revoke`/`revokeAgent`, audit log.
89
+ - Added ceilings: `RowLimit`, `SpendCap`, `CallLimit` (scoped, self-metering), `EgressRank`, `Allow`, `Deny`, `Prefix`, plus `registerCeiling` for custom types.
90
+ - Added `exportBundle` with task redaction and a strict mode that refuses any field outside the published ledger allow-list.
91
+ - Added the LangGraph.js adapter (`attenu-guard/adapters/langgraph`): `guardTool`, `guardNode`, `addGuardedNode`, `delegateTo`, `toolArgs`.
92
+ - Added the CLI: `attenu-guard verify <file> [--hs256-key HEX | --pubkey HEX] [--kid KID]`, matching the Python CLI's output and exit codes.
93
+ - Added cross-language fixtures: `tools/gen_fixtures.py` builds `test/fixtures/` from the Python package; the Python CLI verifies ledgers/bundles here.
387
94
 
388
95
  ### Notes
96
+ - Zero runtime dependencies.
97
+ - Canonical JSON is byte-identical to Python's `json.dumps(obj, sort_keys=True, separators=(",", ":"))`, including ASCII escaping and number formatting.
389
98
 
390
- - Zero runtime dependencies. ESM and CommonJS builds, types included, Node 18+.
391
- - Canonical JSON is byte-identical to Python's
392
- `json.dumps(obj, sort_keys=True, separators=(",", ":"))`, including ASCII
393
- escaping and number formatting. Documents that will be re-hashed are parsed
394
- with number literals preserved.
99
+ Full notes: https://github.com/attenu-io/attenu-guard-ts/blob/main/docs/release-notes/v0.1.0.md
package/README.md CHANGED
@@ -178,6 +178,17 @@ dual-signing mode. The 19 committed interop vectors include the separating cases
178
178
  for number spelling, raw Unicode, UTF-16 member ordering, large integers,
179
179
  duplicates, non-finite values and an unmarked canonical header.
180
180
 
181
+ `test/fixtures/vectors/bundles/bundle_vectors_v1.json` is the second, bundle-level
182
+ suite: whole evidence bundles for `verifyBundle`, the check an auditor runs on a
183
+ published ledger with no engine and no vendor in the loop. The token vectors pin
184
+ what a delegation token means; these pin what the LEDGER of a run has to satisfy.
185
+ A bundle verifier reports a LIST of failures rather than one reject reason, so
186
+ each rejecting case declares the minimal set of `{reason, seq, node}` that MUST
187
+ appear, at that exact position. A conformant verifier may report more, never
188
+ fewer and never elsewhere. `verifyBundle` returns those positions as
189
+ `failure_details`, the structured twin of `failures`: one
190
+ `{reason, seq, node, call_id, detail}` entry per string, in the same order.
191
+
181
192
  ## What it does not do
182
193
 
183
194
  - It does not decide what permissions a task needs. You state them; this library
@@ -18,6 +18,14 @@
18
18
  * const bundle = exportBundle(guard.auditLog(), signer);
19
19
  * const report = verifyBundle(bundle, signer);
20
20
  *
21
+ * `report.failures` is the human-readable list — its strings are a published
22
+ * contract, other implementations parse them — and `report.failure_details` is
23
+ * its machine-readable twin: one entry per string, same order, same count,
24
+ * `{reason, seq, node, call_id, detail}`. It exists so a conformance suite can
25
+ * assert WHICH check failed and WHERE, not merely that something did. The
26
+ * bundle-level interop vectors under `test/fixtures/vectors/bundles/` are scored
27
+ * against exactly that shape.
28
+ *
21
29
  * No engine state is consulted — the bundle is the whole input, which is the
22
30
  * point. This is byte-compatible with the Python library's
23
31
  * `attenu_guard.evidence`.
@@ -98,6 +106,23 @@ export interface ExportOptions {
98
106
  * is thrown on any field outside it.
99
107
  */
100
108
  export declare function exportBundle(auditLog: AuditLog | readonly LedgerEntry[], signer: Signer, options?: ExportOptions): Bundle;
109
+ /**
110
+ * One structured failure — the machine-readable twin of one `failures` string.
111
+ *
112
+ * `reason` is a stable token: the text before the first `:` in `detail`, with
113
+ * the two historical exceptions whose message names a NODE there
114
+ * (`unreadable_authority`, `unreadable_granted`) and so state their reason
115
+ * explicitly. `seq`/`node` are the offending entry's own fields, both `null`
116
+ * when the failure is chain-level with nothing single to point at. Same field
117
+ * names and same values as the Python implementation's `failure_details`.
118
+ */
119
+ export interface FailureDetail {
120
+ reason: string;
121
+ seq: Json;
122
+ node: Json;
123
+ call_id: Json;
124
+ detail: string;
125
+ }
101
126
  export interface GraphNode {
102
127
  agent: Json;
103
128
  task: Json;
@@ -165,6 +190,12 @@ export interface VerifyReport {
165
190
  ok: boolean;
166
191
  checks: VerifyChecks;
167
192
  failures: string[];
193
+ /**
194
+ * The structured twin of `failures`: same order, same count, one
195
+ * `{reason, seq, node, call_id, detail}` per string, so a conformance suite can assert the
196
+ * reason AND the position of every failure instead of matching prose.
197
+ */
198
+ failure_details: FailureDetail[];
168
199
  nodes: number;
169
200
  actions_checked: number;
170
201
  chain_id: Json;
@@ -190,6 +221,9 @@ export interface VerifyReport {
190
221
  * the bundle's actual `(seq, hash, chainId, v)` must equal it exactly, or `checks.expected_anchor`
191
222
  * reports `"FAILED"` and the mismatch lands in `failures`. `report.verified_against` names which
192
223
  * mode ran.
224
+ *
225
+ * `failure_details` is the structured twin of `failures`: same order, same count, one
226
+ * `{reason, seq, node, call_id, detail}` entry per string.
193
227
  */
194
228
  export interface VerifyBundleOptions {
195
229
  /** An independently retained anchor object to verify the bundle's actual head against. */
@@ -1 +1 @@
1
- {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/evidence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/evidence.ts"],"names":[],"mappings":"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"}