From 3f85de8d38cfa79ecb67828f57187207f324b8ec Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Wed, 23 Sep 2026 13:31:34 -0700 Subject: [PATCH 1/5] docs(spec): define precedence and identify conformance artifacts Signed-off-by: Imran Siddique --- CHANGELOG.md | 11 +++++++++++ spec/trace-v0.2.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78849932..f5e30395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,17 @@ Format: [Semantic Versioning](https://semver.org/). Spec versions follow `MAJOR. ### Added +- **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. + - **`condition-appraisal` is a registered `references` relation, and the registry says what it is.** #191, #209, #220 and later #321 each asked for a way to bind a governance fact held outside the record, and #226 read them as one question about the `rel` registry of section 3.1.2. This settles the part ruled on there: the registry is informative and open, the verifier rules of section 3.1.2 stay normative and section 3.1.2 now says so below them, and `docs/references-registry.md` holds each registered value's referenced object, what a relying party may establish from a resolved one, and the steps by which a name is added. The fourth value, `condition-appraisal`, is an independent check's finding on whether a stated condition is established by a stated subject: a test run, a schema validation, a contract check. Its referenced object binds the condition and the subject by digest, names its issuer and key, carries the outcome in the issuer's own closed vocabulary, and signs the canonical bytes of the rest; the reference's `digest` is over the object as retained. What a relying party may establish is bounded to the object: that the cited bytes are the cited bytes and, where it holds the issuer's key, that the issuer signed them; neither reaches the record, a `pass` is not attested evidence that the condition held, and a `fail` is not a finding against the record. `examples/condition-appraisal/` carries five signed records from a seeded generator against an appraisal store: confirmed, altered after issue, a fail that verifies exactly as the pass does, unresolvable, and an issuer whose key the relying party does not hold; `tests/test_condition_appraisal_fixtures.py` recomputes every verdict from the committed bytes and re-runs the generator against them. Re-running a cited check is a separate proposal and no part of this change. Proposed by @nutstrut on #226; ruled on by @imran-siddique. Informative: no requirement, schema shape or wire format changes; the schema's `rel` description gains the name. - **The schema, the reference model and the schema reference carry the reproducibility claim (section 3.1.4).** `schema/trace-claim.json` and its packaged copy gain the optional top-level `reproducibility` object: `function`, `code_identity`, an optional `code_resolver`, `input_closure` as `{id, digest, resolver}` entries with every member required, and `transcript_digest`. `appraisal` gains `method`, the closed set `re-execution`, and `re_execution`: `outcome` of `reproduced`, `diverged` or `not-attempted`, `observed_digest`, `reason`, and an optional `verifier_code_identity`. The three presence rules the section states are schema conditionals and model validators alike: `re_execution` is present exactly when `method` is `re-execution`, `observed_digest` is required on `diverged`, `reason` is required on `not-attempted`. `models.py` adds `Reproducibility`, `ClosureEntry` and `ReExecution`; `docs/schema.md` documents every member in its section. `examples/reproducibility-claim/` holds 21 signed vectors from a seeded key: five accepted records, a claim with no result and one result per outcome, and sixteen rejected ones, two per rule, with the transcript and closure digests recomputable from the JSON they are taken over. Tracks #364; closes #366. diff --git a/spec/trace-v0.2.md b/spec/trace-v0.2.md index 1edae66c..da760824 100644 --- a/spec/trace-v0.2.md +++ b/spec/trace-v0.2.md @@ -11,6 +11,48 @@ > **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 + + + +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 that rejects a record permitted by the specification is too narrow; one +that admits a record prohibited by it is too broad. 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. From 70f0dfe68e4719ff2d467dcc18c18fd91c51b6a7 Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Wed, 23 Sep 2026 21:55:21 -0700 Subject: [PATCH 2/5] docs: separate structural schema constraints from verification Signed-off-by: Imran Siddique --- spec/trace-v0.2.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/trace-v0.2.md b/spec/trace-v0.2.md index da760824..95c8b40e 100644 --- a/spec/trace-v0.2.md +++ b/spec/trace-v0.2.md @@ -33,9 +33,10 @@ 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 that rejects a record permitted by the specification is too narrow; one -that admits a record prohibited by it is too broad. Where normative text is -silent, a rule found only in an implementation, schema description or proposal +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. From 4c9d6bc0b7c7083ef1fdd5f138221d2bc0ab63b5 Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Thu, 24 Sep 2026 16:44:06 -0700 Subject: [PATCH 3/5] docs: clarify verifier profiles and silent policy mode Signed-off-by: Imran Siddique --- docs/schema.md | 2 +- docs/verification.md | 6 +++++- schema/trace-claim-v0.3-draft.json | 2 +- schema/trace-claim.json | 2 +- spec/trace-v0.2.md | 6 +++++- src/agentrust_trace/models.py | 8 +++++--- src/agentrust_trace/schema/trace-v0.2.json | 2 +- 7 files changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/schema.md b/docs/schema.md index da6ecb81..3d817216 100644 --- a/docs/schema.md +++ b/docs/schema.md @@ -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 | diff --git a/docs/verification.md b/docs/verification.md index b2938a5d..65831577 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -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 statement 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 diff --git a/schema/trace-claim-v0.3-draft.json b/schema/trace-claim-v0.3-draft.json index 93f48d12..5706137b 100644 --- a/schema/trace-claim-v0.3-draft.json +++ b/schema/trace-claim-v0.3-draft.json @@ -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", diff --git a/schema/trace-claim.json b/schema/trace-claim.json index da180f98..b73f7f0b 100644 --- a/schema/trace-claim.json +++ b/schema/trace-claim.json @@ -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", diff --git a/spec/trace-v0.2.md b/spec/trace-v0.2.md index 6bde2f28..87948281 100644 --- a/spec/trace-v0.2.md +++ b/spec/trace-v0.2.md @@ -505,6 +505,10 @@ 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. +**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 include the verified `profile` and the complete `accepted_profiles` set in its verification statement, recording the set configured at verification time. `profile` MUST identify the record's signed `eat_profile`. These are verifier-result fields, 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 @@ -760,7 +764,7 @@ 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. +- **`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. **`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. diff --git a/src/agentrust_trace/models.py b/src/agentrust_trace/models.py index eafba8da..78440b06 100644 --- a/src/agentrust_trace/models.py +++ b/src/agentrust_trace/models.py @@ -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 diff --git a/src/agentrust_trace/schema/trace-v0.2.json b/src/agentrust_trace/schema/trace-v0.2.json index da180f98..b73f7f0b 100644 --- a/src/agentrust_trace/schema/trace-v0.2.json +++ b/src/agentrust_trace/schema/trace-v0.2.json @@ -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", From 17e419c00aefffcf54cc6fd4b411af5d0d52b793 Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Thu, 24 Sep 2026 20:54:48 -0700 Subject: [PATCH 4/5] docs: record profile and silent-mode rulings in change history Signed-off-by: Imran Siddique --- CHANGELOG.md | 3 +++ spec/trace-v0.2.md | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4332577..f5493902 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ Format: [Semantic Versioning](https://semver.org/). Spec versions follow `MAJOR. ## [Unreleased] +- Propose explicit verifier profile declarations and verification-statement 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. +- Correct silent-mode descriptions to allow policy-denied actions while preserving audit evidence, following the #28 behavior and the maintainer ruling on #143. Enforce remains the default. Schema constraints and runtime behavior are unchanged. + - Correct regulatory context in the published v0.1/v0.2 introductions and LIMITATIONS.md (#328, following #314). Separate Article 12 logging capability from TRACE tamper-evidence, identify Article 11 / Annex IV as documentation context, correct the applicability dates and remove unsupported TRACE-level compliance claims. Editorial only; reported by @ioanavalea with review context from @lywinged. - Propose v0.3 MCP profile requirements for producer-defined attempts, signed full declaration snapshots and explicit retry outcomes (#324). Clarify existing session wording without changing v0.2 validation. Proposed by @madeinplutofabio with declaration-binding refinements from @Mayur021; maintainer-carried proposal, not adopted or implemented. - Propose MCP Server Provenance v2 with normalized behavioral-hint binding (#406). diff --git a/spec/trace-v0.2.md b/spec/trace-v0.2.md index 87948281..01f7e0fc 100644 --- a/spec/trace-v0.2.md +++ b/spec/trace-v0.2.md @@ -505,6 +505,8 @@ 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. + + **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 include the verified `profile` and the complete `accepted_profiles` set in its verification statement, recording the set configured at verification time. `profile` MUST identify the record's signed `eat_profile`. These are verifier-result fields, not new claims added to the signed input record. @@ -764,6 +766,8 @@ 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, 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. **`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. From 185d3283ec4b65d44dad50ad6cdf54a1938f8355 Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Fri, 25 Sep 2026 16:34:14 -0700 Subject: [PATCH 5/5] docs: classify the silent-mode change as breaking and order the modes Section 4.3 adds a MUST NOT that v0.9.0 and v0.10.0 described the opposite of, under the same eat_profile, so the CHANGELOG now classifies it as breaking rather than a description correction. The section also states that advisory is not weaker than silent, and the OpenShell guide maps an enforcing layer that suppresses logs to advisory. Section 3.3 uses "verifier-result fields" for what a successful verification reports, instead of the undefined "verification statement". Raised by @lywinged in review. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Imran Siddique --- CHANGELOG.md | 4 ++-- docs/integration/openshell.md | 6 ++++-- docs/verification.md | 2 +- spec/trace-v0.2.md | 6 +++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5493902..02618692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,8 @@ Format: [Semantic Versioning](https://semver.org/). Spec versions follow `MAJOR. ## [Unreleased] -- Propose explicit verifier profile declarations and verification-statement 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. -- Correct silent-mode descriptions to allow policy-denied actions while preserving audit evidence, following the #28 behavior and the maintainer ruling on #143. Enforce remains the default. Schema constraints and runtime behavior are unchanged. +- 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. - Correct regulatory context in the published v0.1/v0.2 introductions and LIMITATIONS.md (#328, following #314). Separate Article 12 logging capability from TRACE tamper-evidence, identify Article 11 / Annex IV as documentation context, correct the applicability dates and remove unsupported TRACE-level compliance claims. Editorial only; reported by @ioanavalea with review context from @lywinged. - Propose v0.3 MCP profile requirements for producer-defined attempts, signed full declaration snapshots and explicit retry outcomes (#324). Clarify existing session wording without changing v0.2 validation. Proposed by @madeinplutofabio with declaration-binding refinements from @Mayur021; maintainer-carried proposal, not adopted or implemented. diff --git a/docs/integration/openshell.md b/docs/integration/openshell.md index 664095d4..bd8a65bd 100644 --- a/docs/integration/openshell.md +++ b/docs/integration/openshell.md @@ -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 diff --git a/docs/verification.md b/docs/verification.md index 65831577..e7d1a3c6 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -24,7 +24,7 @@ A signature proves a statement came from the trusted key. It does not establish [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 statement 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. +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. diff --git a/spec/trace-v0.2.md b/spec/trace-v0.2.md index 01f7e0fc..29e0e2c8 100644 --- a/spec/trace-v0.2.md +++ b/spec/trace-v0.2.md @@ -505,11 +505,11 @@ 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. - + **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 include the verified `profile` and the complete `accepted_profiles` set in its verification statement, recording the set configured at verification time. `profile` MUST identify the record's signed `eat_profile`. These are verifier-result fields, not new claims added to the signed input record. +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. @@ -768,7 +768,7 @@ These components exist in their respective ecosystems. TRACE adds the binding ru -- **`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. +- **`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.