Chain of Custody
Table of contents
- Chain of Custody
- Summary
- Intent
- Composes
- Composition logic
- Composition-level invariants
- Examples
- Walkthrough — pharmaceutical chain of custody under 21 CFR Part 211
- Legal evidence — physical exhibit at trial under FRE 901(b)(9)
- Rejection path — transfer by non-current custodian
- Rejection path — custody action on an Archived chain
- Failure path — custody step whose audit write fails, recovered
- DEA controlled substance — 21 CFR Part 1304 inventory custody
- Regulated adversarial scenarios
- Generation acceptance
- Non-goals
- Edge cases
- Terms
- Standards references
- Status
- Ledger
Summary
Chain of Custody is a regulated composition (a spec that wires two or more atoms — freestanding, self-contained pattern specs — together) that solves the chain-of-custody problem no single atom solves alone: making it provable that an artifact moved through an unbroken sequence of verified, non-repudiable (unable later to deny the act) custodians, that no entry was tampered with, and that the chain was kept for the required period. It wires two constituents: Provenance (the append-only custody chain with structural continuity — one current custodian at all times, hand-to-hand transfers) and the Audit Trail substrate (the tamper-evident — designed so unauthorized changes are detectable — regulated-audit substrate that attribution-stamps and seals every custody event via the Event Log, Actor Identity, Retention Window, and Tamper Evidence atoms it contains).
The composition’s defining emergent guarantee is provable custody: a single query, verify_custody(chain_id, original_event_payloads), returns the full ordered chain with, per entry, the verified attribution of the acting custodian (who authorized this step), the tamper-evident seal status (has the entry been altered), and the retention state (is the entry still held lawfully) — plus a continuity check confirming that every transfer’s outgoing custodian equals the prior holder. This is the provable answer to the regulator’s, judge’s, or auditor’s four questions — who held it, was the transfer unbroken, was the record altered, was it kept long enough. Neither Provenance alone nor Audit Trail alone produces this answer; the composition is what wires them into a single custody-proof surface.
Its most common uses are pharmaceutical chain of custody under FDA (US Food and Drug Administration) 21 CFR (Code of Federal Regulations) Part 211 and DEA (US Drug Enforcement Administration) 21 CFR Part 1304 (controlled substance inventory), physical-evidence authentication under FRE (Federal Rules of Evidence) 901(b)(9), financial instrument custody records under SEC (US Securities and Exchange Commission) Rule 17a-4, and digital-file chain of custody under ISO (International Organization for Standardization) 23081 (records-management metadata). Any system that must make it provable that every step in an artifact’s journey was attributed, sealed, and retention-governed is a candidate for this composition.
Intent
Every domain that must account for an artifact’s journey faces the same four-part requirement: the chain must be unbroken (no gap in custody, hand-to-hand transfers with no unattributed interval), the custodians must be verified (each hand-off attributed to a real actor, not just an opaque reference), the chain must be tamper-evident (any after-the-fact rewrite provable), and the chain must be retention-governed (retained for its regulatory lifetime and lawfully destroyable with a defensible record of destruction). No single atom satisfies all four. Provenance supplies the first. Audit Trail supplies the third and fourth, and transitively the second via Actor Identity. But neither provides the full custody-proof surface until they are wired together: Provenance does not verify its opaque custodian_ref values and does not seal its chain; Audit Trail does not know what a custody chain is or that its events need to be tied to custody entries. The wiring is this composition.
The cross-domain structural identity is the composition’s thesis. Under FDA 21 CFR Part 211 (Current Good Manufacturing Practice for Finished Pharmaceuticals), a pharmaceutical manufacturer must demonstrate an unbroken, attributed, tamper-protected, retention-compliant chain of custody from manufacture through distribution to dispensing. Under FRE 901(b)(9) (Authentication by Process or System), an officer presenting physical evidence at trial must authenticate the exhibit via an unbroken custody chain in which every handler is identified. Under DEA 21 CFR Part 1304 (Controlled Substance Inventory Records), a complete, accurate record of every change of custody for a controlled substance is a legal obligation. The structural form is identical across all three domains: one artifact, one current custodian at all times, hand-to-hand transfers, each hand-off attributed and verifiable, the record sealed against tampering, and the record retained for its regulatory lifetime. One grounded composition satisfies all three.
This is a composition, not a new primitive. Provenance and Audit Trail (with its constituent atoms, Event Log, Actor Identity, Retention Window, and Tamper Evidence — reached transitively) are unchanged. The composition is the wiring that makes them coherent as a single chain-of-custody surface. It introduces emergent actions — Originate Custody, Transfer Custody, Transform Custody, Disclose Custody, Archive Custody, Verify Custody — that belong to no single constituent and exist only because the two are wired together. Verify Custody in particular belongs to neither constituent alone: Provenance can verify structural continuity but cannot verify attribution or tamper-evidence; Audit Trail can verify attribution and tamper-evidence but does not know which events constitute a custody chain. The composition is the layer that answers: is this custody chain complete, attributed, sealed, and within its retention horizon?
What the composition is not: it is not a DAG-model (DAG — directed acyclic graph) provenance tracker (the linear single-artifact constraint is inherited from Provenance); it is not a multi-party simultaneous custody surface; it is not the legal-hold suspension layer over custody chains (that is Legal Hold / Defensible Retention); it is not the artifact-registry layer (validating that artifact_ref names a real artifact in the host system is the host’s obligation); and it is not the clock-authority layer (inherited from Audit Trail). Each is named explicitly in Non-goals.
Composes
- Provenance — the structural custody chain: one current custodian at all times, hand-to-hand transfers, the outgoing custodian read from chain state.
- Audit Trail — the regulated-audit substrate: every custody action records through it, and each record is appended to Event Log, attested by Actor Identity, placed under Retention Window and sealed by Tamper Evidence at the configured cadence.
Composes 1: EXACTLY ONE Provenance instance MUST serve the composition.
Composes 2: EXACTLY ONE Audit Trail instance MUST serve the composition.
Composes 3: The composition MUST reach a transitive atom ONLY through Audit Trail.
Composes 4: The composition MUST NOT compose an instance of a transitive atom.
Composes 5: The composition MUST inherit a constituent's invariants PER Execution Contract Conformance 8.
Composes 6: The composition MUST NOT change a constituent's spec.
Composes 7: The composition MUST select the custody events through the custody enumeration.
Deleted: Composes 8. Composes 9 owns it.
Composes 9: The composition MUST NOT read Audit Trail records directly.
Composes 10: IF the custody enumeration answers invalid-query THEN the deployment MUST alert on a deployment fault.
Composes 11: The composition MUST record two audit writes PER bound custody action.
Term composition: this pattern’s wiring of Provenance and Audit Trail — the five custody actions, the verification, the binding index and the reconciliation.
Term constituents: Provenance, Audit Trail.
Term transitive atoms: Event Log, Actor Identity, Retention Window and Tamper Evidence, reached through Audit Trail.
Term custody enumeration: Event Log’s read by sequence-number range with an open upper bound, passed through Audit Trail unchanged, with every selection by action reference and payload field made in the composition’s own code.
Term record read: Audit Trail’s read_record on an event id — the substrate’s declared consolidated read surface.
Term audit write: Audit Trail’s record_action.
Term verification: Audit Trail’s verify_record on an event id and a presentation.
Term custody action: Originate Custody, Transfer Custody, Transform Custody, Disclose Custody or Archive Custody.
Adjacent, not constituents: Defensible Retention (hold-blocks-purge over the custody events); Selective Disclosure (what subset was disclosed); Permissions (custody-transfer authority).
Composition logic
Composition state
Composition state 1: The composition MUST store the binding index.
Composition state 2: The composition MUST key the binding index by entry id.
Composition state 3: A binding MUST carry the outcome event id of the entry's outcome event.
Composition state 4: The composition MUST write a binding ONLY AFTER the entry's outcome event lands.
Composition state 5: The composition MUST NOT change a binding.
Composition state 6: The composition MUST classify a live binding as a derived index.
Composition state 7: The composition MUST rebuild a missing live binding PER the binding rebuild.
Composition state 8: IF the binding rebuild finds two outcome events carrying one entry id THEN the rebuild MUST bind the first by sequence number.
Composition state 9: IF the binding rebuild finds two outcome events carrying one entry id THEN the rebuild MUST open a binding-duplicate finding.
Composition state 10: IF the binding rebuild finds no outcome event for an entry THEN the composition MUST read the entry as an orphan.
Composition state 11: The composition MUST classify a purged binding as extraction-pending against Erasure Tombstone.
Deleted: Composition state 12. Execution Contract Database 3 owns it.
Composition state 13: The composition MUST NOT read an intent as a binding.
Composition state 14: The composition MUST NOT duplicate a constituent's records.
Term entry id: the opaque id Provenance mints for a custody entry.
Term chain id: the opaque id Provenance mints for a custody chain.
Term binding index: the composition’s map from a custody entry to the outcome event that attributes, seals and retention-governs it — entry_to_event in an implementation.
Term binding: one entry id with its outcome event id.
Term outcome event: the custody.originated, custody.received, custody.transferred, custody.transformed, custody.disclosed or custody.archived event — carrying the entry id.
Term live binding: a binding whose outcome event’s payload the audit horizon has not reached.
Term purged binding: a binding whose outcome event’s payload the audit horizon has destroyed.
Term binding rebuild: the custody enumeration from the log’s start, kept to the outcome events whose entry id the chain’s Provenance entries carry, re-keyed by that entry id.
Capability requirement
Capability requirement 1: A deployment MUST set the audit retention policy on the Audit Trail instance.
Capability requirement 2: The composition MUST NOT pass a retention input to the audit write.
Capability requirement 3: A regulated deployment MUST set an audit retention policy encoding the regime's minimum custody-record retention.
Capability requirement 4: A deployment MUST set the seal cadence on the Audit Trail instance.
Capability requirement 5: The composition MUST NOT override the seal cadence.
Capability requirement 6: A deployment MUST set the custody completion bound.
Capability requirement 7: IF the custody completion bound EQUALS blank THEN the composition MUST refuse to start.
Deleted: Capability requirement 8. Execution Contract Database 1 owns it.
Capability requirement 9: A deployment MUST set the custody compensation window.
Capability requirement 10: A deployment MUST set the custody reconciliation cadence.
Capability requirement 11: A deployment MUST disclose the reconciliation run bound.
Capability requirement 12: The composition MUST start ONLY IF the custody compensation window EXCEEDS the liveness sum.
Deleted: Capability requirement 13. Execution Contract Database 3 owns it.
Deleted: Capability requirement 14. Execution Contract Database 3 owns it.
Capability requirement 15: A deployment MUST provision the recovery identity.
Deleted: Capability requirement 16. Execution Contract Database 2 owns it.
Deleted: Capability requirement 17. Execution Contract Database 2 owns it: a custody action's reads of a chain and the writes they decide are one transaction, and the composition names no exclusion to release.
Deleted: Capability requirement 18. Execution Contract Database 2 owns it: a custody action's reads of a chain and the writes they decide are one transaction, and the composition names no exclusion to release.
Deleted: Capability requirement 19. Execution Contract Database 2 owns it: a custody action's reads of a chain and the writes they decide are one transaction, and the composition names no exclusion to lease.
Deleted: Capability requirement 20. Execution Contract Database 2 owns it: a custody action's reads of a chain and the writes they decide are one transaction, and the composition names no lease to expire.
Deleted: Capability requirement 21. Execution Contract Database 2 owns it: a custody action's reads of a chain and the writes they decide are one transaction, and the composition names no exclusion for a host to lack.
Capability requirement 22: A deployment MAY supply the genesis lookup.
Capability requirement 23: A deployment MUST set the intent candidates cap.
Capability requirement 24: A deployment MUST set the input caps so the largest outcome payload built from capped inputs AND the intent candidates up to the intent candidates cap fits Audit Trail's payload cap.
NOTE: open 2026-11-04-e — the minted ids' width is no bound Provenance declares, so the payload cap is summed over capped inputs alone.
Capability requirement 25: The wired Audit Trail instance MUST expose the custody enumeration.
Capability requirement 26: The composition MUST NOT read a clock inside a custody action.
Capability requirement 27: The host MUST inject the reconciliation's now at the reconciliation's own seam once PER run.
Capability requirement 28: The deployment MUST supply the database PER Execution Contract Database 1 through 4.
Capability requirement 29: The composition MUST start ONLY IF the audit horizon EXCEEDS the escalation reach.
NOTE: watch the horizon against the escalation (Capability requirement 29).
Capability requirement 30: A deployment MUST set the clock offset allowance.
Capability requirement 31: IF the clock offset allowance EQUALS blank THEN the composition MUST refuse to start.
Capability requirement 32: EVERY reconciliation run's last write MUST land WITHIN the reconciliation run bound.
Capability requirement 33: A deployment MUST supply an enumeration of EVERY chain id Provenance holds.
NOTE: open 2026-11-06-k — the instant the enumeration is taken is stated nowhere.
Term audit retention policy: the policy reference configured on the composition’s single Audit Trail instance, governing the lifetime of every custody event — never the Provenance chain itself.
Term audit horizon: the audit retention policy’s horizon, past which a custody event’s payload is lawfully destroyed.
| Term seal cadence: Audit Trail’s per-event | interval-based | on-demand setting, which fixes each custody event’s covering range. |
Term custody completion bound: the longest a custody action may run from its intent to its outcome.
Term custody compensation window: the duration, from an orphan’s anchor instant, within which an orphan’s outcome event lands, past which the orphan is overdue and the reconciliation escalates it — custody_compensation_window in configuration, distinct from the substrate’s own compensation window.
Term custody reconciliation cadence: the interval between the reconciliation’s runs, beside the run at every restart — custody_reconciliation_cadence in configuration, distinct from the substrate’s own cadence.
Term reconciliation run bound: the deployment’s disclosed bound on one reconciliation run, from its start to its last write landing, over the largest custody records the deployment sizes for.
Term liveness sum: ((custody completion bound + custody reconciliation cadence) + reconciliation run bound).
Term escalation reach: ((((custody compensation window + clock offset allowance) + custody reconciliation cadence) + reconciliation run bound) + clock offset allowance) — how long after an orphan’s anchor instant the reconciliation may open the orphan’s unresolved finding.
NOTE: watch the reconciliation’s own run (Term liveness sum).
Term recovery identity: a deployment-provisioned actor reference and credential under which the reconciliation attests every write it makes.
Term genesis lookup: the deployment-supplied lookup from an artifact reference to the chains originated for it.
Term intent candidates cap: the most intent candidates one compensating event may name.
Term input caps: the composition-layer byte caps on the artifact reference, the custodian references, the recipient reference and the transformation descriptor.
Term clock offset allowance: the deployment’s declared bound on the difference between the reconciliation’s seam clock and the clock that stamps a recording instant.
Term now: the deployment’s clock reading at the call, as Execution Contract Logic confinement 11 declares it; a reconciliation run takes one reading of its own at the seam (Capability requirement 27).
Primitive policy
Primitive policy 1: IF the artifact reference EQUALS blank THEN [Originate Custody] MUST answer invalid-ref.
Primitive policy 2: IF a custodian reference EQUALS blank THEN the custody action MUST answer invalid-ref.
Primitive policy 3: IF the recipient reference EQUALS blank THEN [Disclose Custody] MUST answer invalid-ref.
Primitive policy 4: IF the transformation descriptor EQUALS blank THEN [Transform Custody] MUST answer invalid-descriptor.
Primitive policy 5: IF the genesis type IS NOT IN originated and received THEN [Originate Custody] MUST answer invalid-genesis-type.
Primitive policy 6: IF the credential EQUALS blank THEN the custody action MUST answer invalid-credential.
Primitive policy 7: IF a reference EXCEEDS the reference's input cap THEN the custody action MUST answer invalid-ref.
Primitive policy 8: IF the transformation descriptor EXCEEDS the descriptor's input cap THEN [Transform Custody] MUST answer invalid-descriptor.
Primitive policy 9: An action refused under Primitive policy 1 through 8 MUST NOT write.
Primitive policy 10: A custody action MUST NOT call a constituent BEFORE Primitive policy 1 through 8 pass.
Primitive policy 11: The composition MUST compare a chain id, an entry id AND an event id byte-exact.
Primitive policy 12: The composition MUST NOT normalize a caller string.
Primitive policy 13: The composition MUST NOT inspect a credential beyond Primitive policy 6.
Primitive policy 14: The composition MUST pass a custody action's acting custodian to the outcome's audit write as the actor reference.
Primitive policy 15: The composition MUST NOT carry the metadata in an audit write.
Primitive policy 16: The composition MUST pass the custodian reference of EVERY custody action to the intent's audit write as the actor reference, [Transfer Custody] excepted (Action wiring 20).
Term artifact reference: the opaque reference to the artifact whose custody a chain tracks — never validated against a registry here.
Term custodian reference: the opaque reference to the custodian performing or receiving a custody step.
Term recipient reference: the opaque reference to the party a disclosure names.
Term transformation descriptor: the non-blank description of a transformation the current custodian applied.
| Term genesis type: originated | received — Provenance’s. |
Term metadata: the optional opaque payload Originate Custody passes to Provenance unchanged.
Term credential: the acting custodian’s opaque credential material, validated by the substrate inside the audit write.
Term caller string: an artifact reference, a custodian reference, a recipient reference or a transformation descriptor.
Term acting custodian: the genesis custodian for Originate Custody; the outgoing custodian for Transfer Custody; the current custodian for Transform Custody, Disclose Custody and Archive Custody.
Audit arm
Audit arm 1: IF Audit Trail answers invalid-credential at an intent THEN the custody action MUST answer invalid-credential.
Audit arm 2: IF Audit Trail answers invalid-request carrying a step below step-3 at an intent THEN the custody action MUST answer invalid-request.
Audit arm 3: IF Audit Trail answers recording-failure carrying a step at an intent THEN the custody action MUST answer recording-failure carrying intent.
Audit arm 4: The composition MUST NOT retry an invalid-request answer.
Deleted: Audit arm 5. Execution Contract Database 1 owns it.
Audit arm 6: IF Audit Trail answers invalid-request(step-4) at an outcome THEN the invocation MUST read the outcome event back.
Audit arm 7: The read-back MUST match the outcome event carrying the custody action's action reference AND the intent event id.
Audit arm 8: The read-back MUST read the custody enumeration from the log's start.
Audit arm 9: IF the read-back finds the outcome event THEN the invocation MUST proceed as landed.
Audit arm 10: The deployment MUST alert on an outcome event read back as landed.
Deleted: Audit arm 11. Execution Contract Database 1 owns it.
Deleted: Audit arm 12. Execution Contract Database 1 owns it.
Deleted: Audit arm 13. Execution Contract Database 1 owns it.
Deleted: Audit arm 14. Reconciliation 25 and Execution Contract Database 1 own it.
Deleted: Audit arm 15. Reconciliation 26 and Execution Contract Database 1 own it.
Deleted: Audit arm 16. Execution Contract Database 2 owns it.
Deleted: Audit arm 17. Execution Contract Database 2 owns it: a custody action's attestations read the actor registry inside one transaction, so no invocation re-attests an outcome.
Deleted: Audit arm 18. Execution Contract Database 1 owns it: a custody action's writes commit together, so no invocation retries an outcome.
Audit arm 19: IF no outcome lands THEN the custody action MUST answer recording-failure carrying outcome.
Deleted: Audit arm 20. Execution Contract Database 2 owns it: a custody action's reads of a chain and the writes they decide are one transaction, and the composition names no exclusion to release.
Deleted: Audit arm 21. Execution Contract Database 2 owns it: a custody action's reads of a chain and the writes they decide are one transaction, and the composition names no lease to expire.
Deleted: Audit arm 22. Execution Contract Database 2 owns it: a custody action's reads of a chain and the writes they decide are one transaction, and the composition names no lease to expire.
Audit arm 23: A caller MUST read recording-failure carrying intent as a committed nothing.
Audit arm 24: A caller MUST read recording-failure carrying outcome as a committed custody entry.
Audit arm 25: IF Audit Trail answers invalid-request carrying a step above step-2 at an intent THEN the custody action MUST answer invalid-request.
Audit arm 26: IF Audit Trail answers invalid-request carrying a step below step-4 at an outcome THEN the deployment MUST alert on a deployment fault.
Audit arm 27: IF Audit Trail answers invalid-credential at an outcome THEN the custody action MUST answer recording-failure carrying outcome.
Audit arm 28: IF Audit Trail answers invalid-request carrying a step below step-4 at an outcome THEN the custody action MUST answer recording-failure carrying outcome.
Term intent: the custody action’s record before its Provenance write — custody.originate_intended, custody.transfer_intended, custody.transform_intended, custody.disclose_intended or custody.archive_intended; carrying the invocation’s parameters other than the credential and the metadata, and no constituent-minted id.
| Term position: intent | outcome — where a recording-failure sat: intent, nothing committed and the whole action may be retried; outcome, the custody entry exists and a re-run commits a second one. |
Term recovery intent: the custody.recovery_intended event — naming the chain id, the entry id and the intent reference an outcome under the recovery identity is about to bind.
Term recovery flag: cascade_recovery set to true — carried by an outcome attested under the recovery identity, and by no other.
Action wiring
originate_custody(artifact_ref, custodian_ref, genesis_type, credential, optional metadata)
answers custody result
refuses invalid-ref | invalid-genesis-type | state-unavailable | invalid-credential | invalid-request | recording-failure(position)
transfer_custody(chain_id, to_custodian_ref, credential)
answers custody result
refuses not-known | archived | invalid-ref | state-unavailable | invalid-credential | invalid-request | recording-failure(position)
transform_custody(chain_id, custodian_ref, transformation_descriptor, credential)
answers custody result
refuses not-known | archived | not-current-custodian | invalid-ref | invalid-descriptor | state-unavailable | invalid-credential | invalid-request | recording-failure(position)
disclose_custody(chain_id, custodian_ref, recipient_ref, credential)
answers custody result
refuses not-known | archived | not-current-custodian | invalid-ref | state-unavailable | invalid-credential | invalid-request | recording-failure(position)
archive_custody(chain_id, custodian_ref, credential)
answers custody result
refuses not-known | already-archived | not-current-custodian | invalid-ref | state-unavailable | invalid-credential | invalid-request | recording-failure(position)
verify_custody(chain_id, original_event_payloads)
answers custody proof
refuses not-known | state-unavailable
read(chain_id, optional query)
answers entry sequence
refuses not-known | invalid-query | state-unavailable
NOTE: open 2026-11-04-c — the closure table spec-format requires as this subsection’s first block is not drafted. NOTE: open 2026-11-06-j — the refuses lines of transform, disclose and archive list not-known and not-current-custodian ahead of invalid-credential, which the wiring answers first.
Term custody result: the entry id and the outcome event id, and for Originate Custody the chain id.
Term original event payloads: the caller’s map from an audit-log sequence number to the byte-exact payload Event Log holds at that position. NOTE: open 2026-11-06-i — the map has no cardinality cap.
Term entry sequence: the chain’s entries in sequence order — Provenance’s read answer.
Term custody proof: what Verify Custody answers — a Custody Proof.
Action wiring 1: A validated custody action MUST NOT call a Provenance write BEFORE the action's intent lands.
Deleted: Action wiring 2. Primitive policy 14 and Action wiring 12, 20, 27, 29 and 31 own it.
Action wiring 3: An intent MUST NOT carry a constituent-minted id.
Action wiring 4: An audit write of the composition MUST NOT carry a timestamp.
Action wiring 5: An outcome event MUST carry the chain id, the entry id, the intent event id AND the acting custodian.
Deleted: Action wiring 6. Execution Contract Database 2 owns it.
Action wiring 7: IF Provenance answers storage-failure THEN the custody action MUST answer recording-failure carrying intent (Execution Contract Constituent failure 1).
Action wiring 8: IF Provenance answers a Provenance refusal THEN the custody action MUST answer the Provenance refusal.
Action wiring 9: A refused Provenance write MUST NOT write beyond the intent.
Deleted: Action wiring 10. Execution Contract Database 1 owns it.
Action wiring 11: A bound custody action MUST write the binding AND answer the custody result.
Action wiring 12: A validated origination MUST record the originate intent carrying the artifact reference, the custodian reference AND the genesis type.
Action wiring 13: An admitted origination MUST call Provenance's originate with the artifact reference, the custodian reference, the genesis type AND the metadata.
Action wiring 14: A committed origination MUST read the genesis entry at sequence number one through Provenance's read.
Action wiring 15: A committed origination MUST record custody.originated for originated AND custody.received for received.
Action wiring 16: A validated transfer MUST NOT record the transfer intent BEFORE resolving the outgoing custodian through Provenance's read.
Action wiring 17: IF Provenance's read answers not-known THEN [Transfer Custody] MUST answer not-known.
Action wiring 18: IF the chain's latest entry carries archived THEN [Transfer Custody] MUST answer archived.
Action wiring 19: A transfer refused ahead of the transfer intent MUST NOT write.
Action wiring 20: A resolved transfer MUST record the transfer intent carrying the chain id, the resolved custodian as the outgoing custodian AND the incoming custodian under the resolved custodian as the actor reference.
Action wiring 21: An admitted transfer MUST call Provenance's transfer with the chain id AND the incoming custodian.
Deleted: Action wiring 22. Execution Contract Database 2 owns it: a transfer's resolution read and its write are one transaction, so the committed entry's outgoing custodian is the resolved custodian.
Deleted: Action wiring 23. Execution Contract Database 2 owns it.
Deleted: Action wiring 24. Execution Contract Database 2 owns it: a transfer's resolution read and its write are one transaction, so no transfer answers principal-divergence.
Deleted: Action wiring 25. Execution Contract Database 2 owns it: a transfer's resolution read and its write are one transaction, so no transfer answers principal-divergence.
Action wiring 26: A committed transfer MUST record custody.transferred under the resolved custodian as the actor reference.
Action wiring 27: A validated transformation MUST record the transform intent carrying the chain id, the custodian reference AND the transformation descriptor.
Action wiring 28: An admitted transformation MUST call Provenance's transform with the chain id, the custodian reference AND the transformation descriptor.
Action wiring 29: A validated disclosure MUST record the disclose intent carrying the chain id, the custodian reference AND the recipient reference.
Action wiring 30: An admitted disclosure MUST call Provenance's disclose with the chain id, the custodian reference AND the recipient reference.
Action wiring 31: A validated archival MUST record the archive intent carrying the chain id AND the custodian reference.
Action wiring 32: An admitted archival MUST call Provenance's archive with the chain id AND the custodian reference.
Action wiring 33: IF Provenance's read answers not-known THEN [Verify Custody] MUST answer not-known.
Action wiring 34: A found verification MUST replay the chain's entries in sequence order as the continuity check.
Action wiring 35: A found verification MUST resolve EVERY entry's binding through the binding index.
Action wiring 36: IF an entry carries no binding after the rebuild AND a purged later entry EXISTS THEN the entry MUST carry binding-unknown.
Action wiring 37: IF an entry carries no binding after the rebuild AND no purged later entry EXISTS THEN the entry MUST carry binding-gap.
Action wiring 38: A found verification MUST read EVERY bound event through the record read.
Action wiring 39: IF the record read answers Purged THEN [Verify Custody] MUST call the verification with the covering range's members the original event payloads carry.
NOTE: open 2026-11-04-d — the call may present an empty payload, which Audit Trail Primitive policy 27 forbids a caller; the purged answer lands before the presentation is read.
Action wiring 40: [Verify Custody] MUST NOT check the original event payloads for an event the record read answers Purged for.
Action wiring 56: IF the record read answers partially-purged coverage THEN [Verify Custody] MUST call the verification with the covering range's members the original event payloads carry.
Action wiring 41: IF a member of a live event's whole covering range IS NOT IN the original event payloads THEN the entry MUST carry payload-not-supplied AND the missing sequence numbers as the attestation verification.
Action wiring 42: IF EVERY member of a live event's whole covering range IS IN the original event payloads THEN [Verify Custody] MUST call the verification with the covering range's payloads in ascending sequence order.
NOTE: watch the purged co-member (Action wiring 41, Action wiring 56).
NOTE: open 2026-11-04-f — Action wiring 41's missing sequence numbers and Verdict 2's named entries carry no stated cardinality cap.
Action wiring 43: A found verification MUST carry the verification's outcome AND reason unchanged as the attestation verification.
Action wiring 44: A found verification MUST carry the verification's compensation-window qualifier beside the attestation verification.
Action wiring 45: IF the attestation verification EQUALS verified THEN a found verification MUST compare the entry's compared fields with the payload at the bound event's own position as the entry payload match.
Action wiring 46: IF the attestation verification IS NOT IN verified and failed-verification carrying purged THEN the entry payload match MUST carry unanchored.
Action wiring 57: IF the attestation verification EQUALS failed-verification carrying purged THEN the entry payload match MUST carry not-applicable carrying purged.
Action wiring 47: IF the record read answers neither Purged nor not-known THEN a found verification MUST compare the bound event's attesting actor with the entry's attested custodian as the actor match.
Action wiring 48: IF the record read answers neither Purged nor not-known THEN a found verification MUST carry the bound event's recovery flag as the recovery attestation.
Action wiring 49: IF the record read answers Purged THEN the recovery attestation MUST carry unknown carrying purged.
Action wiring 58: IF the record read answers Purged THEN the actor match MUST carry unknown carrying purged.
Action wiring 50: A found verification MUST carry the record read's retention state unchanged.
Action wiring 51: The composition MUST NOT fetch a payload for the verification.
Action wiring 52: [Verify Custody] MUST NOT change a presented payload.
Action wiring 53: [Verify Custody] MUST NOT record an audit event.
Action wiring 54: The read passthrough MUST answer Provenance's read for the chain id AND the query unchanged.
Action wiring 55: The read passthrough MUST NOT record an audit event.
Action wiring 59: IF a read the action makes answers state-unavailable THEN the action MUST answer state-unavailable PER Execution Contract Read 1.
Action wiring 60: A caller MUST read a refusal of a custody action as committing no custody entry, a recording-failure carrying outcome excepted (Audit arm 24, Term position).
Action wiring 61: An outcome event MUST carry EVERY compared field of the entry.
Action wiring 62: IF the record read answers not-known THEN the actor match AND the recovery attestation MUST carry unknown carrying not-known AND the entry payload match MUST carry unanchored.
Term validated custody action: a custody action whose inputs cleared Primitive policy.
| Term Provenance refusal: invalid-ref | invalid-genesis-type | not-known | archived | already-archived | not-current-custodian | invalid-descriptor — Provenance’s refusals of a write, relayed by name. |
Term bound custody action: a custody action whose outcome event landed.
Term validated origination: a validated custody action of Originate Custody.
Term admitted origination: a validated origination whose originate intent landed.
Term committed origination: an admitted origination whose Provenance originate answered the chain id.
Term validated transfer: a validated custody action of Transfer Custody.
Term resolved custodian: the incoming custodian of the chain’s latest transferred entry, or the genesis custodian where none — the holder the chain shows.
Term resolved transfer: a validated transfer Action wiring 17 and 18 did not refuse.
Term admitted transfer: a resolved transfer whose transfer intent landed.
Term committed transfer: an admitted transfer whose Provenance transfer answered the entry id.
Term validated transformation: a validated custody action of Transform Custody.
Term admitted transformation: a validated transformation whose transform intent landed.
Term validated disclosure: a validated custody action of Disclose Custody.
Term admitted disclosure: a validated disclosure whose disclose intent landed.
Term validated archival: a validated custody action of Archive Custody.
Term admitted archival: a validated archival whose archive intent landed.
Term purged later entry: an entry later in the chain, by sequence number, whose bound outcome event the record read answers Purged.
Term pre-horizon entry: an entry for which a purged later entry exists.
Term found verification: a Verify Custody call whose chain Provenance’s read found.
Term covering range: the sequence range the record read names for an event’s covering seal, or the event’s own sequence number in the unsealed tail.
Term whole covering range: a covering range the record read does not answer partially-purged coverage for.
Term live event: a bound event the record read answers Retained or unresolved in its compensation window.
| Term entry payload match: match | entry-payload-mismatch carrying the field | unanchored | not-applicable carrying purged. |
Term compared fields: the custodian reference of an originated, received, transformed, disclosed or archived entry; the outgoing and incoming custodians of a transferred entry; the transformation descriptor of a transformed entry; the recipient reference of a disclosed entry — never the metadata, which no outcome payload carries (Primitive policy 15).
Term attested custodian: a transferred entry’s outgoing custodian, or another entry’s custodian reference — the acting custodian of the action that appended the entry.
| Term actor match: match | match carrying recovery | attribution-mismatch carrying the actor reference | unknown carrying purged | unknown carrying not-known — match carrying recovery where the attesting actor is the recovery identity, the event carries the recovery flag and the payload names the entry’s attested custodian. |
| Term recovery attestation: true | false | unknown carrying purged | unknown carrying not-known — whether the bound event carries the recovery flag. |
Term read passthrough: Provenance’s read, passed through unchanged — the composition’s read.
Wiring decision
Wiring decision 1: The composition MUST follow EVERY Provenance custody entry with EXACTLY ONE outcome event naming the entry id.
Deleted: Wiring decision 2. Execution Contract Database 1 owns it.
Wiring decision 3: A validated origination MUST attest under the genesis custodian.
Wiring decision 4: A validated transfer MUST attest under the outgoing custodian.
Wiring decision 5: The outcome of [Transform Custody], [Disclose Custody] AND [Archive Custody] MUST attest under the current custodian.
Wiring decision 6: The composition MUST NOT attest a transfer under the incoming custodian.
WHY
Principle. A court-admissible or regulator-acceptable chain of custody needs four properties at once: continuity (no gap, hand to hand), verified attribution (each custodian non-repudiably identified), tamper evidence (any rewrite provable), and retention governance (kept for its regulatory lifetime, lawfully destroyable with a defensible record). Provenance supplies continuity; Audit Trail supplies the other three. Without a per-entry link the two record sets are parallel and unjoined — an auditor can verify continuity separately and attribution separately, but cannot prove all four together for the same entry.
Likely objection. Why not fold attribution, tamper evidence and retention into Provenance, so the chain itself carries all four?
Mechanism. Those are exactly the concepts Provenance extracted in its EOS (Essence of Software — Daniel Jackson’s framework for freestanding, composable concepts) pass to stay freestanding, naming each composing home in its own edge cases. The composition is where they re-converge, and efficiently: Audit Trail already wires all three into one substrate, so one binding — entry to event — carries the whole surface (Wiring decision 1). Absorbing the three into Provenance would end its freestanding status and make every unregulated chain carry the regulated overhead.
Result. Provable custody, which neither constituent gives alone: an auditor holding the binding index, the chain and the substrate answers the four questions — who held it, was the transfer unbroken, was the record altered, was it kept long enough.
Reconciliation
Reconciliation 1: The reconciliation MUST run at EVERY process start.
Reconciliation 2: The reconciliation MUST run every custody reconciliation cadence.
Deleted: Reconciliation 3. Execution Contract Database 1 owns it.
Reconciliation 4: IF EVERY intent the orphan entry's intent reference names IS an aged intent THEN the reconciliation MUST NOT write a recovery intent, a compensating event or a binding for the orphan entry.
Deleted: Reconciliation 5. Execution Contract Database 2 owns it.
Deleted: Reconciliation 6. Execution Contract Database 2 owns it: the reconciliation's reads of a chain and the writes they decide are one transaction, and the composition names no exclusion for another holder to hold.
Reconciliation 7: The reconciliation MUST read EVERY Provenance entry against the outcome events, through Provenance's read of EVERY chain id Capability requirement 33 supplies.
Reconciliation 8: The reconciliation MUST read EVERY unmatched intent against the Provenance chain.
Reconciliation 9: IF an unmatched intent realizes no orphan entry THEN the reconciliation MUST read the intent as a refused attempt.
Reconciliation 10: The reconciliation MUST pair an orphan entry to the unmatched intents realizing the entry.
Reconciliation 11: IF the candidate count EQUALS zero THEN the reconciliation MUST open an orphan-unpaired finding.
Reconciliation 12: IF the candidate count EQUALS zero THEN the reconciliation MUST NOT compensate the orphan.
Reconciliation 13: IF the candidate count EXCEEDS one THEN the compensating event MUST carry the intent candidates.
Reconciliation 14: IF the candidate count EXCEEDS the intent candidates cap THEN the reconciliation MUST escalate the orphan.
Reconciliation 15: IF the candidate count EXCEEDS the intent candidates cap THEN the reconciliation MUST NOT compensate the orphan.
Deleted: Reconciliation 16. Reconciliation 10 owns it: an orphan transferred entry pairs to the unmatched intents realizing it, as every other orphan entry does.
Reconciliation 17: IF the genesis lookup EXISTS THEN the reconciliation MUST pair a genesis intent to the chains the lookup answers by the custodian, the artifact reference AND the genesis type.
Reconciliation 18: IF the genesis lookup EQUALS blank THEN the reconciliation MUST open a genesis-unresolved finding for an unmatched genesis intent.
Deleted: Reconciliation 19. Execution Contract Database 2 and Reconciliation 11 own it: a transfer's resolution read and its write are one transaction, so no committed transfer's outgoing custodian differs from its intent's, and a transferred entry no intent realizes opens an orphan-unpaired finding.
Deleted: Reconciliation 20. Reconciliation 12 owns it: a transferred entry no intent realizes is never compensated.
Reconciliation 21: The reconciliation MUST NOT record a compensating event BEFORE the reconciliation's recovery intent lands.
Reconciliation 22: The reconciliation MUST attest EVERY write the reconciliation makes under the recovery identity.
Reconciliation 23: A compensating event MUST carry the recovery flag, the acting custodian AND the intent reference.
Reconciliation 24: The reconciliation MUST re-derive a compensating event from the Provenance entry AND the payload of an intent the entry's intent reference names that IS NOT an aged intent.
Reconciliation 25: The reconciliation MUST NOT record a compensating event BEFORE re-reading the custody enumeration for an outcome carrying the entry id.
NOTE: open 2026-11-05-b — a state-unavailable answer from this re-read or from Reconciliation 7's enumeration defers a compensation by another cadence; no bound in the liveness sum covers it and no rule names the deployment as its owner.
Reconciliation 26: IF the re-read finds an outcome carrying the entry id THEN the reconciliation MUST bind the entry to the found outcome.
Reconciliation 27: IF the reconciliation records a compensating event THEN the reconciliation MUST write the binding ONLY AFTER the compensating event lands.
Reconciliation 28: The reconciliation MUST escalate EVERY overdue orphan as an unresolved finding.
Reconciliation 29: IF the reconciliation escalates an orphan entry AND no escalation record naming the entry id EXISTS THEN the reconciliation MUST record an escalation record naming the entry id.
NOTE: open 2026-11-06-b — Reconciliation 28 escalates every overdue orphan and no rule skips an orphan an escalation record already names, so each run after the first may open another unresolved finding for one orphan.
Term reconciliation: the leg the composition runs outside every invocation, whose output — a bound orphan — an auditor awaits within the custody compensation window, and whose escalation an auditor awaits within the escalation reach. NOTE: watch the escalation outside the window (Term reconciliation).
Term aged intent: an intent whose (recording instant + audit horizon) PRECEDES the reconciliation’s now.
Term unmatched intent: an intent no outcome event names by intent event id or among its intent candidates.
Term refused attempt: an unmatched intent whose invocation committed nothing — the chain refused it.
Term orphan entry: a Provenance entry carrying no outcome event.
Term realize: an entry realizes an intent when the entry carries the intent’s chain id, event type and parameters — the incoming custodian of a transfer, the descriptor of a transformation, the recipient of a disclosure — from the custodian the intent authenticated, or, for a genesis entry, when Reconciliation 17 pairs the intent to the entry’s chain.
Term candidate count: how many unmatched intents realize one orphan entry.
Term intent candidates: the unmatched intents realizing one orphan entry where more than one does — intent_event_candidates.
Term intent reference: the intent event id where one intent realizes the entry, or the intent candidates.
Term anchor instant: the entry’s recording instant, as Provenance stamps it at the entry’s append — never an intent’s recording instant.
Term compensating event: an outcome event the reconciliation records under the recovery identity.
Term overdue orphan: an orphan entry whose (anchor instant + custody compensation window) PRECEDES the reconciliation’s now.
Term escalation: the surfacing of an orphan entry as a finding by the reconciliation, under Reconciliation 11, 14 or 28 — never the binding-duplicate finding of Composition state 9, which names no orphan entry.
Term escalation record: the custody.escalated event the reconciliation records — carrying the chain id and the entry id of an orphan entry it escalated.
Verdict
Verdict 1: The overall verdict MUST carry custody-proof-complete ONLY IF no class applies AND no entry's recovery attestation EQUALS true.
NOTE: open 2026-11-06-h — a chain whose every bound event is purged completes, and an omitted live member of a partially-purged range is never reported (Action wiring 41, 56).
Verdict 2: IF no class applies AND an entry's recovery attestation EQUALS true THEN the overall verdict MUST carry custody-proof-complete-with-recovered-attestations naming the entries whose recovery attestation EQUALS true.
Verdict 3: IF a class applies THEN the overall verdict MUST carry custody-proof-incomplete naming EVERY class that applies.
Verdict 4: IF the continuity check answers gap-detected THEN gap-detected MUST apply.
Verdict 5: IF an entry carries binding-gap THEN binding-gap MUST apply.
Verdict 6: IF an entry carries binding-unknown THEN binding-unknown MUST apply.
Verdict 7: IF the binding rebuild opens a binding-duplicate finding for the chain THEN binding-duplicate MUST apply.
Verdict 8: IF an entry payload match carries entry-payload-mismatch THEN entry-payload-mismatch MUST apply.
Verdict 9: IF an actor match carries attribution-mismatch THEN attribution-mismatch MUST apply.
Verdict 10: IF an attestation verification carries a failed attestation THEN attestation-failed MUST apply.
Verdict 11: IF an attestation verification carries a failed seal THEN seal-failed MUST apply.
Verdict 12: IF an attestation verification carries unsealed THEN unsealed MUST apply.
Verdict 13: IF the record read answers not-known for a bound event THEN binding-gap MUST apply.
Verdict 14: IF an attestation verification carries payload-not-supplied THEN payload-not-supplied MUST apply.
Verdict 15: IF an attestation verification carries an unavailable surface THEN availability MUST apply.
Verdict 16: IF an attestation verification carries partially-purged-coverage THEN partially-purged-coverage MUST apply.
Verdict 17: An attestation verification carrying purged MUST NOT apply as a class.
Verdict 18: A compensation-window qualifier MUST NOT apply as a class.
Verdict 19: An unknown MUST NOT apply as a class.
Term continuity check: the replay of the chain’s entries in sequence order with a running current-custodian cursor, answering continuous or gap-detected carrying the entry, the clause, the expected and the actual — a Continuity Check.
Term class: a failure class, an incomplete-input class or the standing class — each blocks Custody Proof Complete.
| Term failure class: gap-detected | binding-gap | binding-unknown | binding-duplicate | entry-payload-mismatch | attribution-mismatch | attestation-failed | seal-failed | unsealed. |
| Term incomplete-input class: payload-not-supplied | availability. |
Term standing class: partially-purged-coverage.
Term failed attestation: failed-verification carrying an attestation reason.
Term failed seal: failed-verification carrying seal-proof-invalid, seal-record-set-mismatch or seal-not-known.
Term unavailable surface: unverifiable carrying attestation-registry-unavailable or seal-mechanism-verification-unavailable.
Composition-level invariants
These emerge from the composition; none belongs to one constituent, and each needs Provenance and the Audit Trail substrate together.
- Invariant 1 — Attributed custody.
Invariant 1.1: IF no escalation record names a settled entry THEN the entry MUST carry EXACTLY ONE outcome event whose action reference matches the entry's event type. Invariant 1.2: IF an outcome event carries no recovery flag THEN the event MUST carry an attestation of the acting custodian. Invariant 1.3: IF an outcome event carries the recovery flag THEN the event MUST carry an attestation of the recovery identity AND the acting custodian in the payload. Invariant 1.4: EVERY outcome event MUST name an intent of the matching kind attested under the acting custodian.Term settled entry: a Provenance entry whose
(anchor instant + escalation reach)PRECEDES the instant of the auditor’s read. - Invariant 2 — Tamper-evident custody chain.
Invariant 2.1: IF an outcome event's sequence number DOES NOT EXCEED sealed through THEN EXACTLY ONE seal MUST cover the event. Invariant 2.2: IF a live outcome event is rewritten AND EVERY member of the event's whole covering range IS IN the original event payloads AND the verification DOES NOT answer an unavailable surface THEN [Verify Custody] MUST carry failed-verification as the attestation verification. - Invariant 3 — Retention-governed custody and honest disposal.
Invariant 3.1: IF no invalid-request(step-4) refusal kept a custody event THEN the event MUST carry a retention record from the audit write. Invariant 3.2: [Verify Custody] MUST carry failed-verification carrying purged as the attestation verification of a lawfully destroyed event. - Invariant 4 — Binding bijection.
Invariant 4.1: The composition MUST NOT leave an orphan entry unsurfaced. Invariant 4.2: EVERY compensable orphan MUST land an outcome event WITHIN the custody compensation window, an orphan the reconciliation escalated as an unresolved finding excepted (Reconciliation 28). Invariant 4.3: Two outcome events MUST NOT name one entry id. Invariant 4.4: EVERY outcome event MUST name an entry the Provenance chain carries. Invariant 4.5: The reconciliation MUST escalate EVERY uncompensable orphan. Deleted: Invariant 4.6. Reconciliation 12 and Invariant 4.5 own it: a transferred entry no intent realizes is never compensated, and it is escalated.Term compensable orphan: an orphan entry paired to at least one and at most the intent candidates cap of realizing intents, and — for a genesis orphan — in a deployment supplying the genesis lookup.
Term uncompensable orphan: an orphan entry that is not compensable.
NOTE: open 2026-11-05-a — Invariant 4.2’s exception for an escalated orphan lets a build that only escalates meet it; no rule obliges the compensating event inside the window.
- Invariant 5 — Provable custody.
Invariant 5.1: [Verify Custody] MUST answer the continuity, the attestation, the seal AND the retention of EVERY entry of the chain. - Invariant 7 — Authentication precedes commitment.
Invariant 7.1: The composition MUST NOT call a Provenance write BEFORE Audit Trail validates the acting custodian's credential at the intent. Invariant 7.2: A validated transfer MUST authenticate the outgoing custodian. Deleted: Invariant 6. Composes 5 owns it.
Continuity, attribution, seal and retention for the same entry give provable custody; the bijection makes it total; authentication before commitment makes the attribution exact.
Examples
Walkthrough — pharmaceutical chain of custody under 21 CFR Part 211
A pharmaceutical manufacturer uses this composition to track batch X-91 from manufacturing through distribution to dispensing. Configuration: audit_trail_retention_policy = pharma_7yr_predicate_rule (encoding the applicable predicate-rule retention minimum), seal_cadence = per-event (ALCOA — Attributable, Legible, Contemporaneous, Original, Accurate — requires contemporaneous, tamper-evident records; per-event sealing satisfies this at the strongest cadence).
-
Manufacturer originates the batch. The quality-release officer calls
originate_custody(artifact_ref="batch-x91", custodian_ref="manuf-lab-7", genesis_type=originated, credential=<qro_credential>)→{chain_id="chain-0041", entry_id="e1", event_id="ev_1001"}. Provenance opens the chain;AuditTrail.record_action(custody.originated, actor_ref="manuf-lab-7", qro_credential, ...)→ev_1001;entry_to_event["e1"] = "ev_1001". The genesis is attributed and sealed. -
Transfer to regional distributor.
transfer_custody(chain_id="chain-0041", to_custodian_ref="dist-region-3", credential=<lab7_credential>)→{entry_id="e2", event_id="ev_1002"}. Provenance writes the transferred entry (from_custodian_ref="manuf-lab-7",to_custodian_ref="dist-region-3"); the composition readsfrom_custodian_ref = "manuf-lab-7"and passes it asactor_refto Audit Trail. The outgoing custodian’s credential attests the release.entry_to_event["e2"] = "ev_1002". -
Distributor transfers to hospital pharmacy.
transfer_custody("chain-0041", "pharm-hosp-9", <dist_credential>)→{entry_id="e3", event_id="ev_1003"}.entry_to_event["e3"] = "ev_1003". -
Pharmacist transforms (dispenses dose).
transform_custody("chain-0041", "pharm-hosp-9", "dispensed 10mg dose into dispensing unit D44", <pharm_credential>)→{entry_id="e4", event_id="ev_1004"}.entry_to_event["e4"] = "ev_1004". -
Archive at terminal disposition.
archive_custody("chain-0041", "pharm-hosp-9", <pharm_credential>)→{entry_id="e5", event_id="ev_1005"}.entry_to_event["e5"] = "ev_1005". The Provenance chain is now Archived. -
FDA inspection. The FDA inspector asks: “Prove unbroken, attributed, tamper-evident, retention-compliant custody of batch X-91 under 21 CFR Part 211.” The system calls
verify_custody("chain-0041", original_event_payloads)— the payload map retrieved from cold storage, keyed by audit-logsequence_numberand covering every seal range the chain’s events fall in; the two-argument form is the signature. The result:continuity_check = continuous(everyfrom_custodian_refmatches the prior holder).- For each of
e1–e5:attestation_verification = verified(each attributed, each seal valid, each inRetainedretention state). overall_verdict = custody-proof-complete. The inspector sees a single provable answer to all four questions. Invariant 1 through 5 and Invariant 7 are the structural guarantees behind each field.
Legal evidence — physical exhibit at trial under FRE 901(b)(9)
A detective collects exhibit-A at a crime scene, handled through forensic lab, evidence room, and courtroom. Each step uses this composition with credential bound to the individual officer’s or lab’s verified identity via the Audit Trail’s Actor Identity. At trial, defense counsel challenges authentication under FRE 901(b)(9).
The prosecution calls verify_custody(chain_id_of_exhibit_A, original_event_payloads) — the payload map assembled from the evidence-room records, keyed by audit-log sequence_number over the covering ranges read_record names. The returned custody-proof:
continuity_check = continuous— every transfer’sfrom_custodian_refmatches the prior holder; no unattributed handler appears.- Every entry’s
attestation_verification = verified— each custodian’s credential was valid at the time of their action. overall_verdict = custody-proof-complete.
Defense counsel’s claim — that the exhibit passed through an unrecorded, unverified handler — has no structural basis. Invariant 1 (attributed custody) and Provenance Invariant 4 (custody continuity) are the structural rebuttal. The exhibit is authenticated under FRE 901(b)(9)’s process-or-system standard.
Rejection path — transfer by non-current custodian
A prior custodian, "manuf-lab-7", attempts to record a transformation after having transferred to "dist-region-3": transform_custody("chain-0041", "manuf-lab-7", "added label update", <lab7_credential>) → rejected(not-current-custodian). The action writes the intent record custody.transform_intended first — lab7_credential validates, so the attempt is authenticated and durable — and Provenance’s not-current-custodian guard then catches the call: no Provenance entry is written, no outcome event is recorded, no entry_to_event binding is created, and the binding bijection (Invariant 4) is preserved, because it is over entries and outcome events and an intent record is neither. The chain’s integrity is unchanged. The intent record stands, and that is the point: a former custodian attempting to alter a chain they no longer hold is precisely what a chain-of-custody trail should retain, and before the intent record existed the attempt left no trace anywhere. It surfaces among the unmatched intents Reconciliation 9 reads and is adjudicated against the Provenance chain, which shows the attempt was refused.
Rejection path — custody action on an Archived chain
After the pharmaceutical chain is Archived (example step 5), a downstream system attempts another transfer: transfer_custody("chain-0041", "disposal-unit-1", <credential>) → rejected(archived). Derived at the read-only resolution — which reads the terminal archived entry from chain state — and confirmed by Provenance’s own guard had the call proceeded. No state is written anywhere, and here that includes no intent record — because the archive preceded the call: Transfer Custody alone resolves existence and archival state before its intent record, so its not-known and archived arms are refused earlier than the other three chain-addressed actions’ constituent guards, which fire after theirs.
Failure path — custody step whose audit write fails, recovered
A hospital pharmacist transfers a controlled-substance tote: transfer_custody("chain-0107", "pharm-hosp-9", credential=<evid_room>). The intent custody.transfer_intended lands first — the evidence-room custodian’s credential validates, so what follows is an authenticated attempt on the record. Provenance.transfer writes the entry (entry_id = e5); the outcome’s AuditTrail.record_action returns recording-failure (the Event Log append answered nothing, its reply lost, and the (step) payload names step-3). The call returns rejected(recording-failure(outcome)) — the position telling the pharmacist the entry exists and the action must not be re-run — and in the same outcome the orphan — a committed custody entry with no audit event — surfaces to the compliance dashboard as a high-priority finding. Recovery is the reconciliation’s: at its next run it re-reads the log for an event carrying data.entry_id = e5 and finds none (had the lost append landed after all, it would bind e5 to that event instead), pairs e5 to the transfer intent it realizes (the intent’s chain and to_custodian_ref, from the custodian the intent authenticated), writes custody.recovery_intended, and lands the outcome under recovery_identity with cascade_recovery = true and the pharmacist named in data.custodian_ref. A later Verify Custody reports e5 with recovery attestation true, actor match match carrying recovery, and an Overall Verdict of custody-proof-complete-with-recovered-attestations([e5]) — structurally sound, surfaced for the auditor’s judgment. Had the credential been bad, this path would never have been reached: it is refused at the intent with nothing committed.
DEA controlled substance — 21 CFR Part 1304 inventory custody
A DEA-licensed pharmacy tracks each controlled substance dispensing as a separate artifact with its own chain under this composition. Each Originate Custody (pharmacy receives from supplier) → Transfer Custody (to dispensing cabinet) → Transform Custody (patient dispensing recorded) → Archive Custody sequence produces a complete attributed, sealed, retention-governed custody record per substance unit. A DEA compliance inspection calls Verify Custody for each in-scope chain and verifies the returned custody-proof-complete verdict. Every change of custody is attribution-stamped; every record is sealed; every record is retained per the applicable predicate rule.
Regulated adversarial scenarios
Three scenarios the composition must survive in regulated contexts:
Regulator audit — pharma: prove unbroken, attributed, tamper-evident, retention-honored custody of batch X-91 under 21 CFR Part 211.
An FDA inspector runs verify_custody("chain-0041", original_event_payloads). The returned custody-proof:
continuity_check = continuous: by Provenance Invariant 4, everyfrom_custodian_refequals the prior to_custodian_ref (or genesis custodian_ref). The chain cannot have a gap because Provenance readsfrom_custodian_reffrom chain state — no forged predecessor is possible.- Per-entry
attestation_verification = verified: by Invariant 1 (attributed custody), each entry has an outcome Audit Trail event whose Actor Identity attestation binds the custodian_ref to a credential — and, by Invariant 7, an intent event preceding the entry’s commit under that same custodian, which is the stronger claim under Part 11’s (FDA’s electronic-records rule, 21 CFR Part 11) Attributable limb: the identity was checked before the custody step, not merely recorded after it. The Tamper Evidence seal (via Audit Trail Invariant 3) confirms no entry was rewritten. - Per-entry
retention_state = Retained: by Invariant 3, every entry is under active retention per the configured predicate-rule policy; none has elapsed. overall_verdict = custody-proof-complete. The inspector’s four questions — unbroken custody, verified attribution, tamper-evidence, retention honored — have provable answers. Invariant 1 through 5 and Invariant 7 are the structural basis for each answer.
Disputed transaction — legal evidence: defense claims tampering or an unattributed handler under FRE 901(b)(9).
Defense counsel claims: (a) custody was broken (an unrecorded handler existed), or (b) a custodian was unverified (their identity was not attributable), or (c) the chain was tampered with (an entry was altered after the fact). The prosecution’s Verify Custody result addresses each:
- Claim (a):
continuity_check = continuous, resting on Provenance Invariant 4. If there were a gap, a transfer would have been recorded with afrom_custodian_refthat did not match the prior holder — structurally impossible because Provenance readsfrom_custodian_reffrom chain state. - Claim (b): per-entry
attestation_verification = verified, resting on Invariant 1 and Audit Trail Invariant 1. Each custodian’s credential was verified by Actor Identity before their custody step committed — the intent record makes it provable, and the outcome record’sintent_event_idnames it — so the claim is exact rather than approximate; the attestation is in the record. - Claim (c): per-entry
attestation_verificationseal check (Invariant 2, Audit Trail Invariant 3). Any rewrite of an audit event whose covering range the auditor presents produces afailed-verification(seal-proof-invalid)result. A rewrite of the Provenance chain itself is caught by the per-entry field-match clause (entry_payload_match): the sealed audit payload’s copies are the anchored values, so a rewritten transformation_descriptor or a consistently rewritten from/to pair reports entry-payload-mismatch, while a removed binding reports binding-gap. Direct cryptographic sealing of the chain records themselves remains the composing Tamper-Evidence-on-Provenance concept the constituent’s own edge case names.
Breach or incident investigation: reconstruct custody during an anomaly window and bound the time of tampering via the Audit Trail seal cadence.
An incident responder suspects a custody record was tampered with between two dates. The responder calls verify_custody(chain_id, original_event_payloads) with payloads from the suspect window. For each entry in the window:
attestation_verification = verifiedfor entries whose covering seal predates the anomaly: the seal is intact.attestation_verification = failed-verification(seal-proof-invalid)for entries whose seal was tampered with: the first such entry, and its seal’ssealed_atandanchored_at(from the Tamper Evidence atom via Audit Trail), bound the window. The most recent seal that returns verified end-to-end and the first that returnsfailed-verification(seal-proof-invalid)define the forensic window — a bound that holds when the deployment chains its seals (each seal covering its predecessor’s proof, a property of the substrate’s seal mechanism, not of its cadence); without seal chaining the tampering bound is per-seal, not global. Theseal_cadencegoverns the window’s resolution.attestation_verification = failed-verification(unsealed)for entries in the unsealed tail: these are covered by Event Log per-event immutability but not yet seal-verified; a tighterseal_cadencewould have narrowed this window. Where seals carryanchored_atfrom a TSA (Time-Stamp Authority — a trusted third party that signs proofs of when data existed) outside the adversary’s reach, the upper bound on tampering time is independently established. The forensic picture is provable.
Generation acceptance
An implementation is acceptable — in the regulator-acceptance sense — when the checks below are provable to an external auditor given the binding index, the Provenance chain records and the Audit Trail substrate’s records.
Conformance checks
Check 1.1: An auditor MUST find a binding for EVERY entry of the chain that is no pre-horizon entry, that is a settled entry AND that no escalation record names, resolving a missing live binding through the binding rebuild (Invariant 1.1).
Check 1.2: An auditor MUST find a passing verification for EVERY bound event whose covering range the auditor presents (Invariant 1.1).
NOTE: open 2026-11-06-e — the check cites Invariant 1.1 but tests the attestation, and Term passing verification admits unsealed and partially-purged-coverage.
Check 1.3: IF a bound event carries no recovery flag THEN an auditor MUST find the event's attesting actor matching the entry's attested custodian (Invariant 1.2).
Check 1.4: An auditor MUST find no event carrying the recovery flag attested under a custodian (Invariant 1.3).
Check 1.5: An auditor MUST find no outcome event attested under the recovery identity carrying no recovery flag (Invariant 1.3).
Check 2.1: An auditor MUST find EVERY outcome event the sealed-through position covers carrying EXACTLY ONE verifying seal (Invariant 2.1).
Deleted: Check 2.2. Verdict 12 owns it.
Check 3.1: An auditor MUST replay the chain's entries AND find the continuity check continuous (Invariant 5.1).
Check 4.1: IF no invalid-request(step-4) refusal kept a custody event THEN an auditor MUST find the custody event's retention state IS IN Retained and Purged through the record read (Invariant 3.1).
NOTE: open 2026-11-06-f — the check names Retained and Purged, while Term live event adds unresolved in its compensation window.
Check 4.2: An auditor MUST find EVERY Purged outcome event carrying a Purged retention record whose purge instant DOES NOT PRECEDE the retention deadline (Invariant 3.2).
Check 5.1: An auditor MUST enumerate the chain's outcome events through the custody enumeration AND find no two outcome events carrying one entry id (Invariant 4.3).
Check 5.2: An auditor MUST find EVERY outcome event naming an entry the chain carries (Invariant 4.4).
Check 5.3: An auditor MUST find EVERY live binding's outcome event carrying the binding's entry id (Composition state 3).
NOTE: watch the check over a derived index (Check 1.1, Check 5.3).
Deleted: Check 5.4. Execution Contract Database 3 and Execution Contract Database 4 own it.
Deleted: Check 5.5. Reconciliation 11 owns it.
Check 5.6: An auditor MUST find the custody compensation window exceeding the liveness sum (Capability requirement 12).
Check 5.7: IF no escalation record names an orphan entry that is a settled entry THEN an auditor MUST find the orphan's compensating event WITHIN the custody compensation window (Invariant 4.2).
Check 5.8: An auditor MUST find the audit horizon exceeding the escalation reach (Capability requirement 29).
Check 6.1: IF an outcome event's intent event id names a live intent THEN an auditor MUST find the outcome event preceded by the intent, of the matching kind, on the same chain, under the acting custodian (Invariant 7.1).
Deleted: Check 6.2. Reconciliation 9 owns it.
Deleted: Check 6.3. Check 6.1 owns it.
Check 7.1: An auditor MUST clear Provenance's Generation acceptance over the Provenance instance (Composes 5).
Check 7.2: An auditor MUST clear Audit Trail's Generation acceptance over the Audit Trail instance (Composes 5).
NOTE: EVERY check names the rule the check tests.
| Term passing verification: verified | failed-verification carrying purged | failed-verification carrying unsealed | unverifiable carrying partially-purged-coverage. |
Term live intent: an intent the record read answers Retained or unresolved in its compensation window.
External checks
External check 1: An auditor needing a custodian's authority confirmed MUST read the deployment's Permissions records (Non-goal 5).
External check 2: An auditor needing an artifact reference's meaning confirmed MUST read the host's artifact registry (Non-goal 6).
External check 3: An auditor needing a jurisdiction's substantive standard confirmed MUST read the evaluating authority's own determination (Non-goal 7).
Deleted: External check 4. Execution Contract Database 2 owns it.
Deleted: External check 5. Execution Contract Database 3 owns it.
Non-goals
Non-goal 1: The composition MUST NOT track a custody graph.
Non-goal 2: The composition MUST NOT hold two custodians of one chain at once.
Non-goal 3: The composition MUST NOT suspend a purge of the custody events.
Non-goal 4: The composition MUST NOT adjudicate an erasure request against a retention obligation.
Non-goal 5: The composition MUST NOT gate a custody action on the custodian's authority.
Non-goal 6: The composition MUST NOT validate an artifact reference against a registry.
Non-goal 7: The composition MUST NOT assess a jurisdiction's substantive standard.
Non-goal 8: The composition MUST NOT record an attempt the intent refused.
Non-goal 9: The composition MUST NOT record an audit event for a query.
Non-goal 10: The composition MUST NOT dispose of the Provenance chain.
Non-goal 11: The composition MUST NOT account for a disclosure's scope.
NOTE: the owners — a chain per sub-sample, each opened with a received genesis (Non-goal 1); a composing pattern (Non-goal 2); Erasure Coordination (forthcoming) (Non-goal 4); Permissions (Non-goal 5, External check 1); the evaluating authority (Non-goal 7, External check 3); a Failed-Attempt Log (forthcoming) (Non-goal 8); an access-logging wrapper (Non-goal 9); a Retention Window or Defensible Retention instance applied to the chain directly (Non-goal 10, Retention asymmetry 1); Selective Disclosure (Non-goal 11).
Edge cases
Clock semantics
Clock semantics 1: The composition MUST read the Event Log sequence AND the Provenance sequence as the order of the composition's records.
NOTE: open 2026-11-06-c — the two sequences have no cross comparison, so the binding rebuild's order across them is the build's.
Clock semantics 2: The composition MUST NOT claim a Provenance entry's recording instant AND the entry's event's recording instant equal.
Clock semantics 3: The reconciliation MUST widen a comparison of now against another seam's stamp by the clock offset allowance on both sides.
Concurrency
Deleted: Concurrency 1. Execution Contract Database 2 owns it.
Deleted: Concurrency 2. Execution Contract Database 2 owns it.
Retention asymmetry
Retention asymmetry 1: The composition MUST report an entry whose outcome event was lawfully purged as failed-verification carrying purged.
Pre-genesis custody
Pre-genesis custody 1: The composition MUST NOT claim custody ahead of a chain's genesis entry.
Rename
Rename 1: The composition MUST NOT change a recorded custodian reference for a rename.
Terms
Each [Term] marker above links to its term entry here; a term entry states what the concept is, in plain English, and its Kind, and — for a Field, a Parameter or a wire Member — carries the one Projection line where the concrete name stays visible on the page. The composition’s own concepts are the five custody actions it wraps (Originate Custody, Transfer Custody, Transform Custody, Disclose Custody, Archive Custody) and the emergent verification (Verify Custody); the structure that verification answers (Custody Proof) with its Continuity Check and Overall Verdict; and the verdict’s three values — Custody Proof Complete, custody-proof-complete-with-recovered-attestations and Custody Proof Incomplete. The constituents’ refusals are relayed by name. Attributed custody, the binding bijection and authentication before commitment are structural properties, not data. The deployment settings keep their wire spellings in configuration — audit_trail_retention_policy, seal_cadence, custody_completion_bound, custody_compensation_window, custody_reconciliation_cadence, reconciliation_run_bound, clock_offset_allowance, recovery_identity, genesis_lookup, intent_candidates_cap — and the binding index its own in an implementation, entry_to_event; the page names each in English where it declares it. (annotation.md Terms registry; representational only — it changes no guarantee, invariant, or behavior of the composition above.)
Vocabulary
Term actors: the composition; the constituents; the host; the reconciliation; a deployment; a regulated deployment; an auditor; a caller; a custodian; an invocation; a custody action; a validated custody action; a bound custody action; a validated origination; an admitted origination; a committed origination; a validated transfer; a resolved transfer; an admitted transfer; a committed transfer; a validated transformation; an admitted transformation; a validated disclosure; an admitted disclosure; a validated archival; an admitted archival.
Term records: the intents, outcome events, recovery intents and escalation records the composition records through the audit write — each an Event Log event carrying one action reference below.
Term record verbs: account, adjudicate, alert, answer, apply, assess, attest, authenticate, bind, call, carry, change, check, claim, classify, clear, compare, compensate, compose, cover, disclose, dispose, duplicate, enumerate, escalate, expose, fetch, find, follow, gate, hold, inherit, inject, inspect, key, land, leave, match, name, normalize, open, override, pair, pass, proceed, provision, re-derive, re-read, reach, read, rebuild, record, refuse, replay, report, resolve, retry, run, select, serve, set, start, store, supply, suspend, track, validate, widen, write.
| Term value sets: action reference = custody.originate_intended | custody.transfer_intended | custody.transform_intended | custody.disclose_intended | custody.archive_intended | custody.originated | custody.received | custody.transferred | custody.transformed | custody.disclosed | custody.archived | custody.recovery_intended | custody.escalated. finding = binding-duplicate | orphan-unpaired | genesis-unresolved | unresolved. The rest are declared where the section that owns each declares it: genesis type, position, entry payload match, actor match, recovery attestation, class, failure class, incomplete-input class, standing class, Provenance refusal. |
Term bounds: custody completion bound (custody_completion_bound), custody compensation window (custody_compensation_window), reconciliation run bound (reconciliation_run_bound), clock offset allowance (clock_offset_allowance), intent candidates cap (intent_candidates_cap), input caps, audit horizon (audit_trail_retention_policy).
Term cadences: custody reconciliation cadence (custody_reconciliation_cadence), seal cadence (seal_cadence).
Term qualifiers: migrated — rewritten in GRACE lang v0.61 (2026-09-24).
Term terms: composition, constituents, transitive atoms, custody enumeration, record read, audit write, verification, custody action, entry id, chain id, binding index, binding, outcome event, live binding, purged binding, binding rebuild, audit retention policy, audit horizon, seal cadence, custody completion bound, custody compensation window, custody reconciliation cadence, reconciliation run bound, liveness sum, escalation reach, clock offset allowance, now, recovery identity, genesis lookup, intent candidates cap, input caps, artifact reference, custodian reference, recipient reference, transformation descriptor, genesis type, metadata, credential, caller string, acting custodian, intent, position, recovery intent, recovery flag, custody result, original event payloads, entry sequence, custody proof, validated custody action, Provenance refusal, bound custody action, validated origination, admitted origination, committed origination, validated transfer, resolved custodian, resolved transfer, admitted transfer, committed transfer, validated transformation, admitted transformation, validated disclosure, admitted disclosure, validated archival, admitted archival, purged later entry, pre-horizon entry, found verification, covering range, whole covering range, live event, entry payload match, compared fields, attested custodian, actor match, recovery attestation, read passthrough, reconciliation, aged intent, unmatched intent, refused attempt, orphan entry, realize, candidate count, intent candidates, intent reference, anchor instant, compensating event, overdue orphan, escalation, escalation record, continuity check, class, failure class, incomplete-input class, standing class, failed attestation, failed seal, unavailable surface, settled entry, compensable orphan, uncompensable orphan, passing verification, live intent.
Term cited: Execution Contract Conformance 8 — the recursive inheritance of a constituent’s guarantees. Execution Contract Logic confinement 7 — the clock’s guarantees are the deployment’s. Execution Contract Logic confinement 11 — the clock reading at the call. Execution Contract Read 1 — a failed read answers state-unavailable. The section titled Composition state in execution-contract.md — the derived-index and extraction-pending classifications. The section titled Compositions of compositions in spec-format.md — the transitive atoms. originate, transfer, transform, disclose, archive, read, entry, event type, outgoing custodian, incoming custodian, genesis custodian, sequence number, Archived, Open, invalid-ref, invalid-genesis-type, invalid-descriptor, not-known, archived, already-archived, not-current-custodian, storage-failure, invalid-query: Provenance. record_action, read_record, verify_record, payload cap, sealed through, unsealed tail, step-2, step-3, step-4, invalid-credential, invalid-request, recording-failure, verified, failed-verification, unverifiable, purged, partially-purged coverage, compensation-window, Retained, Purged, Erasure Tombstone: Audit Trail. append, recording instant: Event Log.
Term composing patterns: Defensible Retention; Selective Disclosure; Permissions; Multi-Party Approval.
Originate Custody
The composition action that opens a new Provenance custody chain for an artifact under a genesis custodian, binding the genesis entry to an attributed Audit Trail event (custody.originated). Returns {chain_id, entry_id, event_id}; the genesis custodian’s credential attests the artifact entered custody under their hand.
Kind: Operation
Transfer Custody
The composition action that records a hand-to-hand transfer to a new custodian. The outgoing custodian attests the release — from_custodian_ref is read from Provenance’s chain state (unforgeable, Provenance Invariant 4) and bound as the actor on the custody.transferred event; the incoming custodian attests their own later actions.
Kind: Operation
Transform Custody
The composition action recording a transformation of the artifact by the current custodian (Provenance enforces holder-only), bound to an attributed custody.transformed event.
Kind: Operation
Disclose Custody
The composition action recording a disclosure of the artifact by the current custodian to a named recipient — custody is not transferred — bound to an attributed custody.disclosed event.
Kind: Operation
Archive Custody
The composition action recording the terminal disposition by the current custodian and transitioning the chain to Archived — no further entries are accepted — bound to an attributed custody.archived event.
Kind: Operation
Verify Custody
The composition’s emergent verification: construct the provable Custody Proof for a chain by replaying Provenance continuity (Continuity Check) and, for each entry, verifying its bound Audit Trail event’s attestation, seal, and retention. Neither constituent can answer it alone; the caller presents the original event payloads (Audit Trail’s verification asymmetry).
Kind: Operation
Custody Proof
The structured artifact Verify Custody returns: the chain’s state, its Continuity Check result, an ordered per-entry sequence of attestation / seal / retention verifications, and a summary Overall Verdict. What makes unbroken + attributed + tamper-evident + retention-governed custody provable, which neither Provenance nor Audit Trail provides alone.
Kind: Type Role: provable custody
Continuity Check
The Custody Proof field carrying the result of replaying the Provenance entries in order and confirming every transfer’s from_custodian_ref equals the custodian in effect immediately before it (Provenance Invariant 4, replayed on demand): continuous, or gap-detected naming the entry and the mismatch.
Kind: Field Field of: the custody proof Role: the custody-continuity result Projection: continuity_check
Overall Verdict
The Custody Proof’s summary field, with three values: Custody Proof Complete when continuity holds, every entry is bound, every supplied verification passes (or is lawful destruction), and no entry is recovery-attested; custody-proof-complete-with-recovered-attestations(entry_ids) — the same, except the named entries carry recovery attestations in place of the custodian’s own, surfaced for the auditor’s judgment; else Custody Proof Incomplete naming the specific failure, incomplete-input, and standing-coverage classes.
Kind: Field Field of: the custody proof Role: the summary verdict Projection: overall_verdict
Custody Proof Complete
The Overall Verdict value when the chain’s continuity is unbroken, every entry has its audit-event binding, and every supplied attestation verifies (or is a lawful failed-verification(purged)) — the full custody is provable.
Kind: Member Member of: the overall verdict Role: Verdict Projection: custody-proof-complete
Custody Proof Incomplete
The Overall Verdict value when at least one failure, incomplete-input, or standing-coverage class applies (a continuity gap, a binding gap or duplicate, an entry–payload mismatch, an attribution mismatch, a failed attestation, a seal failure, a not-supplied payload, an availability condition, or partially-purged seal coverage) — the proof is not fully self-proving, and the verdict names why per class.
Kind: Member Member of: the overall verdict Role: Verdict Projection: custody-proof-incomplete
Standards references
This composition is the structural form of the chain-of-custody requirement across its canonical domains:
-
FDA 21 CFR Part 211 (Current Good Manufacturing Practice — Finished Pharmaceuticals) — requires an unbroken, attributed, tamper-protected, retention-compliant chain of custody from manufacture through distribution for drug substances and products. The composition’s Originate Custody → Transfer Custody → Archive Custody sequence, with per-entry attribution and seal via the Audit Trail substrate, is the operational form of Part 211’s custodial recording requirement. The ALCOA / ALCOA+ (Attributable, Contemporaneous, Original, Accurate, plus Complete, Consistent, Enduring, Available) principles are all satisfied simultaneously by the four-atom stack inside the Audit Trail substrate, applied to every custody entry.
-
DEA 21 CFR Part 1304 (Controlled Substance Inventory Records) — requires complete, accurate records of every change of custody of a controlled substance. Invariant 1 (attributed custody) and Invariant 5 (provable custody) are the structural implementation of this requirement at every transfer step.
-
Federal Rules of Evidence 901(b)(9) (Authenticating or Identifying Evidence — Process or System) — the US evidentiary rule for authenticating physical or electronic evidence via chain-of-custody records. The composition’s Verify Custody result —
continuity_check = continuousplus per-entryattestation_verification = verified— is the structural form of the unbroken, attributed chain courts require for authentication. Provenance Invariant 4 (custody continuity) and Invariant 1 (attributed custody) are the rebuttal to authentication challenges. -
ISO 23081 (Information and documentation — Managing metadata for records) — the ISO (International Organization for Standardization) standard on records-management metadata. The composition’s custody entries map directly to the origin, transfer, transformation, and disclosure metadata elements ISO 23081 specifies; the Audit Trail attribution and retention satisfy ISO 23081’s authenticity and retention obligations.
-
W3C (the World Wide Web Consortium) PROV (Provenance Data Model — W3C’s RDF (Resource Description Framework)-based standard for representing provenance as a directed acyclic graph) — this composition implements the linear single-entity spine of a W3C PROV graph: the
wasGeneratedBy,used, andwasAttributedTorelationships along a single entity’s chain. The deliberate non-goal ofwasDerivedFrom(DAG derivation) and artifact splitting are explicitly out of scope relative to the full PROV model. -
SEC Rule 17a-4 (Records to be preserved by certain exchange members, brokers, and dealers) — requires broker-dealer records (including custody records for financial instruments) to be preserved in a non-rewriteable, non-erasable format. The Audit Trail substrate’s Tamper Evidence (Invariant 2) satisfies the integrity requirement; the configured
audit_trail_retention_policysatisfies the lifetime requirement. -
GDPR (EU General Data Protection Regulation) Article 17 — the right to erasure. An erasure request colliding with an FDA, DEA or court retention obligation over the custody events is a composed Erasure Coordination pattern’s decision, not this composition’s (Non-goal 4).
this composition inherits the broader standards compliance of its constituents:
-
Through Audit Trail (and its transitive atoms): SOX (Sarbanes-Oxley Act) section 802 record retention, HIPAA (Health Insurance Portability and Accountability Act) section 164.312(b) audit controls, PCI DSS (Payment Card Industry Data Security Standard) Requirement 10, 21 CFR Part 11 electronic records, ISO/IEC (International Electrotechnical Commission) 27001 clause A.12.4 logging and monitoring, GDPR Articles 30 and 32, and the full Audit Trail standards inheritance. Deployments composing this composition for pharmaceutical or legal-evidence purposes receive these as the substrate’s contribution; they are framed as inherited, not as this composition’s own primary standards anchors.
-
Through Provenance: ISO 23081, W3C PROV, FDA 21 CFR Part 211, DEA 21 CFR Part 1304, FRE 901(b)(9), and SEC Rule 17a-4 at the structural custody-chain layer. This composition lifts these to the full attributed+sealed+retention-governed form those standards actually require but that Provenance alone cannot satisfy.
Status
grounded on Final Critique 17 — 2026-11-06 at dbb9804 — see the Ledger.
Ledger
status: grounded on Final Critique 17 — 2026-11-06 at dbb9804
formal: verified — chain-of-custody.tla + 8 twins + 6 probes, 2026-10-27
last gate: 2026-11-06 — Final Critique 17, a delta round, frozen at dbb9804 — foundational count 0, clean, one sample; 2026-11-06 — Final Critique 16, delta — foundational count 1, not clean, no sample
open:
- 2026-11-04-c · refining · Action wiring · spec-format's closure table (the subsection's first block) is not drafted; its cells land every constituent answer, and this settle changed Audit arm 25, 27 and 28 and Action wiring 59 → draft it once on the cured text, in the delta round that reads the cures
- 2026-11-04-d · refining · Action wiring 39, 56 · the verification call may present an empty payload, which Audit Trail Primitive policy 27 forbids a caller, though Verify Record step 1.2 lands the purged answer before the presentation is read → decide whether Audit Trail states that order as a caller exemption
- 2026-11-04-e · refining · Capability requirement 24 · the minted ids' width is no bound Provenance declares, so the payload cap sums capped inputs alone → decide whether Provenance declares an id width
- 2026-11-04-f · refining · Action wiring 41, Verdict 2 · the missing sequence numbers and the named entries are set-valued outcomes with no stated cardinality cap (the sizing check) → state each as at most the covering range's size and the chain's entries
- 2026-11-05-a · refining · Invariant 4.2 · the exception for an orphan the reconciliation escalated (Reconciliation 28) lets a reconciliation that never compensates meet the invariant by escalating every orphan once its window passes, and no rule obliges the reconciliation to compensate a compensable orphan inside the window → decide whether a Reconciliation rule obliges the compensating event on a compensable orphan whose window is open
- 2026-11-05-b · refining · Reconciliation 25, 7 · a state-unavailable answer from the re-read or the enumeration defers a compensation by another cadence, which no bound in the liveness sum covers → decide whether the liveness sum adds one cadence for a run that answers state-unavailable, or the deployment declares a retry bound
- 2026-11-06-b · refining · Reconciliation 28, 29 · Reconciliation 28 escalates every overdue orphan and no rule skips an orphan an escalation record already names, so each run after the first may open another unresolved finding for one orphan (the record and the finding commit together: Execution Contract Database 2 through the Reconciliation 6 tombstone) → decide whether Reconciliation 28 skips an orphan an escalation record names or findings are deduplicated by entry id
- 2026-11-06-c · refining · Clock semantics 1 · the Event Log sequence and the Provenance sequence have no cross comparison, so the binding rebuild's order across them is the build's → decide the ordering key a rebuild across both sequences uses
- 2026-11-06-e · refining · Check 1.2 · it cites Invariant 1.1 but tests the attestation, and Term passing verification admits unsealed and partially-purged-coverage, which Verdict 12 and 16 make classes → decide which invariant the check tests and whether a passing verification excludes the two
- 2026-11-06-f · refining · Check 4.1 · it names the states Retained and Purged, while Term live event adds unresolved in its compensation window → decide the retention states the check admits
- 2026-11-06-h · refining · Verdict 1, Action wiring 41, 56 · a chain whose every bound event is purged completes, and an omitted live member of a partially-purged range is never reported as payload-not-supplied → decide whether a standing class names an all-purged chain and whether Action wiring 41 reaches a partially-purged range
- 2026-11-06-i · refining · Term original event payloads · the caller's map has no cardinality cap, so the verification input's size is unbounded for a large covering range → state the cap
- 2026-11-06-j · refining · Action wiring refuses lines · transform, disclose and archive list not-known and not-current-custodian ahead of invalid-credential, which the wiring answers first (Execution Contract Refusal 1) → reorder the refuses lines or state the answer order
- 2026-11-06-k · refining · Capability requirement 33, Reconciliation 7 · the instant the chain id enumeration is taken (run start, each chain read) is stated nowhere, so a chain minted mid-run is read by the next run, within the liveness sum's one cadence → decide the instant Capability requirement 33's enumeration is taken
Decisions
Directional changes only — the turns a future reader must know the pattern took, and why. Everything smaller lives in the commit that made it: git log -- compositions/chain-of-custody.md; the migration and the earlier gate records that got it here are in its history.
- 2026-10-27 — The principal divergence retired: the one transaction forecloses it. Chose: the transfer leg paired by Term realize, like every other leg, so an orphan transferred entry pairs to the unmatched intents it realizes, from the custodian each authenticated, and a transfer written outside the composition’s surface that no intent realizes lands orphan-unpaired, surfaced and escalated (Reconciliation 10 through 12, Invariant 4.5); Reconciliation 16, 19 and 20, Invariant 4.6, the principal-divergence finding, Term confirmed transfer and Action wiring 22’s confirmation read deleted, each tombstoned to its owner, with Action wiring 26 recording custody.transferred under the resolved custodian; Term compensable orphan paired to at least one intent, so the zero-candidate orphan Reconciliation 12 never compensates is one Invariant 4.5 escalates; and the formal model re-derived for this protocol, its in-invocation retry gone (chain-of-custody.tla, 8 twins, 6 probes). Rules 224 to 219. Over: keeping the divergence at the reconciliation and opening it only where no paired intent realizes the entry, the first cure open line 2026-10-27-a named. Because: the maintainer ruled 2026-10-27 that the one transaction already forecloses a divergence at the invocation: a transfer’s resolution read and its write are one transaction (Execution Contract Database 2), so a committed transfer’s outgoing custodian is its intent’s, and what the reconciliation’s comparison still caught was a write outside the composition’s surface, which orphan-unpaired lands, and a false divergence between two orphans of one chain (2026-10-27-a). It turns the 2026-08-30 entry’s divergence landing, which the 2026-10-20 rewrite kept. Open lines 2026-08-30-v and 2026-10-27-a close as dissolved, and 2026-08-29-a closes: the re-derived model carries the recovery identity, the recovery intent and the aged-intent edge.
- 2026-10-20 — The store’s guarantees cited, not restated, and a custody action’s writes one transaction. Chose: Capability requirement 28 citing Execution Contract Store 1 through 4 for the store the chain, the substrate and the binding index keep their records in; the rules restating what that store guarantees tombstoned to it — the binding index’s durability and the auditor’s reading of it, the chain exclusion with its supply, holding, release, lease and the refusal to start without it, an invocation’s release of it and the expiry of its lease, the reconciliation’s take of it and the host’s declaration an auditor read (Composition state 12, Capability requirement 13, 14, 16 through 21, Action wiring 6, Audit arm 20 through 22, Reconciliation 5, 6, Concurrency 1, 2, External check 4, 5); the rule denying that a Provenance entry and its outcome event commit together, which Store 1 as ruled contradicts (Wiring decision 2); and the machinery that existed only because a crash, a split write or an invocation seen between its writes could leave a partial: the reconciliation’s lower edge, the outcome retry attempts and the in-invocation retries with their pre-check and adoption, the read-back of a recording-failure at the substrate’s retention step, which Audit Trail’s own rewrite made commit nothing, the escalation of an invalid-request(step-4) whose event the read-back cannot find, which Audit Trail’s evidence rule leaves appended, a recording-failure carrying outcome for a Provenance read that fails after the write, the mid-flight revocation and its re-attestation, which the one transaction’s read of the actor registry forecloses, the principal divergence at Transfer Custody, which the one transaction’s resolution read forecloses, and the auditor’s durability reading of an unbound pre-horizon entry (Reconciliation 3, Capability requirement 8, Audit arm 5, 11 through 18, Action wiring 10, 23 through 25, Check 5.4). Rules 263 to 228. Over: a Provenance write and an outcome append no transaction could span, so that each custody action was ordered across a durability boundary — the entry committed first, its outcome appended second — with a chain exclusion the host held across the invocation’s retries as a lease, and a reconciliation that took the exclusion and paired what a crash between the writes left. Because: the maintainer ruled 2026-10-12 that the corpus takes a store’s specification, never the store, and 2026-10-20 that Store 1 binds every store write of one composition action, its constituents’ included, while a refused attempt a page keeps as evidence stays a domain choice, written in the same transaction as the refusal. The Provenance write and the audit append are store writes, Provenance and Audit Trail each supplying theirs under Store 1 through 4, so a custody action’s intent, entry and outcome commit together; and the chain exclusion, a host mutual exclusion over store-only work, is Store 2’s, under which a custody action’s reads of a chain and the writes they decide are one transaction. An Audit Trail refusal at an outcome still leaves the custody entry committed and its record owed (Audit arm 19, 24, 26), as Audit Trail’s own refusals leave their evidence (Audit Trail record action step 7.11), so what closes it stays: the reconciliation’s pairing, its candidates and their cap, the genesis lookup, the recovery identity behind a recovery intent, the re-read before a compensating event, the custody compensation window and the liveness sum it must exceed, and the escalation; the custody completion bound stays with them, since the liveness sum and the pairing still read it (Capability requirement 12, Reconciliation 16, 17). The binding index stays a derived index outside every action’s transaction, as obligation 2 of the section titled Composition state in
execution-contract.mdkeeps it, so its rebuild on a miss and the binding-unknown reading of an entry no rebuild can reach stay (Composition state 7, Action wiring 36). Custody records are ordered by the Event Log’s and Provenance’s own sequence numbers (Clock semantics 1) and no rule reads a record as it stood at a past commit, so the composition cites neither Store 5 nor Order 1. What the composition does for its domain is unchanged. - 2026-08-30 — One writer per entry, the purged answer first, the attestor named, the divergence landed, the genesis leg honest about what it can find. Chose: an in-invocation retry bounded by
outcome_retry_attemptsand a scan that takes the per-chain section for every chain it touches, so the invocation and the scan never both land an outcome for one entry, with a binding-duplicate collision rule on the rebuild and check 5 quantified over the log; Verify Custody branching on retention state before any composition-side membership check, so a purged entry landsfailed-verification(purged)and Custody Proof Complete stays reachable after a purge; anactor_matchfield and check-1 clause comparing the attestation’s survivingactor_refto the entry’s custodian;rejected(principal-divergence)at Transfer Custody step 5 with no outcome written and a scan that never compensates it;genesis_lookupdeclared as an instance capability requirement, the genesis leg downgraded to surfaced without it;recording-failure(intent | outcome)on every signature; the liveness inequality written out withoutcome_write_latency;intent_candidates_capsizing the compensation envelope; and, on the closure check,chain_serializationdeclared as the instance capability the one-writer rule rests on, with a lease exactly the bound long whose expiry is the invocation’s terminus, the transfer leg pairing by chain and recipient before it compares actors so a divergence is reachable at the scan, the zero-match landing (escalate, never compensate), and checks 2 and 4 quantified over the log. Over: “retry until it lands” beside a scan starting at the bound; a membership check ahead of the constituent’s purged short-circuit; a check that confirmed the credential was valid and not whose it was; a code in the signature with no landing; a genesis leg that presumed a lookup Provenance declines; a bare token on both sides of the commit. Because: two compensators over one act land two outcomes the seal then protects; a lawfully destroyed payload is not one the caller failed to supply; a valid credential attesting a step its holder did not perform is the forgery the proof exists to catch; a committed entry nobody authenticated cannot be attested later without inventing an attribution; and a caller who cannot tell intent from outcome re-runs a committed act (the frozen rules of 2026-08-30 — A compensator is exclusive, Lawful destruction is answered before absence, A composition’s own rejection arm carries the retry bit, Liveness is arithmetic, An outcome is sized before the intent, and Capability provenance frozen — with §Authentication precedence’s binding half and §A transcribed rejection arm’s fourth tell). -
2026-08-29 — The scan is bounded at both edges, pairs by parameters and names candidates, and writes as the recovery identity behind a recovery record; the outcome’s step decides its landing. Chose:
custody_completion_boundbelow and the audit horizon above for the reconciliation scan; an orphan entry paired to its intent by chain, custodian, and the parameters the entry realizes, withintent_event_candidateswhere more than one unmatched intent matches; every scan write underrecovery_identitybehindcustody.recovery_intended(the in-invocation retry keeping the custodian’s credential and no marker);recording-failure(step-4)and the retention-source invalid-request at an outcome record read back and treated as landed at all five actions;compensation_window,reconciliation_cadenceandindex_durabilitydeclared; Invariant 4’s “committed atomically or” restated as ordered. Over: an unbounded scan whose crash-side retry had no identity it could honestly use, and “all three arms surface as recording-failure” at the outcome. Because: an unbounded scan mints a second outcome beside an in-flight invocation’s and re-emits lawful destruction as orphans; the custodian’s credential is never persisted; and the substrate’s step-4 arm means the event exists (the frozen rules of 2026-08-29 — A reconciliation is bounded at both ends, Intents pair with outcomes, Recovery commits under a declared service identity, A transcribed rejection arm keeps its payload). - 2026-08-27 — The verification presentation is keyed by audit-log position, not by custody entry. Chose: original_event_payloads keyed by
sequence_number, with Verify Custody assembling each entry’s presentation from the covering rangeread_recordnames. Over: a map keyed byentry_idholding one payload per entry, or a wiring rule requiring per-event cadence. Because: the substrate’s rule is that a seal commits to a sequence range whose members include intent records and unrelated actions carrying noentry_id; an entry-keyed map cannot present that set under any cadence but per-event, and pinning the cadence would trade a representational fix for a deployment constraint. - 2026-06-11 — Invariant 4’s compensated arm is in the model, not idealized away. Chose: re-derive the model over sequential sub-writes with compensation. Over: the original model, which committed the sub-writes as one atomic action. Because: the Provenance write is irreversible and the audit append cannot be withdrawn, so the compensated path is the design.
- 2026-08-26 — Transfer Custody resolves the outgoing custodian before it authenticates, and authenticates before it commits. Chose: a read-only resolution step, then the intent record under the outgoing custodian’s credential, then the Provenance call; invalid-credential restored to all five signatures, reversing Final Critique 5’s F2 on new grounds. Over: declaring the pre-check “a deployment obligation” (Final Critique 5’s F-6 closure), or authenticating the incoming caller, the only principal available earlier. Because: a declaration discharges nothing the records can show, and the guarantee names the outgoing custodian, a principal produced by the commit.
NOTE: End of Chain of Custody.