Canonical predicate type https://schemas.governedwork.com/attestation/governance-receipt/v1 · schema.json · namespace home

GovernanceReceipt — Core Predicate (v1)

Status: Draft — tracking @appitudeio/governance v1.0.0-rc.1. The wire format described here is frozen against the rc.1 shape; it becomes 1.0.0 when @appitudeio/governance v1.0.0 final ships, not before (governance ADR 0015 §4).

This document is the field-by-field specification of the core governance predicate. It is the authoritative prose companion to ../schema/governance-receipt-v1.json; when prose and schema disagree, the schema is normative for structure, the prose is normative for semantics. This document uses MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY with their RFC 2119 meanings.

1. Layer map (read this first)

The name GovernanceReceipt is also used by Microsoft's Agent Governance Toolkit (AGT) for a different artifact at a different granularity. Governance ADR 0015 §6 requires every public document to lead with this disambiguation. The names collide; the layers do not.

Artifact Granularity Namespace
Microsoft AGT GovernanceReceipt per tool call microsoft/agent-governance-toolkit
governance-spec GovernanceReceipt (core — this document) per governed action governedwork.com/...
Steerboard InvocationReceipt (profile) per invocation (aggregate of one or more governed actions) governedwork.com/.../profiles/steerboard-invocation-receipt/...

The core artifact specified here is per governed action: one observer, one principal, one action, one target, one execution. An invocation that spans several governed actions is an aggregate, expressed by a profile that references one or more core receipts by { id, hash } — never by embedding them (§8). Microsoft AGT's per-tool-call receipt is a narrower unit again; the two can be composed at the in-toto subject[] layer (see ../envelopes/in-toto-predicate.md) but are not the same artifact.

2. Purpose

A GovernanceReceipt is the per-governed-action evidence artifact: a domain-agnostic, signed record that a single governed action was authorized, executed, and is provably attributable to a recorder. It serves agent governance first and standards-mapping second.

Its spine is CADF (DMTF DSP0262) — the Cloud Auditing Data Federation domain-agnostic audit-event record (Observer / Initiator / Action / Target / Outcome). CADF was built to audit any cloud-resource action, so the same five fields carry an email send, a workflow run, or a deployment with zero fiction — that genericity is the entire point of the artifact. Onto that spine the receipt adds first-class governance linkage (intentRef, decisionRef, approvalRef) whose W3C PROV wasAssociatedWith semantics tie the recorded action back to the decision and approval that authorized it.

The receipt body carries the CADF event plus the governance linkage. The cryptographic proof is the layer around it: the predicate is augmented with a signer and recordedAt (together forming the AttestationPredicate, §3.1), carried as the predicate of an in-toto v1 Statement, DSSE-signed. A signature cannot be a field inside the body it signs, so the proof is the signed bundle, not a field of the receipt — see ../envelopes/in-toto-predicate.md.

3. The signed predicate

The artifact that is signed and verified is the AttestationPredicate: the full GovernanceReceipt body plus the two evidence-layer fields a standalone in-toto consumer needs to resolve who signed and when.

AttestationPredicate = GovernanceReceipt + {
  signer: Signer,         // who signed (DID-shaped) — §4.7
  recordedAt: string      // when the attestation was minted — §4.8
}

The schema in ../schema/governance-receipt-v1.json validates this full AttestationPredicate, because that is what appears on the wire inside the Statement's predicate.

3.1 Field inventory

AttestationPredicate {
  // Version envelope
  schemaVersion: "1.0.0"        // shape version (SemVer) — REQUIRED §4.0
  // CADF six (neutral spine)
  observer: Identity            // CADF observer — REQUIRED          §4.1
  principal: Identity           // CADF initiator — REQUIRED         §4.2
  action: string                // CADF action name — REQUIRED       §4.3
  resource: ResourceRef         // CADF target — REQUIRED            §4.4
  execution: Execution          // what happened — REQUIRED          §4.5
  // Governance linkage (W3C PROV wasAssociatedWith; first-class)
  intentRef: string             // REQUIRED                          §4.6
  decisionRef: DecisionRef      // REQUIRED                          §5
  approvalRef: ApprovalRef|null // REQUIRED (nullable)               §6
  // Evidence layer (added by AttestationPredicate)
  signer: Signer                // REQUIRED                          §4.7
  recordedAt: string            // REQUIRED                          §4.8
}

