Skip to content

spec: add an assurance-neutral references block (#197) - #198

Merged
imran-siddique merged 1 commit into
mainfrom
spec-references-block
Aug 22, 2026
Merged

spec: add an assurance-neutral references block (#197)#198
imran-siddique merged 1 commit into
mainfrom
spec-references-block

Conversation

@imran-siddique

Copy link
Copy Markdown
Member

Implements the proposal in #197. Normative change, so please read that issue first for the argument.

What this fixes

A self-produced, hardware-attested Trust Record has no way to reference a fact outside itself.

origin.source_event_id is the right shape, defined as "Identifier of the source event in that system, so a record traces back to it". But §3.1.1 scopes origin to records assembled from someone else's evidence, and requires that a record whose origin.kind is not self MUST carry runtime.platform: "software-only", with verifiers rejecting it otherwise.

So today, the moment a record points outward, it stops being a hardware attested record.

Why one mechanism rather than a third field

Three open items are the same shape:

Item Wants to bind
#179 authorized pre-execution intent
#191 attributable human-approval outcomes
AAIF Observability & Traceability WG a behavioural execution identifier

Two bespoke fields is a pattern. Three is a design failure. Each becomes a registered rel value here instead.

The rule that makes it safe

The signature attests that this record points somewhere. It does not attest the truth of what it points at. The pointer is produced inside the boundary that produced the record; the target is not.

That gives three normative consequences, all in the diff:

  • references MUST NOT affect runtime.platform
  • a verifier MUST NOT reject a record because a reference cannot be resolved
  • a verifier MUST NOT treat a resolved reference as attested evidence

Without the second rule, whoever controls the target could invalidate evidence they do not hold. Without the third, this becomes the assurance laundering §3.1.1 exists to prevent.

§3.1.1 is untouched

This does not weaken that MUST. It stops it being applied to a job it was not written for. Records assembled from third-party evidence still take software-only, exactly as now.

Backward compatibility

Non-breaking. New optional field, existing records do not carry it, existing verifiers ignore unknown fields. No wire format or algorithm change. Per CONTRIBUTING a schema PR must track a merged spec change, so the schema and conformance vectors follow once the shape here is agreed rather than in this PR.

What I have deliberately left unresolved

resolver and retention are the interesting fields and I do not expect one round of review to get them right.

A reference is worth only the ability to resolve it later. That is not hypothetical: in the npm keyv compromise the poisoned packages carried valid provenance logged in a transparency service, and fifteen days on the attestations could not be fetched, because the only working index was the artifact digest and unpublishing removed the metadata holding it. Log intact, records unreachable.

TRACE has the same exposure through transparency. §7 open question 3 is the same problem for that field and I think the two want resolving together. The spec text says retention is an undertaking that nothing here enforces, rather than implying otherwise.

Sponsor

Not yet named. CONTRIBUTING says proposals are evaluated on the technical argument alone and that a normative PR without a sponsor is not rejected on that basis, so raising it this way deliberately. Happy to have a maintainer carry it, or to name a sponsor if reviewers would rather.

Closes the gap where a self-produced, hardware-attested Trust Record had no
way to point at a fact outside itself. origin.source_event_id is the right
shape but origin is scoped to assembled evidence, and a record whose
origin.kind is not self MUST carry runtime.platform software-only. So
pointing outward cost the attestation.

Adds an optional references block in a new section 3.1.2, with three
registered rel values covering the three cases that are currently the same
shape: authorized-intent (#179), approval-outcome (#191), and behavior-trace,
raised by the AAIF Observability and Traceability WG.

The rule that makes it safe: the signature attests that this record points
somewhere, not the truth of what it points at. So references MUST NOT affect
runtime.platform, a verifier MUST NOT reject a record for an unresolvable
reference, and MUST NOT treat a resolved one as attested evidence.

section 3.1.1 is unchanged. This does not weaken its MUST, it stops that MUST
being applied to a job it was not written for.

Names retention as unsettled and unenforced rather than implying otherwise,
and points at open question 3 as the same problem for transparency.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EQx4N5BzTQbY8kvXUsdkY
@imran-siddique
imran-siddique requested a review from a team as a code owner August 21, 2026 22:32

@lywinged lywinged left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things that are checkable, and one design question that I think decides the shape of the block.

schema/trace-claim.json is additionalProperties: false at the top level and this branch does not touch it, so a record carrying references does not validate. Measured on examples/tpm2.json, which validates as committed and fails with Additional properties are not allowed ('references' was unexpected) once an entry from the field table is added. Rule 2 requires the signature to cover the block, so as it stands there is no conformant record that can carry it. The suite is green at 496 passed because nothing connects the section 3.1 field table to the schema.

The shape is unstated as well. The section says "a references entry" and "an entry in references" throughout, which reads as a collection, but neither it nor the field table says whether that is an array or an object. Section 3.1.1 does not state origin's shape either, because the schema does it there. With no schema entry there is nowhere that says what a producer emits, and canonicalisation under section 3.2.2 differs between the two. The same gap leaves duplicates undefined: whether two authorized-intent entries are legal, and what a verifier does with them.

On the design. Section 3.3.2 answers the same question for external execution evidence and answers it the other way. There, a verifier configured with the issuer key MUST reject the Trust Record when the signature or the digest check fails, and a verifier without the key treats the receipt as unverified rather than invalid and SHOULD surface an advisory status rather than silently ignoring it. Rule 3 here says MUST NOT reject and MUST NOT count, with nothing surfaced. Two sections apart, the same question has two answers and no text reconciling them.

That difference lands hardest on digest. An entry carrying one is checkable in the way an external receipt is checkable, and rule 3 forbids the check from mattering. So the question I would want settled: what is a verifier permitted to do after resolving an entry and matching its digest? If the answer is nothing, digest is decorative and could come out. If it is anything at all, rule 3 needs that exception written down, and appraisal is additionalProperties: false over status, verifier, policy_ref, timestamp and provenance_depth_verified, so there is nowhere for a verifier to record that it happened.

Smaller, and worth settling now rather than at the fourth value: rel is a closed set, on the same reasoning section 3.1.1 gives for origin.kind, but there is no rule for a value a verifier does not recognise. Without one, adding a fourth rel is a breaking change for a strict verifier, which the backward-compatibility note does not cover.

@imran-siddique
imran-siddique merged commit 83c078d into main Aug 22, 2026
8 checks passed
@imran-siddique
imran-siddique deleted the spec-references-block branch August 22, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants