Skip to content

Commit 5ea7cd3

Browse files
authored
release(v0.39.2): SEP-2787 v2 envelope with full wire round-trip, audit-event schema 1.0, Qi survey mapping (#151)
The four mechanical alignments Vaara committed to in modelcontextprotocol/modelcontextprotocol#2787 after the trust-surface grouping was incorporated into the SEP draft on soup-oss commit dd030d5b ship as the v2 envelope shape: 1. toolCalls lives under payloadDerived, not plannerDeclared. Tool bindings (name, server fingerprint, args commitment) are facts derived from the request payload, not planner declarations. 2. argsProjection serialises with a JSON-stringified projection field carrying the JCS-canonical encoding of the projection object. The digest is taken over those bytes. 3. The v1 kind-discriminated union is dropped. ArgsRef and ArgsProjection self-discriminate by which fields are present. 4. Commitment-only audit composes on ArgsProjection as a hash-only-identity projection of the form {"digest": "sha256:..."}. No separate ArgsDigest type ships in the spec. parse_attestation(d) is the new wire-decode entrypoint: inverse of Attestation.to_dict(). 13 new tests cover emit -> JCS bytes -> parse -> verify across HS256, ES256, RS256 for both ArgsRef and ArgsProjection, plus parse rejection on missing-field and unsupported-alg inputs and a byte-identical re-emit check. Two doc artefacts ship in the same release: - docs/audit_event_schema.md: AUDIT-EVENT-SCHEMA-1.0, versioned wire/storage contract for the audit events Vaara emits. Independent of code version so third-party consumers can pin without coupling to a Python runtime version. - docs/qi_survey_mapping.md: Vaara surface coverage against the taxonomy in Qi et al., Towards Trustworthy Agentic AI (arXiv:2605.23989, 2026-05-17). Direct, partial, and out-of-scope rows by Perceive / Plan / Act / Reflect / Learn / Multi-agent / Long-horizon stage under both top-level dimensions. SEP-2787 reference implementation tag sep2787-ref-v2 lands on this release commit alongside v0.39.2 for cross-repo provenance. The v0.40 slot stays reserved for the deployment-shape scope (HTTP transport, multi-tenancy schema, hot-reload extended, fan-out) per project_v040_roadmap_opa_frame_20260527.md. Co-authored-by: vaaraio <[email protected]>
1 parent a61e87c commit 5ea7cd3

18 files changed

Lines changed: 1342 additions & 397 deletions

CHANGELOG.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,71 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
66

77
## [Unreleased]
88

9+
## [0.39.2] - 2026-05-27
10+
11+
**Theme: SEP-2787 envelope v2 shape, full wire round-trip, versioned
12+
audit-event schema, and Qi-survey coverage mapping.**
13+
14+
The four mechanical alignments Vaara committed to in
15+
`modelcontextprotocol/modelcontextprotocol#2787` after the
16+
trust-surface grouping was incorporated into the SEP draft on
17+
soup-oss commit `dd030d5b` ship as the v2 envelope shape:
18+
19+
1. `toolCalls` lives under `payloadDerived`, not `plannerDeclared`.
20+
Tool bindings (name, server fingerprint, args commitment) are
21+
facts derived from the request payload, not planner declarations.
22+
2. `argsProjection` serialises with a JSON-stringified `projection`
23+
field carrying the JCS-canonical encoding of the projection
24+
object. The digest is taken over those bytes.
25+
3. The v1 `kind`-discriminated union is dropped. `ArgsRef` (ref +
26+
digest) and `ArgsProjection` (projection + projectionDigest)
27+
self-discriminate by which fields are present.
28+
4. Commitment-only audit composes on `ArgsProjection` as a
29+
hash-only-identity projection of the form `{"digest":
30+
"sha256:..."}`. No separate `ArgsDigest` type ships in the spec.
31+
32+
### Added
33+
- `parse_attestation(d)` (and `sep2787_parse_attestation` from the
34+
package root): inverse of `Attestation.to_dict()`. Reconstructs the
35+
Python dataclass tree from a wire JSON dict so third-party
36+
consumers of the v0 test vectors can parse, verify, and re-emit
37+
envelopes byte-identically. Strict field-presence validation and
38+
alg allowlisting on the boundary.
39+
- `docs/audit_event_schema.md`: AUDIT-EVENT-SCHEMA-1.0, versioned
40+
wire/storage contract for the audit events Vaara emits.
41+
Independent of code version so third-party consumers can pin
42+
without coupling to a Python runtime version.
43+
- `docs/qi_survey_mapping.md`: Vaara surface coverage against the
44+
taxonomy in Qi et al., *Towards Trustworthy Agentic AI*
45+
(arXiv:2605.23989, 2026-05-17). Direct, partial, and
46+
out-of-scope rows by Perceive / Plan / Act / Reflect / Learn /
47+
Multi-agent / Long-horizon stage under both top-level dimensions.
48+
- `tests/test_attestation_sep2787_wire.py`: 13 wire round-trip tests
49+
covering `emit -> JCS bytes -> parse -> verify` across HS256,
50+
ES256, RS256 for both `ArgsRef` and `ArgsProjection`, plus parse
51+
rejection on missing-field and unsupported-alg inputs and a
52+
byte-identical re-emit check.
53+
54+
### Changed
55+
- `vaara.attestation.sep2787` emits the v2 envelope shape.
56+
- `docs/sep2787-overt-mapping.md` field table updated to v2.
57+
- `COMPLIANCE.md` "Position relative to open runtime-attestation
58+
standards" gains a SEP-2787 v2 subsection alongside the OVERT 1.0
59+
positioning.
60+
- `vaara.attestation` package docstring covers both OVERT 1.0 and
61+
SEP-2787 v2 surfaces (previously OVERT-only by omission).
62+
63+
### SEP-2787 reference implementation tag
64+
- `sep2787-ref-v2`: v2 envelope shape with the four post-soup-oss
65+
alignments and full wire round-trip. Pinned for cross-repo
66+
provenance citation against
67+
`modelcontextprotocol/modelcontextprotocol#2787` and the v0 test
68+
vector PR (`#2789`).
69+
- `sep2787-ref-v1` (preserved at commit `a61e87c`): camelCase
70+
envelope, the prior proposed-shape artefact.
71+
- `sep2787-ref-v0` (preserved at commit `3d7af54`): snake_case
72+
envelope, the historical proposed-shape artefact.
73+
974
## [0.39.1] - 2026-05-27
1075

1176
**Theme: SEP-2787 reference impl follows the spec into camelCase.**

COMPLIANCE.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,32 @@ Operators who need AAL-4 should pair Vaara with an independent
376376
attestation provider. The Vaara-emitted evidence is the input to
377377
that provider, not a replacement for it.
378378

379+
### SEP-2787 v2 tool-call attestation (v0.39.2)
380+
381+
`vaara.attestation.sep2787` ships a reference implementation of
382+
SEP-2787, a per-tool-call JSON attestation envelope carried in MCP
383+
`_meta`. The v2 envelope shape groups envelope fields under three
384+
trust-surface blocks (`plannerDeclared`, `issuerAsserted`,
385+
`payloadDerived`) with `toolCalls` as a payload-derived fact, not a
386+
planner declaration. Signing modes are HS256 (HMAC-SHA256), ES256
387+
(ECDSA P-256 raw r||s), and RS256 (RSASSA-PKCS1-v1_5). The signature
388+
is computed over the JCS-canonical encoding of the four envelope
389+
blocks `{version, alg, plannerDeclared, issuerAsserted,
390+
payloadDerived}` and is excluded from its own input.
391+
392+
The two envelopes coexist. OVERT 1.0 is the operator-side attestation
393+
kernel emitting per-action CBOR Base Envelopes. SEP-2787 is the
394+
per-tool-call JSON envelope carried inside MCP transport. A
395+
deployment can run both: the OVERT envelope binds the action chain
396+
while the SEP-2787 envelope binds the specific tool-call payload.
397+
Field-level mapping between the two lives in
398+
[`docs/sep2787-overt-mapping.md`](docs/sep2787-overt-mapping.md).
399+
400+
`parse_attestation(d)` provides full wire round-trip: a third-party
401+
consumer of the published v0 test vectors can parse JSON bytes,
402+
verify the signature, and re-emit byte-identically. The reference
403+
implementation is pinned at tag `sep2787-ref-v2`.
404+
379405
### Hardware TEE attestation hook (experimental, v0.18.0)
380406

381407
Beyond the software-signed attestation chain described above, Vaara

clients/ts/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@vaara/client",
3-
"version": "0.39.1",
3+
"version": "0.39.2",
44
"description": "TypeScript client for the Vaara HTTP API. Conformal risk scoring, hash-chained audit, policy reload, named detectors.",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

docs/audit_event_schema.md

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# Vaara Audit Event Schema, v1.0
2+
3+
Versioned wire/storage contract for the audit events that flow through
4+
the Vaara execution layer. This document is the schema; the
5+
implementation in `src/vaara/audit/trail.py` is one conforming emitter.
6+
The schema is versioned independently of the Vaara Python package so
7+
downstream consumers (compliance combiners, regulatory exports,
8+
third-party verifiers) can pin to a schema version without coupling to
9+
a runtime version.
10+
11+
## Status and scope
12+
13+
- Schema version: **1.0**
14+
- Status: stable
15+
- Applies to: events appended to `trail.jsonl` and to the JSON shape
16+
returned by the audit HTTP API.
17+
- Out of scope: the combiner that assembles per-Article evidence
18+
reports, the signer that wraps an exported trail, the verifier that
19+
walks the hash chain. Those consume conforming events and have their
20+
own contracts.
21+
22+
A conforming emitter MUST produce events that satisfy the field
23+
requirements below. A conforming consumer MUST tolerate optional
24+
fields and unknown additive fields under the rules in
25+
[§ Forward compatibility](#forward-compatibility).
26+
27+
## Event envelope
28+
29+
Every audit event is a JSON object with the following fields.
30+
31+
| Field | Type | Required | Description |
32+
|---|---|---|---|
33+
| `record_id` | UUIDv4 string | yes | Identifier unique to this single event. |
34+
| `action_id` | UUIDv4 string | yes | Groups every event that belongs to one action lifecycle (request, score, decision, execute/block, outcome). |
35+
| `event_type` | string enum | yes | Lifecycle stage. Closed set in [§ Event types](#event-types). |
36+
| `timestamp` | number | yes | Unix epoch seconds (UTC), IEEE-754 double. Finite (no NaN, no ±∞). |
37+
| `agent_id` | string | yes | Identity of the agent that submitted the action. Free-form, ≤ 256 bytes. |
38+
| `tool_name` | string | yes | Name of the tool or action under interception. ≤ 512 bytes. |
39+
| `data` | object | no | Event-specific payload. Schema by `event_type`, see [§ Data payloads](#data-payloads). Default `{}`. |
40+
| `regulatory_articles` | array of objects | no | Regulatory provenance of this event. See [§ Regulatory article objects](#regulatory-article-objects). Default `[]`. |
41+
| `previous_hash` | hex string | yes | SHA-256 of the predecessor record's `record_hash`. Empty string for the first record. |
42+
| `record_hash` | hex string | yes | SHA-256 over the canonical encoding of the hashed-fields subset of this record. See [§ Hash chain](#hash-chain). |
43+
| `system_operation` | string | no | prEN ISO/IEC 12792 transparency axis: how the AI system operated at this event. Metadata, not hashed. |
44+
| `data_usage` | string | no | prEN ISO/IEC 12792 transparency axis: what data was consumed. Metadata, not hashed. |
45+
| `decision_making` | string | no | prEN ISO/IEC 12792 transparency axis: how the conclusion was reached. Metadata, not hashed. |
46+
| `limitations` | string | no | prEN ISO/IEC 12792 transparency axis: known constraints. Usually carried out-of-band. Metadata, not hashed. |
47+
48+
## Event types
49+
50+
`event_type` is a closed enum at schema 1.0. Additive values may
51+
appear in a minor version bump; see [§ Forward compatibility](#forward-compatibility).
52+
53+
| Value | Lifecycle position |
54+
|---|---|
55+
| `action_requested` | Agent submitted an action; recorded before processing. |
56+
| `risk_scored` | Scorer produced a risk assessment with conformal prediction interval. |
57+
| `decision_made` | Allow / escalate / deny decided. |
58+
| `action_executed` | Action was actually executed downstream. |
59+
| `action_blocked` | Action was blocked before execution. |
60+
| `escalation_sent` | Action routed to human reviewer. |
61+
| `escalation_resolved` | Human reviewer responded. |
62+
| `outcome_recorded` | Post-execution outcome observed and recorded. |
63+
| `policy_override` | Manual override of a prior automated decision. |
64+
65+
Each event for one action references a single shared `action_id`. The
66+
canonical lifecycle is `action_requested``risk_scored`
67+
`decision_made` → (`action_executed` | `action_blocked` |
68+
`escalation_sent``escalation_resolved`) → `outcome_recorded`.
69+
`policy_override` may appear at any point after `decision_made`.
70+
71+
## Hash chain
72+
73+
The chain is SHA-256 over a canonical JSON encoding of a strict subset
74+
of the record. Encoding: `json.dumps(content, sort_keys=True,
75+
separators=(",", ":"), allow_nan=False)`.
76+
77+
Fields included in `content`: `record_id`, `action_id`, `event_type`
78+
(as string), `timestamp`, `agent_id`, `tool_name`, `data`,
79+
`regulatory_articles`, `previous_hash`.
80+
81+
Fields excluded: `system_operation`, `data_usage`, `decision_making`,
82+
`limitations`. Rationale: the four prEN ISO/IEC 12792 transparency
83+
annotations may evolve with the WG4 draft. Excluding them keeps
84+
records hash-stable under re-emission and avoids coupling chain
85+
integrity to a moving annotation schema. A future major schema may
86+
introduce a separate signed-bundle mechanism for transparency tagging.
87+
88+
`previous_hash` of the first record is the empty string `""`.
89+
`record_hash` is computed at append time and stable across
90+
re-serialization.
91+
92+
## Regulatory article objects
93+
94+
`regulatory_articles` is an array of objects. Each object has:
95+
96+
| Field | Type | Required | Description |
97+
|---|---|---|---|
98+
| `domain` | string | yes | Regulatory regime: `EU_AI_ACT`, `DORA`, `NIS2`, `MiFID_II`, `GDPR`. |
99+
| `article` | string | yes | Article reference, e.g. `Article 12(1)` or `Article 9(2)(a)`. |
100+
| `requirement` | string | yes | What the article requires. |
101+
| `how_satisfied` | string | yes | How this event satisfies the requirement. |
102+
103+
The combiner uses `regulatory_articles` to assemble per-Article
104+
evidence reports. Including the regulatory provenance in the
105+
hash-covered fields means that tampering with article attribution
106+
after the fact breaks chain verification.
107+
108+
## Data payloads
109+
110+
`data` carries event-specific structured fields. The schema by
111+
`event_type` is non-exhaustive at 1.0 (consumers MUST accept unknown
112+
keys). Reserved top-level keys:
113+
114+
- `action_requested`: `action_request` (object), `context` (object, optional).
115+
- `risk_scored`: `risk_score` (number in [0, 1]), `interval` (`[low, high]`), `classifier_version` (string), `contributing_signals` (array).
116+
- `decision_made`: `decision` (`allow` | `escalate` | `deny`), `threshold_set` (string), `verdict_inputs` (array).
117+
- `action_executed`: `executor` (string), `duration_ms` (number).
118+
- `action_blocked`: `reason` (string), `blocking_policy` (string).
119+
- `escalation_sent`: `reviewer_queue` (string), `priority` (string).
120+
- `escalation_resolved`: `reviewer_id` (string), `decision` (enum), `reason` (string).
121+
- `outcome_recorded`: `outcome` (`success` | `failure` | `partial` | `unknown`), `feedback` (object, optional).
122+
- `policy_override`: `overrider_id` (string), `prior_decision` (enum), `new_decision` (enum), `reason` (string).
123+
124+
Caller-controlled strings in `data` (`agent_id`, `reason`,
125+
`override_reason`) MUST be treated as untrusted at narrative-rendering
126+
time. The hash chain still covers original values; the renderer
127+
sanitizes for display only.
128+
129+
## Numeric and string discipline
130+
131+
- `timestamp` is IEEE-754 double Unix epoch seconds, UTC. NaN, +∞, -∞
132+
are rejected at the canonical-JSON boundary.
133+
- Strings are UTF-8. ≤ 256 bytes for `agent_id`, ≤ 512 bytes for
134+
`tool_name`. `record_id` and `action_id` are UUIDv4 in canonical form.
135+
136+
## Wire and storage encodings
137+
138+
JSONL on disk: one event per line, sorted keys, no trailing
139+
whitespace. The hash chain is computed and verified against this
140+
encoding. JSON over HTTP: the audit API returns events as JSON
141+
objects, optionally wrapped in a pagination envelope; the event
142+
object itself is byte-identical to the JSONL line modulo whitespace.
143+
Other encodings (CBOR, Protobuf) may be defined in sibling specs
144+
without bumping this version, provided they round-trip to the
145+
canonical JSON form.
146+
147+
## Signing and export
148+
149+
A trail is exported as a zip bundle: `trail.jsonl`, `manifest.json`,
150+
`trail.sig`, `signer_pubkey`. Signed message:
151+
`SHA-256(trail.jsonl || manifest.json)`. Reference signing algorithms:
152+
Ed25519 (default), ML-DSA-65 (FIPS 204). This schema does not
153+
constrain the signing algorithm beyond requiring that the export
154+
bundle carry the public key in a form the verifier can consume.
155+
156+
## Forward compatibility
157+
158+
- **Minor (1.x).** Additive only: new `event_type` values, new
159+
optional fields, new entries inside `data`. Consumers MUST tolerate
160+
unknown fields.
161+
- **Major (2.0+).** Breaking changes: field removal, renames, change
162+
of hash-input set, change of canonical encoding. Major bumps ship
163+
with a migration note and the prior schema remains a valid emission
164+
target for one release cycle.
165+
166+
Producers SHOULD include a schema version tag in the export manifest.
167+
Events themselves do not carry a per-record schema field, since the
168+
chain integrity guarantee covers re-emission only under the same
169+
schema version.
170+
171+
## Relation to OVERT 1.0 and SEP-2787
172+
173+
OVERT 1.0 Base Envelopes (`vaara.attestation.overt`) are per-action
174+
attestations encoded as deterministic CBOR. SEP-2787 v2 envelopes
175+
(`vaara.attestation.sep2787`) are per-tool-call JSON envelopes carried
176+
in MCP `_meta`. This audit-event schema is the full per-event
177+
lifecycle log. The three coexist: an OVERT or SEP-2787 envelope can
178+
back-link to the audit event that recorded the same action via
179+
`record_id`. See `docs/sep2787-overt-mapping.md` for the
180+
OVERT ↔ SEP-2787 field mapping.
181+
182+
## Reference implementation
183+
184+
- `src/vaara/audit/trail.py`, `signer.py`, `verify.py`, `export.py`.
185+
- `src/vaara/server/schemas.py` (pydantic HTTP wire models).
186+
187+
The reference implementation pins this schema at version 1.0. A
188+
conforming third-party emitter or consumer may target this document
189+
without coupling to the Python implementation.

0 commit comments

Comments
 (0)