All objects are closed (additionalProperties: false); no open extension key-map exists anywhere in the core. There are no user-controlled keys and no numbers in the signed predicate (every field is a branded id, an enum, or a string), which is what keeps canonicalization byte-stable across consumers.

4. Field-by-field reference

4.0 schemaVersion (REQUIRED) — "1.0.0"

The GovernanceReceipt shape version, full SemVer 2.0.0. The predicateType URI carries the MAJOR only (…/governance-receipt/v1); schemaVersion pins the precise shape version IN the signed predicate, so a verifier can distinguish a 1.0.0 receipt from a future 1.1.0 without parsing the URI. A receipt minted before this field existed (the pre-1.0 rc-era, under the same /v1 URI) is not recognized — the schema requires the field (const: "1.0.0"), and a conformant verifier rejects a missing or unrecognized value fail-closed. A future 1.1.0 is a conscious spec-version + ADR event (governance ADR 0018).

4.1 observer (REQUIRED) — Identity

The CADF observer: the kernel that recorded the event, by its own did:web Identity. A receipt MUST name its recorder. Distinct from signer (§4.7) as a role, though in practice the same kernel identity fills both. See §4.9 for the Identity shape.

Standards: CADF DSP0262 observer; W3C DID Core (the recorder is a DID subject).

4.2 principal (REQUIRED) — Identity

