Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Format: [Semantic Versioning](https://semver.org/). Spec versions follow `MAJOR.

## [Unreleased]

- **Proposal: `references` entries validated by recomputation, and `position-commitment` registered (#321). Version gate for v0.3.** Section 3.1.2 modelled every reference as a fact a party holds and a verifier fetches, so `resolver` was required and rule 4 told a producer with no obliged party to omit the entry. That is wrong when `id` is a digest the verifier recomputes from a proof the holder already carries: nothing is fetched or retained. The v0.3 draft schema adds an optional `validation` member on an entry, whose only value is `recompute`. On such an entry the recomputation must bind the entry to this record, because the proof is public once presented and any record could otherwise carry it; this record's digest is the §3.1.3 preimage. `resolver` and `retention` must be absent, since no party has undertaken anything. "Cannot recompute" (the entry is absent, not a finding) and "recomputed and disagreed" (a contradiction, never reported as an absence) are separate outcomes, the distinction `docs/rfcs/a2a-delegation-profile.md` §4.3 draws for delegation links. `scripts/gen_v03_draft_schema.py` takes the member, the entry's `required` list and its resolver condition from the draft and everything else from canonical. `position-commitment` is registered in §3.1.2, the `rel` description of `schema/trace-claim.json` and its packaged copies, `docs/schema.md` and `docs/references-registry.md`; under v0.2 it earns nothing a pointer does not, and `resolver` is still required. `examples/position-commitment/` (generated by its own script, recomputed by `tests/test_position_commitment_fixtures.py`, named in `tests/test_adequacy_all_sets.py`) holds five v0.3 draft records: a proof that establishes the position, the same proof on another record, no proof held, an altered proof, and a proof whose committer key only the proof itself supplies. The committer keys and the sequence come from a separate `relying-party.json`, never from a proof, because agreement under a key the proof carries establishes consistency and not ordering. **Compatibility:** every existing record keeps validating against both schemas, and no v0.2 rule changes. Forward compatibility does not hold: a record that uses `validation` is rejected by the v0.2 schema and by `TrustRecord`, which is why this is a breaking change under GOVERNANCE.md with a 30-day comment period. The reference library does not read `references[]` and gains no verification path. Requested on #321 by @imran-siddique; construction and the binding correction from Michael Argento (Argento Computing Inc.), named with permission.

- **`content_marking`: consumer field and strict UTF-8 / RFC 8259 JSON boundaries.**
`verify_assertion` now validates `subject` (SPIFFE or DID URI), `eat_profile` (non-empty
string), and optional `anchor` (non-empty string) on both the assertion and the resolved
Expand Down
2 changes: 1 addition & 1 deletion conformance/schemas/trace-claim.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
"rel": {
"type": "string",
"minLength": 1,
"description": "Relationship type. The registered values are a registry that grows, so this is not a closed set. authorized-intent: an authorization decided before execution, held in another system. approval-outcome: an attributable human approval attached to a step-up or defer decision. behavior-trace: a behavioural record of what the agent did, of which this record is the environment evidence. condition-appraisal: an independent check's finding on whether a stated condition is established by a stated subject, bound to both by digest. observed-effect: a signed record of the state change an observer outside the agent saw over one interval, with the state before and after, the paths observed and the authority change ran under. applicability-assessment: an assessor's statement of whether a stated legal provision applies to a stated party, held in another system."
"description": "Relationship type. The registered values are a registry that grows, so this is not a closed set. authorized-intent: an authorization decided before execution, held in another system. approval-outcome: an attributable human approval attached to a step-up or defer decision. behavior-trace: a behavioural record of what the agent did, of which this record is the environment evidence. condition-appraisal: an independent check's finding on whether a stated condition is established by a stated subject, bound to both by digest. observed-effect: a signed record of the state change an observer outside the agent saw over one interval, with the state before and after, the paths observed and the authority change ran under. applicability-assessment: an assessor's statement of whether a stated legal provision applies to a stated party, held in another system. position-commitment: a commitment to a position in an externally ordered sequence, taken before the digest of this record exists; id is the commitment, validated by recomputation from a proof the holder carries and that commits this record, rather than by resolution. Under v0.2 the entry still names a resolver, so a producer that retains nothing omits it; under the v0.3 draft, an entry carrying validation recompute names no resolver."
},
"id": {
"type": "string",
Expand Down
78 changes: 77 additions & 1 deletion docs/references-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ schema description, in `docs/schema.md`, or in this document.

## Registered values

The six labels agreed so far, what each one points at, and where it is defined:
The seven labels agreed so far, what each one points at, and where it is defined:

| `rel` | Referenced object | Defined in | Example |
|---|---|---|---|
Expand All @@ -49,6 +49,7 @@ The six labels agreed so far, what each one points at, and where it is defined:
| `condition-appraisal` | An independent check's finding on whether a stated condition is established by a stated subject | This document, below | [`examples/condition-appraisal/`](../examples/condition-appraisal/) |
| `observed-effect` | A signed record of the state change an observer outside the agent saw over one interval | This document, below | [`examples/observed-effect/`](../examples/observed-effect/) |
| `applicability-assessment` | An assessor's statement of whether a stated legal provision applies to a stated party, held in another system | This document, below | [`examples/applicability-assessment/`](../examples/applicability-assessment/) |
| `position-commitment` | A commitment to a position in an externally ordered sequence, taken before the digest of this record exists | This document, below | [`examples/position-commitment/`](../examples/position-commitment/) |

The first three were registered by the change that introduced the block (#197) and
their referenced objects are defined to the extent the documents cited define them.
Expand Down Expand Up @@ -281,6 +282,81 @@ not a finding for or against the record. The example set holds `applies`,
`does-not-apply`, and `undetermined` objects side by side, and the records citing them
verify identically.

## `position-commitment`

### Meaning

The record points at a position it took in an externally ordered sequence before its
own digest existed. The construction that prompted the registration (#321): an enclave
allocates a slot before the record's digest exists, signs the slot record, and the
not-before is an ordered sequence the producer does not operate. The slot record
travels inside a proof the holder carries. Nothing is held anywhere for a verifier to
fetch, so what the relation lets a relying party establish is when the position was
taken relative to the sequence, which a timestamp inside the record cannot show
because the producer writes it.

Two neighbours it is not. It is not a transparency-log inclusion: an inclusion proof
shows a record was logged after it existed, and this commitment is taken before. And
it is not `origin`: the position says nothing about where the record's evidence came
from.

### The referenced object

A proof the holder carries, not an object a resolver retains. Its wire format belongs
to the sequence and the committing party; what registration fixes is what it must
carry.

| Fact | Carried as | Why it is required |
|---|---|---|
| The position | The commitment that is the entry's `id` | Recomputing it from the proof is the check |
| The sequence | An identifier for the externally ordered sequence and the position within it | The not-before comes from a sequence the producer does not operate |
| The record binding | This record's digest, committed by the proof | Without it the same proof passes for any record that carries it |
| The committing party | An identifier and a reference to its verification key, where the slot record is signed | Lets a relying party check who allocated the slot |

This record's digest is the §3.1.3 preimage applied to this record: the RFC 8785
canonical form of the complete record as published, `signature` member included.

No resolver undertakes to retain the proof, which is why the entry names none. That
removes the retention undertaking, not the need for the bytes: recomputing later still
needs the proof, so a holder who wants the entry checked keeps the proof bytes, and a
verifier handed none treats the entry as absent.

The committed example, [`examples/position-commitment/`](../examples/position-commitment/),
shows one concrete shape for these facts: a slot whose digest is the `id`, and a
binding the committer signs once the record exists, which commits the slot and this
record's digest. A producer whose sequence or committer emits another shape that
carries these facts is within this definition.

### What a verifier may conclude

Under v0.2, nothing beyond any other pointer. Recomputation is not a check v0.2
defines, rule 3 of §3.1.2 governs, and `resolver` is still required, so a producer
that retains nothing must omit the entry. The registration is useful under v0.2 only
so that two implementations mean the same thing by the name.

Under the v0.3 draft, an entry carrying `validation: "recompute"` has three outcomes,
each reported separately:

1. **The verifier does not hold the proof.** The entry is absent. That is not a
finding against the record.
2. **The recomputation agrees and the proof commits this record's digest.** The
proof is consistent with this record. Whether the position was taken for this
record, and where it sits in the sequence, rests on trust the relying party
supplies itself: a committer key and a copy of the sequence it holds independently
of the proof. A proof checked under a key it carries for itself establishes
consistency, not an independently established ordering.
3. **The recomputation runs and disagrees, or the proof commits another record's
digest.** A contradiction in the record, never reported as an absence.

The example set holds each of these apart: a proof that establishes the position
under an independently held key and sequence, the same proof carried by another
record, no proof held, a proof altered to an earlier position, and a proof whose only
committer key is the one it carries. The records' signatures verify in every case.

The second outcome is the property the producer gave up by omitting the entry under
v0.2: telling a backdate from a late commit by a contradiction inside the record,
rather than bounding the commit by rule.

## Adding a name

A registration is a non-breaking spec change of the informative kind under
Expand Down
4 changes: 3 additions & 1 deletion docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,14 @@ An array of pointers to facts held outside this record: an authorization decided

| Field | Type | Required | Description |
|---|---|---|---|
| `rel` | string | **yes** | Registered values: `authorized-intent`, `approval-outcome`, `behavior-trace`, `condition-appraisal`, `observed-effect`, `applicability-assessment`. A registry rather than a closed set, so the schema does not restrict which relation is named: only that one is: the value must be non-empty |
| `rel` | string | **yes** | Registered values: `authorized-intent`, `approval-outcome`, `behavior-trace`, `condition-appraisal`, `observed-effect`, `applicability-assessment`, `position-commitment`. A registry rather than a closed set, so the schema does not restrict which relation is named: only that one is: the value must be non-empty |
| `id` | string | **yes** | Identifier of the referenced fact within the resolver's system |
| `resolver` | string | **yes** | Identifier of the party obliged to resolve `id` |
| `retention` | string | no | ISO 8601 duration the resolver undertakes to keep `id` resolvable. An undertaking only; nothing enforces it |
| `digest` | string | no | `sha256:` or `sha384:` digest of the referenced object, when the producer holds it at issue time |

The v0.3 draft schema adds one optional member, `validation`, whose only value is `recompute`: `id` is then checked by recomputation from evidence the verifier holds and that commits this record, rather than by resolution, and `resolver` and `retention` must be absent. This schema rejects the member. Section 3.1.2, "Entries validated by recomputation", has the rules.

`rel` is open where `origin.kind` is closed. Section 3.1.1 says `kind` is a closed set "because the value of the field is that a verifier can key on it"; section 3.1.2 calls `rel`'s values a registry and does not say that, so a new relation is a spec change and not also a schema change. What each registered value's referenced object is, what a verifier may conclude from it, and how a name is added are in [`references-registry.md`](references-registry.md).

Spec section 3.1.2 also binds verifiers: one **must not** reject a record because an entry cannot be resolved, and **must not** treat a resolved entry as attested evidence. A reference that could invalidate a record would hand whoever controls the target a way to invalidate evidence they do not hold. Both are verifier behaviour, so neither the schema nor the reference model can enforce them; they are conformance-suite rules. What the schema and the model do enforce is the shape, and that a producer who cannot name a `resolver` cannot emit an empty one.
Expand Down
7 changes: 7 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ checking any archived record, including these.
`does-not-apply` and an `undetermined` assessment that verify identically to `applies`,
unresolvable, and an issuer whose key the relying party does not hold. Every digest
recomputes; see that directory's README and `docs/references-registry.md`.
- `position-commitment/`: five signed v0.3 draft Trust Records, each carrying a
`position-commitment` entry with `validation: "recompute"` and no resolver, beside the
proofs a holder presents and a separate `relying-party.json` holding the committer keys
and sequence the relying party supplies itself: a position established, the same proof
on another record, no proof held, a proof altered to an earlier position, and a proof
whose only committer key is the one it carries. Every digest recomputes; see that
directory's README and `docs/references-registry.md`.
- `signature-encoding/`: three signed Trust Records that separate a canonically-encoded
embedded signature from two non-canonical respellings of the same 64 bytes. Proposed,
not accepted normative text (#247); carries a `scan_published_signatures.py` tool
Expand Down
41 changes: 41 additions & 0 deletions examples/position-commitment/01-position-established.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"eat_profile": "tag:agentrust-io.com,2026:trace-v0.3",
"iat": 1792487100,
"subject": "spiffe://trust.example.org/agent/settlement",
"model": {
"provider": "example",
"model_id": "example-model"
},
"runtime": {
"platform": "software-only",
"measurement": "sha256:0000000000000000000000000000000000000000000000000000000000000000"
},
"policy": {
"bundle_hash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"enforcement_mode": "enforce"
},
"data_class": "confidential",
"build_provenance": {
"slsa_level": 1,
"digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
},
"appraisal": {
"status": "none",
"verifier": "https://verifier.example.org"
},
"cnf": {
"jwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "4lYwTNnFrnAzuzGYnCjHgaKjy2cCsjf9e7QrQ4z-sP8"
}
},
"references": [
{
"rel": "position-commitment",
"id": "sha256:a57dff7b9562239543a519a6a773ccba86c368b8eb2b2307b074b78c5502b6e9",
"validation": "recompute"
}
],
"signature": "sm5urBa3Gktx7A2ZJpml8lCKUT5kLkaBu_fPLvjoDkh_7D53o2qzr9-iD_UuPEDnwhXZwrO3Du8FEk9BuPdWAg"
}
41 changes: 41 additions & 0 deletions examples/position-commitment/02-same-proof-other-record.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"eat_profile": "tag:agentrust-io.com,2026:trace-v0.3",
"iat": 1792487100,
"subject": "spiffe://trust.example.org/agent/settlement-replay",
"model": {
"provider": "example",
"model_id": "example-model"
},
"runtime": {
"platform": "software-only",
"measurement": "sha256:0000000000000000000000000000000000000000000000000000000000000000"
},
"policy": {
"bundle_hash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"enforcement_mode": "enforce"
},
"data_class": "confidential",
"build_provenance": {
"slsa_level": 1,
"digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
},
"appraisal": {
"status": "none",
"verifier": "https://verifier.example.org"
},
"cnf": {
"jwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "4lYwTNnFrnAzuzGYnCjHgaKjy2cCsjf9e7QrQ4z-sP8"
}
},
"references": [
{
"rel": "position-commitment",
"id": "sha256:a57dff7b9562239543a519a6a773ccba86c368b8eb2b2307b074b78c5502b6e9",
"validation": "recompute"
}
],
"signature": "7u956pusYtnXk2OJtDCo88NJHlKmBjLj_zXGjfBBOwzjuHbqHlP8kEb_uxAbYhTTEyE3tu3oHfxXhk0HO35gAg"
}
41 changes: 41 additions & 0 deletions examples/position-commitment/03-proof-not-held.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"eat_profile": "tag:agentrust-io.com,2026:trace-v0.3",
"iat": 1792487100,
"subject": "spiffe://trust.example.org/agent/settlement/3",
"model": {
"provider": "example",
"model_id": "example-model"
},
"runtime": {
"platform": "software-only",
"measurement": "sha256:0000000000000000000000000000000000000000000000000000000000000000"
},
"policy": {
"bundle_hash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"enforcement_mode": "enforce"
},
"data_class": "confidential",
"build_provenance": {
"slsa_level": 1,
"digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
},
"appraisal": {
"status": "none",
"verifier": "https://verifier.example.org"
},
"cnf": {
"jwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "4lYwTNnFrnAzuzGYnCjHgaKjy2cCsjf9e7QrQ4z-sP8"
}
},
"references": [
{
"rel": "position-commitment",
"id": "sha256:db4cf289d429612133ac5fb523c5bd047da48af0b961410a53019c2e36aa936e",
"validation": "recompute"
}
],
"signature": "wpDJbcLvNzPN4JqkifFGI1GZsikzLeNLzi16DCfuyUZZFS10QftXHgm1F165nHcPisFOhUvaDwRCQ7Ty2ZgVAg"
}
Loading
Loading