Skip to content
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ Format: [Semantic Versioning](https://semver.org/). Spec versions follow `MAJOR.

## [Unreleased]

- Propose explicit verifier profile declarations and verifier-result field requirements for obligations 2 and 3 of #116, carried by Imran Siddique following the discussion with @lywinged. Obligations 1 and 4 remain deferred; the v0.1 cutover is unchanged.
- **Breaking, proposed: `silent` mode allows a policy-denied action (#143, following #28).** Section 4.3 now says a deny MUST NOT block in `silent` mode and the audit chain MUST record every would-have-denied decision. v0.9.0 and v0.10.0 described `silent` as evaluated and enforced under the same `eat_profile`, so a producer that followed that text and blocks on deny no longer conforms, and a `silent` record does not say which meaning it was written under. Classified breaking under GOVERNANCE.md, so adoption needs the 30-day comment period and Project Lead sign-off. Section 4.3 also orders the modes: `advisory` is not weaker than `silent`. Enforce remains the default; schema constraints and runtime behavior are unchanged. Classification raised by @lywinged.

- **Specification precedence and reproducible conformance claims (#247).** The
v0.2 draft now states that normative text governs meaning and conformance;
disagreements in the schema, reference model or explanatory documentation are
defects in those supporting artifacts. Schema validation alone is not complete
conformance, and a normative gap needs a specification decision. Conformance
reports identify the specification revision and schema artifact, including its
digest, plus the verifier or suite version and assessed level where used. This
adds reporting requirements without changing Trust Record fields or verifier
acceptance behavior. Based on the maintainer ruling and producer feedback from
@chernistry and @lywinged in #247.

## [0.11.0] - 2026-09-25

- **Breaking:** `TraceSandboxAdapter` and `TraceAGTAdapter` require `enforcement_mode`;
Expand Down
6 changes: 4 additions & 2 deletions docs/integration/openshell.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ bundle:
single `policy.enforcement_mode` is the weakest mode among the layers:

- `enforce` only when both layers enforce;
- `advisory` when either layer evaluates without blocking;
- `silent` when a layer enforces while suppressing operational logs.
- `advisory` when either layer evaluates without blocking, or enforces while
suppressing operational logs, since no mode states that combination and
`advisory` understates it rather than overstating it;
- `silent` when either layer runs in `silent` mode.

## Transcript binding

Expand Down
2 changes: 1 addition & 1 deletion docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Binds the governance policy in force during this session.
| Field | Type | Required | Description |
|---|---|---|---|
| `bundle_hash` | string | **yes** | `sha256:` digest of the Cedar policy bundle bytes |
| `enforcement_mode` | string | **yes** | One of: `enforce` (evaluated, blocked on deny), `advisory` (evaluated, logged, allowed), `silent` (evaluated and enforced with operational logs suppressed; the audit chain still records every would-have-denied decision), `declared` (the policy is named and bound into the signed record and nothing evaluated it: the honest value for a producer with no policy engine, never a default, and not evidence that any rule was checked). Defaults to `enforce`. Section 4.3 of the spec defines `enforce`, `silent` and `declared`; `advisory` is in the schema's closed set and its one-line meaning is the schema's own description, not spec text |
| `enforcement_mode` | string | **yes** | One of: `enforce` (evaluated, blocked on deny), `advisory` (evaluated, logged, allowed), `silent` (evaluated and allowed, including on deny, with operational logs suppressed; the audit chain still records every would-have-denied decision), `declared` (the policy is named and bound into the signed record and nothing evaluated it: the honest value for a producer with no policy engine, never a default, and not evidence that any rule was checked). Defaults to `enforce`. Section 4.3 of the spec defines `enforce`, `silent` and `declared`; `advisory` is in the schema's closed set and its one-line meaning is the schema's own description, not spec text |
| `version` | string | no | Policy bundle version string |
| `policy_uri` | string | no | URI to the policy bundle for inspection |

Expand Down
6 changes: 5 additions & 1 deletion docs/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ A signature proves a statement came from the trusted key. It does not establish

### Step 4: Check the EAT profile

The current SDK requires `tag:agentrust-io.com,2026:trace-v0.2`. The superseded v0.1 identifier is rejected. This check is already included in `verify_record`; a custom verifier must also enforce its supported profile.
[Section 3.3](../spec/trace-v0.2.md#33-verification) requires a nonempty `accepted_profiles` set containing only profiles whose schemas and verification semantics the verifier implements. Reject an unsupported member anywhere in that declaration, even if the record names a supported profile. Also reject a record whose `eat_profile` is outside the declared set.

A successful verification result records both the verified `profile` and the complete `accepted_profiles` set configured at verification time. Retaining only the record's profile loses which other profiles the verifier claimed to support. These fields belong to the verifier's result; they do not alter the signed record.

The current SDK accepts only `tag:agentrust-io.com,2026:trace-v0.2` and rejects the superseded v0.1 identifier. `verify_record` enforces these checks and returns `result.profile` and `result.accepted_profiles`. Obligations 1 and 4 of [#116](https://github.com/agentrust-io/trace-spec/issues/116), emitter version declarations and downgrade disclosure, remain deferred.

### Step 5: Appraise the claims

Expand Down
2 changes: 1 addition & 1 deletion schema/trace-claim-v0.3-draft.json
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@
"declared"
],
"default": "enforce",
"description": "How the policy named by bundle_hash related to this execution. enforce: evaluated and blocked on deny. advisory: evaluated, logged, allowed. silent: evaluated and enforced with operational logs suppressed; the audit chain still records every would-have-denied decision. declared: the policy is named and bound into the signed record and NOTHING evaluated it, which is the honest value for a producer with no policy engine, such as an agent framework. Gateways MUST default to enforce. A deployment MUST explicitly configure silent. declared is never a default, a producer that evaluates policy MUST NOT use it, and a consumer MUST NOT read it as evidence that any rule was checked."
"description": "How the policy named by bundle_hash related to this execution. enforce: evaluated and blocked on deny. advisory: evaluated, logged, allowed. silent: evaluated and allowed, including on deny, with operational logs suppressed; the audit chain still records every would-have-denied decision. declared: the policy is named and bound into the signed record and NOTHING evaluated it, which is the honest value for a producer with no policy engine, such as an agent framework. Gateways MUST default to enforce. A deployment MUST explicitly configure silent. declared is never a default, a producer that evaluates policy MUST NOT use it, and a consumer MUST NOT read it as evidence that any rule was checked."
},
"version": {
"type": "string",
Expand Down
2 changes: 1 addition & 1 deletion schema/trace-claim.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
"declared"
],
"default": "enforce",
"description": "How the policy named by bundle_hash related to this execution. enforce: evaluated and blocked on deny. advisory: evaluated, logged, allowed. silent: evaluated and enforced with operational logs suppressed; the audit chain still records every would-have-denied decision. declared: the policy is named and bound into the signed record and NOTHING evaluated it, which is the honest value for a producer with no policy engine, such as an agent framework. Gateways MUST default to enforce. A deployment MUST explicitly configure silent. declared is never a default, a producer that evaluates policy MUST NOT use it, and a consumer MUST NOT read it as evidence that any rule was checked."
"description": "How the policy named by bundle_hash related to this execution. enforce: evaluated and blocked on deny. advisory: evaluated, logged, allowed. silent: evaluated and allowed, including on deny, with operational logs suppressed; the audit chain still records every would-have-denied decision. declared: the policy is named and bound into the signed record and NOTHING evaluated it, which is the honest value for a producer with no policy engine, such as an agent framework. Gateways MUST default to enforce. A deployment MUST explicitly configure silent. declared is never a default, a producer that evaluates policy MUST NOT use it, and a consumer MUST NOT read it as evidence that any rule was checked."
},
"version": {
"type": "string",
Expand Down
53 changes: 52 additions & 1 deletion spec/trace-v0.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,49 @@

> **Note:** This is a pre-ratification draft. Fields, wire formats, and conformance requirements are subject to change before v1.0. Send feedback to: open an issue on this repository.

## Authority and conformance claims

<!-- CHANGED: #247 - define specification precedence and identify conformance artifacts -->

This specification defines the meaning of TRACE claims and the requirements for
conformance. Normative companion specifications apply within the scope this
specification assigns them. The following rules govern the relationship between
the specification and its supporting artifacts:

- `schema/trace-claim.json` defines the machine-readable validation constraints.
Passing schema validation alone MUST NOT be represented as establishing TRACE
conformance: semantic requirements and verification checks also apply. Schema
descriptions do not add or override normative requirements.
- The reference model in `src/agentrust_trace/models.py` is an implementation of
those requirements, not an independent source of requirements.
- `docs/*.md`, including proposals under `docs/rfcs/`, explains the specification
and MUST NOT override its normative requirements.

Where the schema, reference model or explanatory documentation disagrees with
the normative specification, the disagreement MUST be treated as a defect in
the supporting artifact. Neither accepting the union nor requiring the
intersection of conflicting implementations resolves the normative rule. A
schema is too narrow or too broad when it rejects or admits records contrary to
the specification's structural validation constraints. Requirements enforced by
verification, including signature and freshness checks, remain separate. Where
normative text is silent, a rule found only in an implementation, schema description or proposal
MUST NOT be promoted to a normative requirement without the specification
change process. Such a coverage gap requires a specification decision.

A conformance claim MUST identify the specification version and exact revision
being claimed, together with the schema artifact used for validation. For a
published release, record the release tag or package version and a digest of
the schema bytes; for an unreleased checkout, record the full commit identifier
and schema digest. Identify the verifier or conformance-suite version and the
level assessed when either is used. These are accompanying reporting details,
not new Trust Record fields. A schema-only result MUST be described as schema
validation, not as complete conformance.

TRACE v0.2 is a draft: `main` can differ from a published package. A finding
against one revision MUST NOT be presented as a finding against another without
checking the applicable requirements and artifacts. Reporting a schema version
does not make that schema authoritative over the normative specification.

## Changes from v0.1

One normative change, and it is breaking.
Expand Down Expand Up @@ -462,6 +505,12 @@ Any party, browser, CLI, in-cluster verifier, third-party auditor, verifies:
7. SLSA provenance resolves to a trusted builder.
8. The record-signing key is not revoked as of the entry the record was logged at, per section 3.2.3. A verifier holding no revocation bundle, or only an expired one, reports that rather than treating it as a pass.

<!-- CHANGED: #116 - verifier profile declarations and verifier-result fields -->

**Verifier profile compatibility.** A verifier MUST declare a nonempty `accepted_profiles` set of profile identifiers whose schemas and verification semantics it implements. A declaration containing an unimplemented profile MUST be refused, even when the incoming record names another implemented profile. A verifier MUST refuse a record whose `eat_profile` is outside its declared set. These requirements do not override the v0.1 cutover in "Changes from v0.1".

For each successful verification, the verifier MUST report the verified `profile` and the complete `accepted_profiles` set as verifier-result fields, recording the set configured at verification time. `profile` MUST identify the record's signed `eat_profile`. These fields belong to the verifier's result; they are not new claims added to the signed input record.

No callback to the issuer. No vendor in the trust path beyond silicon root and transparency log operators.

#### 3.3.1 Build provenance verification depth
Expand Down Expand Up @@ -717,7 +766,9 @@ TRACE is a **profile**, not a parallel stack. It binds existing primitives into

These components exist in their respective ecosystems. TRACE adds the binding rule that places each into a hardware-attested envelope:

- **`policy` claim.** Policy artifacts (OPA bundles, Cedar policies, custom DSLs) and policy hashing are established. TRACE adds the binding: the policy bundle hash is sealed to the TEE measurement, the enforcement mode is recorded, and substituting the policy invalidates the runtime claim. Gateways MUST default `enforcement_mode` to `enforce`. A deployment MUST explicitly configure `silent` mode; `silent` MUST NOT be the default. In `silent` mode, the audit chain still records every would-have-denied decision; only operational log lines are suppressed.
<!-- CHANGED: #143 - silent allows deny decisions while retaining audit evidence -->

- **`policy` claim.** Policy artifacts (OPA bundles, Cedar policies, custom DSLs) and policy hashing are established. TRACE adds the binding: the policy bundle hash is sealed to the TEE measurement, the enforcement mode is recorded, and substituting the policy invalidates the runtime claim. Gateways MUST default `enforcement_mode` to `enforce`. A deployment MUST explicitly configure `silent` mode; `silent` MUST NOT be the default. In `silent` mode, a policy deny MUST NOT block the action. The audit chain MUST still record every would-have-denied decision; only operational log lines are suppressed. Where modes are compared, `enforce` is the strongest and `silent` the weakest: `advisory` is not weaker than `silent`, because both allow a denied action and `silent` also suppresses operational logs.

**`enforcement_mode: "declared"`.** The three modes above all assert that *something evaluated the policy*. `declared` asserts less: the policy is named and bound into the signed record, and nothing evaluated it. That is not a corner case, it is the common one for a producer with no policy engine: an agent framework observed by an adapter has a policy the operator declares and no evaluation of it anywhere, and with only three values such a record had to claim an evaluation that never happened.

Expand Down
8 changes: 5 additions & 3 deletions src/agentrust_trace/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,11 @@ class PolicyInfo(_TraceModel):
"""How the policy named by ``bundle_hash`` related to this execution.

The first three all assert that **something evaluated the policy**:
``enforce`` acted on the result, ``advisory`` did not, ``silent`` acted and
suppressed the log lines. ``declared`` asserts less than any of them: the
policy is named and bound into the signed record, and nothing evaluated it.
``enforce`` blocked on deny, ``advisory`` logged and allowed, and ``silent``
allowed even on deny while suppressing operational log lines. The audit
chain still records every would-have-denied decision in ``silent`` mode.
``declared`` asserts less than any of them: the policy is named and bound
into the signed record, and nothing evaluated it.

That case is not hypothetical, it is the common one. An agent framework has
no policy engine, so a record produced by observing a LangChain or LlamaIndex
Expand Down
2 changes: 1 addition & 1 deletion src/agentrust_trace/schema/trace-v0.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
"declared"
],
"default": "enforce",
"description": "How the policy named by bundle_hash related to this execution. enforce: evaluated and blocked on deny. advisory: evaluated, logged, allowed. silent: evaluated and enforced with operational logs suppressed; the audit chain still records every would-have-denied decision. declared: the policy is named and bound into the signed record and NOTHING evaluated it, which is the honest value for a producer with no policy engine, such as an agent framework. Gateways MUST default to enforce. A deployment MUST explicitly configure silent. declared is never a default, a producer that evaluates policy MUST NOT use it, and a consumer MUST NOT read it as evidence that any rule was checked."
"description": "How the policy named by bundle_hash related to this execution. enforce: evaluated and blocked on deny. advisory: evaluated, logged, allowed. silent: evaluated and allowed, including on deny, with operational logs suppressed; the audit chain still records every would-have-denied decision. declared: the policy is named and bound into the signed record and NOTHING evaluated it, which is the honest value for a producer with no policy engine, such as an agent framework. Gateways MUST default to enforce. A deployment MUST explicitly configure silent. declared is never a default, a producer that evaluates policy MUST NOT use it, and a consumer MUST NOT read it as evidence that any rule was checked."
},
"version": {
"type": "string",
Expand Down
Loading