The CADF initiator: who acted. Its role ("access-subject") is expressed by position (this field), not by a role[] field — the XACML 3.0 subject-category vocabulary is the named reference model for these positional roles, adopted as documentation, not materialized as a field (governance ADR 0014 #I-2).

Standards: CADF initiator; XACML 3.0 subject-categories; W3C DID Core.

4.3 action (REQUIRED) — string (branded ActionName)

The action name — a dot-namespaced, channel-bearing string ("contact.customer.email", "workflow.run"). It is the join key into the kernel's trusted ActionDefinition registry; the traits of the action (classification, reversibility, externality) live in that trusted registry and are committed to via decisionRef.actionDefinitionHash (§5), never carried on the receipt directly. Deliberately an open string, not a closed enum: the kernel serves many consumers, each with its own action vocabulary.

Standards: CADF DSP0262 action taxonomy (with a documented "communicate/send" extension to cloud-CRUD verbs, ADR 0014 #A-2); AuthZEN action.name.

4.4 resource (REQUIRED) — ResourceRef

The CADF target: what was acted upon. A closed, kind-discriminated union (no open key-map). kind is one of:

kind Meaning Extra fields
invocation one governed AI invocation (the substrate's primary subject)
capability a capability definition or version
workflow a workflow definition or version
external a downstream effect (mail.send, notification.dispatch, …) optional system

Every variant carries a required id (string) and an optional scopeId (the tenant / project / namespace under which id is addressable; omitted by non-multi-tenant consumers). Only the external variant additionally carries an optional system. The union is closed: a consumer maps its own branded IDs into id + scopeId at the substrate boundary rather than adding a new kind.

Standards: CADF DSP0262 target.

4.5 execution (REQUIRED) — Execution

What the governed action actually did. Generalizes CADF's Action/Outcome plus the executor pointer.

Field Type Req. Semantics
kind string REQUIRED executor class ("communication.send", "workflow.run", "http.call", …). A free string — the substrate carries no opinion on a consumer's executor taxonomy.
ref string REQUIRED opaque pointer to the executed thing (a Slack ts, a message id, a run id).
outcome GovernanceOutcome enum REQUIRED success | failure | pending — the CADF Outcome taxonomy, narrowed to the three values a governed action produces.
performedAt string (date-time) REQUIRED CADF eventTime.
payload ExecutionPayload | null REQUIRED (nullable) binds what ran to the approved intent (§4.5.1).

4.5.1 execution.payloadExecutionPayload | null

Field Type Req. Semantics
hash Sha256Hex REQUIRED canonical-JSON SHA-256 of the payload that actually executed (the real message body / deploy parameters / request). It rides in the signed predicate, so tampering it fails verification.
derivedFromIntentHash Sha256Hex REQUIRED the intentHash of the approved/decided intent this payload was derived from.

payload is REQUIRED (non-null) for an executed outcome (success / failure — an attempt was made, so what was attempted MUST be bound) and MAY be null for a non-executed pending outcome. This is a trust-chain invariant enforced at emit (§7, rule 4). The kernel models approve-as-is (no edit-on-approve), so today hash === derivedFromIntentHash whenever the executed payload is the intent body; the two fields stay distinct so a future revise flow (edit → new intent → re-approve) can record a payload causally derived from — but not byte-identical to — the approved intent without pretending they are the same.

Standards: CADF Action/Outcome; the payload binding is governance-native (no CADF-native slot).

4.6 intentRef (REQUIRED) — string (branded IntentId)

The caller-minted idempotency key of the ActionIntent this receipt is about — the receipt's stable identity and the join key everywhere downstream. One governed action = one receipt: intentRef is unique within a store instance (governance ADR 0014, review resolution 1). It also names the in-toto subject (subject[0].name = "governance:<intentRef>", see the envelope doc).

Standards: W3C PROV wasAssociatedWith (linkage); CADF extension attribute (no native intent slot, ADR 0014 #E-1).

4.7 signer (REQUIRED) — Signer

The DID subject that signed, exercising an assertionMethod key (W3C DID Core §5.3: assertionMethod = issuer-of-evidence). Part of the AttestationPredicate, not the GovernanceReceipt body.

Field Type Req. Semantics
identity Identity REQUIRED the DID subject doing the signing. Its did is OPTIONAL at the type level but a verifiable signer MUST carry a did — the verifier resolves the verification method from it and fails closed when it is absent.
keyId string REQUIRED the verificationMethod fragment within the DID document (the part after #), e.g. "key-1".
algorithm SignatureAlg enum REQUIRED Ed25519 | ECDSA_SHA_256 | RSASSA_PSS_SHA_256. MUST equal the producing signature's algorithm. The verifier supports only Ed25519 (did:key publicKeyMultibase) and ECDSA_SHA_256 (did:web publicKeyJwk); a bundle claiming RSASSA_PSS_SHA_256 fails closed (no verify path).

Standards: W3C DID Core §5.3 verification relationships; in-toto functionary.

4.8 recordedAt (REQUIRED) — string (ISO-8601 date-time)

The instant the attestation was minted. Part of the AttestationPredicate. It is the default valid-at-time instant for verifying the signer's key validity (§ envelope doc). Security note: recordedAt is self-asserted by the signer; a verifier that needs compromise-resistant revocation MUST supply a trusted observation time instead of trusting it. Typed as a plain ISO-8601 string here (see judgment calls in the schema), validated format: date-time.

4.9 Identity (shared shape)

observer, principal, signer.identity, and approvalRef.approver are all the same unified Identity.

Field Type Req. Semantics
did string (DID URI) OPTIONAL W3C DID URI (did:<method>:<id>). Present for DID-addressable actors; absent for session-only humans or not-yet-DID machine workloads (governance ADR 0014 #I-1). Required in practice for a signer (§4.7).
kind IdentityKind enum REQUIRED agent | human | service | machine.
ref string (branded IdentityRef) REQUIRED the stable join key: the DID when present, else a consumer-supplied principal id.

Standards: W3C DID Core (an identity is a DID subject); SPIFFE/SPIRE (machine workload identity).

5. decisionRef (REQUIRED) — DecisionRef

The signed link from the attested action back to the Decision that authorized it. It is a reference, not the full decision: the heavy intent + decision bodies live in the Decision-pillar stores; this envelope lifts the load-bearing, tamper-evident facts into the signed predicate.

Field Type Req. Semantics
intentId string (IntentId) REQUIRED the join key — the same caller-minted id as intentRef.
intentHash Sha256Hex REQUIRED canonical-JSON SHA-256 of the decided intent body. On the direct-allow path (approvalRef === null) this is the only signed anchor binding what executed to what was authorized (§7).
outcome enum REQUIRED the decided outcome: allow | deny | require_approval | require_info. Almost always allow on an attested action (a denied action does not run); the full union is carried so a policy-violation incident receipt can record the actual outcome.
decidedAt string (date-time) REQUIRED when the decision was made.
actionDefinitionHash Sha256Hex REQUIRED canonical-JSON SHA-256 of the trusted ActionDefinition the decision resolved. Commits the signed receipt to the exact trait-set that authorized the action; an auditor recomputes it from the archived registry entry. Always present (a receipt is only emitted for an authorized action, which always resolved a definition).
actionDefinitionVersion string OPTIONAL human-readable definition version label.
policyBundleHash Sha256Hex REQUIRED hash of the policy world the decision was evaluated under — directly inspectable, so an auditor can confirm which bundle decided and detect that the live bundle has since changed.
decisionProvenanceHash Sha256Hex REQUIRED umbrella anchor: canonical-JSON SHA-256 of the whole DecisionProvenance (action definition + policy world + grants considered + caller-context hash). actionDefinitionHash and policyBundleHash are inspectable shortcuts into this same envelope.

Trust model (rc.1, honest): verify() does NOT cross-check decisionRef against a decision store — the kernel is the trusted signer, so a verifier trusts the linkage the kernel signed. Independent third-party re-derivation (recomputing the hashes from archived inputs) is the audit path the hashes enable, but is not performed by verify().

Standards: W3C PROV wasAssociatedWith; CADF extension attribute (ADR 0014 #E-1); decision-provenance envelope (governance issue #21).

6. approvalRef (REQUIRED, nullable) — ApprovalRef | null

The signed link back to the ApprovalRequest that authorized the action when it required a human gate, or null for a direct allow (a permit with no @requires_approval obligation — the action ran without a human gate). Carrying the resolution snapshot here (not a bare ID) makes the receipt self-contained: an auditor reads who approved, when, and for which intent body straight from the signed predicate.

When non-null:

Field Type Req. Semantics
approvalRequestId string (ApprovalRequestId) REQUIRED the kernel-minted join key into the approval store.
approvalRequestHash Sha256Hex REQUIRED umbrella anchor: canonical-JSON SHA-256 of the approval-request resolution snapshot ({ id, intentHash, state, approver, createdAt, expiresAt, resolution? } — the full request minus the embedded intent body, which is bound separately by intentHash).
approver Identity REQUIRED who was required to decide.
state ApprovalState enum REQUIRED pending | approved | rejected | expired. A receipt for an executed action always references an approved approval (§7, rule 5). pending appears only on an incident receipt for an unresolved request.
intentHash Sha256Hex REQUIRED canonical-JSON SHA-256 of the intent body that was approved — the divergence detector.
requestedAt string (date-time) REQUIRED when the approval was raised (the request's createdAt, exposed under the audit-facing name requestedAt).
resolvedAt string (date-time) OPTIONAL when the approval was resolved. Present IFF state is approved / rejected; ABSENT for expired (no human resolver) and pending (unresolved).
expiresAt string (date-time) REQUIRED when the approval window lapses.

Trust model (rc.1, honest): as with decisionRef, verify() does not cross-check this against an approval store; it trusts the kernel-signed linkage.

Standards: GNAP grant-request lifecycle (approval states); W3C PROV wasAssociatedWith; WS-HumanTask 1.1 (prior art, simplified — ADR 0012 §5).

7. Trust-chain invariants

The invariants below are enforced at two independent points, mapped per-invariant in the enforcement matrix at the end of this section:

  1. Subject = intent. intentRef MUST equal the provided intent.id.

  2. Decision faithfully references the intent. decisionRef.intentId MUST equal intent.id and decisionRef.intentHash MUST equal the canonical-JSON SHA-256 of the real intent body. (This is recomputed at emit; a fabricated decisionRef.intentHash is rejected.)

  3. Approval (if present) legitimized the same intent body. When approvalRef !== null, approvalRef.intentHash MUST equal decisionRef.intentHash — one intent chain, not two independent refs.

  4. Executed attempts bind what ran. For an executed outcome (success / failure), execution.payload MUST be non-null. A pending receipt MAY omit it.

  5. No real execution under a non-approved approval. When the outcome is executed and approvalRef !== null, approvalRef.state MUST be approved. A rejected / expired / pending approval can never back an executed receipt.

  6. Payload derives from the decided intent. When execution.payload !== null, execution.payload.derivedFromIntentHash MUST equal decisionRef.intentHash. Because rule 2 has welded decisionRef.intentHash to the real intent hash, this transitively binds the executed payload to ground truth on both the approval and direct-allow paths.

  7. Executed success requires an allow decision. When execution.outcome is success, decisionRef.outcome MUST be allow. (Deliberately scoped to success — a failure / pending incident receipt recording an unauthorized attempt stays representable.)

  8. A require_approval decision references its approval. When decisionRef.outcome is require_approval, approvalRef MUST NOT be null.

  9. The signed bytes are I-JSON. A conformant statement MUST NOT contain duplicate object keys — RFC 8785 (JCS) is defined over I-JSON, and a duplicate-key document is ambiguous across parsers (last-wins and first-wins parsers read different content from the same bytes). Verifiers MUST reject such a document before computing any digest (see envelopes/in-toto-predicate.md §4 step 2).

Together: a receipt can never claim a decision, approval, or payload for a different intent than the one it names.

7.1 Enforcement matrix

# Invariant Kernel emit() Conformance validator Validator rule ID
1 intentRef = ground-truth intent.id — (needs the ground-truth intent)
2 decision binds the intent (id + recomputed hash) partial — id binding only invariant-2
3 approval binds the same intent body partial — transitively via invariant-6-approval
4 executed ⇒ non-null payload invariant-4
5 executed + approval ⇒ approved state invariant-5
6 payload derives from the decided intent invariant-6-direct / invariant-6-approval
7 success ⇒ allow decision ✅ — and STRICTER: the kernel also rejects deny+failure (an attempt is an attempt; rule 7, decision-denies-execution) invariant-7
8 require_approval ⇒ non-null approvalRef ✅ — scoped to executed receipts (rule 8, decision-requires-approval) invariant-8
9 signed bytes are I-JSON (no duplicate keys) ✅ (JSON.stringify cannot emit duplicates) json-strict

The validator additionally enforces structural rules with no invariant number: the Statement envelope shape (envelope), exact _type / predicateType URIs (statement-type / predicate-type), subject-name binding (subject-name), the JCS subject digest (digest), and core-schema validation (schema) — which includes the schemaVersion: "1.0.0" const pin (§4.0): a missing or unrecognized shape version is rejected on the schema rule (fixture missing-schema-version), the spec-side mirror of the kernel verifier's predicate-version gate (ADR 0018).

Every invariant is now enforced at both points (rows 7/8 flipped when governance kernel rules 7–9 landed, 2026-06-03). When enforcement status changes, this matrix — and only this matrix — changes; prose elsewhere never tracks it. On the direct-allow path the anchor is derivedFromIntentHash === decisionRef.intentHash; on the approval path the additional anchor is approvalRef.intentHash === decisionRef.intentHash and approvalRef.state === approved for executed actions.

verify() proves cryptographic integrity + signer identity; it deliberately does not re-run these construction invariants (they were enforced at emit) and does not prove historical decision replay (that decide() / approval actually ran at some past time) — that is the future audit/replay layer.

8. What this spec does NOT constrain

Per governance ADR 0015 §5, this spec constrains exactly one thing: the JSON that emit() produces — the signed predicate and its DSSE/in-toto envelope. It does not constrain:

The conformance authority is asymmetric and time-bound (ADR 0015 §5): while governance is -rc, the spec is published from the kernel's frozen shape (kernel → spec); after v1.0.0 final, the conformance ratchet holds the kernel to the published contract (spec → kernel), and breaking it becomes a conscious spec-version event.

9. Profiles

A profile is a separate artifact (its own schema, its own optional signature) that enriches one or more core receipts with consumer-specific fields. Profiles are governed by these rules (ADR 0014 Pillar 3; ADR 0015 §2):

The Steerboard InvocationReceipt (per-invocation aggregate carrying controlProfile, disclosureProofs, policyWarnings, economics, modelCallManifests, …) is the first profile, at profiles/steerboard-invocation-receipt/, carrying governanceReceiptRefs[]. The deep regulatory mappings that reference profile fields live with that profile (see ../mappings/core/traceability.md § scope); this core's mappings cover only core fields.