Internet-Draft VTL September 2026
El Khatabi Expires 10 March 2027 [Page]
Workgroup:
Independent Submission
Internet-Draft:
draft-elkhatabi-verifiable-telemetry-ledgers-11
Published:
Intended Status:
Informational
Expires:
Author:
B. El Khatabi
Independent

Verifiable Telemetry Ledgers

Abstract

This document profiles a verifiable-telemetry ledger. Its interoperability boundary begins with exact canonical-record byte strings that an upstream system has already produced. The profile fixes their admission into serial-numbered segments, deterministic commitment-tree calculation, an authoritative segment artifact encoded in Concise Binary Object Representation (CBOR), a producer manifest, three disclosure classes, and binding of the artifact digest through a required external timestamp channel. Segment closure uses a deployment-configured elapsed-time interval and does not depend on calendar dates.

The profile enables independent recomputation and audit of disclosed evidence from the admitted bytes onward. Transport framing, decryption, anti-replay processing, payload interpretation, and source-telemetry-to-record mapping are outside it, as are device onboarding, end-to-end security of sensor values, and safety decisions.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 10 March 2027.

Table of Contents

1. Introduction

Verifiable Telemetry Ledgers (VTL) support long-lived telemetry deployments that need evidence that record bytes disclosed later are the same bytes a ledger producer committed when they were admitted, even when upstream delivery is intermittent and verification happens much later.

Two trust questions must be distinguished. The first is whether a source was genuine, authorized, and mapped correctly. The second is whether, after exact record bytes were selected for commitment, a later verifier can determine that those bytes, the ledger artifact, and the timestamp artifacts are mutually consistent. This profile standardizes the second question only.

This memo is an Independent Submission. It is not an Internet Standard and has not been evaluated as having IETF community consensus.

Segment intervals are ledger batching rules, not source-reporting periods or civil-time claims. Segment membership follows admission into intervals governed by a non-decreasing elapsed-time source; timestamps carried in records do not select membership (Section 4.4.1).

The profile boundary is admission of an already-formed canonical-record byte string. This document specifies admission and the subsequent commitment, disclosure, and verification of those bytes.

1.1. Example: From Admission to Audit

This informative example shows why the machinery in the rest of this document exists. It is illustrative only; every rule it depends on is specified normatively later.

A factory gateway admits sensor readings as canonical-record byte strings throughout the day. At each interval boundary it fixes the membership of a segment. It then hashes the exact admitted bytes, sorts the resulting leaf hashes, computes a segment root, seals a segment artifact naming the ledger, the segment's serial number, its closure claim, and the digest of its predecessor, and submits SHA-256 over the artifact bytes to a Timestamp Authority.

Months later a customer disputes one shipment. For this example, the disputed readings fall within two batches of a segment containing several batches. The operator does not want to disclose all the segment's telemetry, so it supplies a producer manifest, the segment artifact, its predecessor artifact, the timestamp response, and any request required for nonce correlation, along with complete openings for those two batches. Because batches are slices of the sorted leaf-hash list, those openings also disclose any unrelated records in the same batches.

The auditor recomputes the two batch roots from the disclosed record bytes, checks that the artifact's complete list of batch roots composes to the committed segment root, confirms that the timestamp token binds the exact artifact bytes, and validates the artifact's link to its predecessor.

The auditor's result then says exactly that much: the two disclosed batches were recomputed and matched, the artifact existed no later than the timestamp under the auditor's stated policy, and the predecessor link held. It claims nothing about the batches that were not opened, nothing about whether the readings were true, and nothing about whether other segments exist.

Disclosing some batches but not all is one of three disclosure levels a producer can claim. They are defined normatively in Section 7; in outline:

  • Class A discloses everything needed to recompute the whole segment.
  • Class B discloses complete selected batches, so a partner can check that material against the commitment without seeing the rest.
  • Class C discloses the segment artifact without record openings; successful verification requires valid timestamp evidence.

What a producer discloses and what a verifier actually checks are separate throughout this document. A Class A bundle does not by itself mean anyone performed full recomputation, and a successful narrower check makes no claim about unopened records. Every verifier result states the scope it exercised, and that scope bounds what the result asserts.

1.2. Relationship to Existing Work

VTL reuses the Merkle Tree Hash construction of [RFC9162] to commit a producer-local segment. It defines no Certificate Transparency log, transparency service, public append-only log, statement-registration protocol, receipt, inclusion or consistency proof, gossip protocol, or log-auditing API. Unlike Certificate Transparency, VTL sorts leaf hashes before reduction and so commits to a multiset rather than an append-ordered sequence: the root is independent of admission order and supports no append-order, prefix, or consistency claim between partial snapshots. Chaining orders sealed artifacts, not records within a segment.

The SCITT architecture in [RFC9943] defines signed statements, registration with transparency services, and receipts; [RFC9052] defines COSE signing and encryption structures. Neither is used by the VTL baseline: the authoritative artifact is not a COSE object and no producer signature is required. A separate specification can publish or wrap VTL artifacts through either without altering the rules defined here.

Profile Boundary

The following informative figure shows the profile boundary and the artifact and timestamp flows used for baseline verification.

VTL profile boundary and processing flow Source telemetry passes through an upstream admission system before exact canonical-record bytes cross the VTL profile boundary. The ledger producer creates an authoritative segment artifact and submits its digest to a Timestamp Authority. The verifier receives the artifact and timestamp response. Source telemetry OUTSIDE VTL Upstream admission system auth, replay projection exact canonical-record bytes profile boundary authoritative segment artifact Ledger producer admit, assign, commit seal and chain artifact digest TSA Verifier timestamp artifact
Figure 1: VTL Profile Boundary and Processing Flow

2. Conventions and Terminology

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

Upstream admission system:
A component outside this profile that authenticates, filters, decrypts, de-duplicates, interprets, or maps source telemetry and produces a canonical-record byte string. It can be colocated with the ledger producer.
Canonical-record byte string:
The exact deterministic-CBOR byte string presented at the profile boundary and admitted as one commitment input. "Canonical" describes its byte encoding, not its physical truth or upstream derivation.
Commitment profile:
An immutable set of rules for validating canonical-record bytes and constructing commitment outputs, selected by commitment_profile_id. This document assigns the UUIDv4 c08ade4e-1785-4eb6-9648-b7003d76288d; identifier handling is specified in Section 8.
Ledger:
An ordered chain of segment artifacts identified by one ledger_id. Segment numbers are scoped to that identifier.
Ledger epoch:
The lifetime of one ledger sequence under one ledger_id. Its first segment is number zero and carries the all-zero predecessor value.
Segment:
The multiset of canonical-record occurrences assigned to one emitted interval. It can be empty when the profile permits emission of an empty interval.
Admission linearization point:
The serialization point at which one exact record is accepted and assigned to the open interval. It is not a wall-clock instant.
Boundary-logic resolution:
The smallest difference between elapsed-time readings that the producer's interval-boundary logic can distinguish.
Closed-unsealed interval:
Internal producer state whose membership, policy snapshot, close reason, counts, and serial allocation are durably fixed but whose artifact is not yet durably persisted.
Authoritative segment artifact:
The deterministic-CBOR record for one emitted segment and the cryptographic source of truth for its verification. It is referenced by artifacts.segment_cbor and is also called the segment artifact.
Projection:
Any non-authoritative representation derived from an authoritative segment artifact.
Producer manifest:
A non-authoritative JSON object that claims a disclosure class and identifies disclosed artifacts, their digests, and producer timestamp state.
Verifier result:
The verifier-authored output reporting exercised scope, chain conclusion, TSA channel status, overall outcome, and failure reasons. It is neither a commitment input nor a producer claim.
Evidence bundle:
The producer manifest and disclosed artifacts presented to a verifier as one unit.
Bundle root:
The single logical container relative to which every manifest path is resolved. It can be a directory, archive, object-store prefix, or equivalent closed namespace.
Safe resolver:
A resolver that confines every consumed object to the bundle root and prevents substitution between resolution and consumption.
Disclosure class:
The level of artifact disclosure claimed by the producer, as defined in Section 7.
Verification scope:
The validation boundaries a verifier actually exercised and asserts in its result.
Verifier policy:
The complete identified set of local inputs governing scope, TSA trust and validation, resource limits, and other policy decisions.
Primary-input boundary:
The point at which the verifier-result contract begins, as defined in Section 6.5.1.
Bundle-processing error:
A rejection before the primary-input boundary. This document defines no verifier-result object for it.
Baseline producer:
A producer claiming the conformance defined in Section 6.1.
Baseline verifier:
A verifier claiming the conformance defined in Section 6.1.
Baseline TSA channel:
The mandatory timestamp channel defined in Section 6.4. Its verifier-result key is tsa.
Timestamp Authority (TSA):
An authority that issues timestamp responses over submitted digests.
Timestamp artifacts:
The exact TimeStampResp and any disclosed supporting TimeStampReq.

Batch subtrees and batch openings are defined where they are specified, in Section 4.4.6 and Section 5.1.

3. System Roles

These roles participate at different sides of the profile boundary; conformance is defined in Section 6.1. The upstream admission system produces canonical-record byte strings after deployment-specific source validation and mapping and is outside this profile. The ledger producer accepts those exact byte strings at the profile boundary, assigns them to serial-numbered segments, constructs segment artifacts, and submits their digests for timestamping; one physical gateway can implement both roles, but conformance begins only at the byte-level handoff. The verifier recomputes commitments and validates timestamp artifacts from disclosed bundles. The Timestamp Authority issues timestamp responses over artifact digests.

4. Data and Commitment Model

The input to this section is an exact canonical-record byte string already produced by an upstream admission system. This section covers admission, segment assignment, deterministic hashing and tree calculation, segment formation, artifact construction, and chaining.

4.1. Canonical-Record Input Boundary

The profile input is one exact canonical-record byte string. It MUST encode exactly one CBOR data item using the record shape below and the deterministic encoding constraints in Section 4.2, and a ledger producer MUST reject an input that does not satisfy those byte-level constraints.

A baseline producer MUST accept every conforming canonical-record byte string whose total encoded length is no greater than 4096 octets and whose payload nesting depth is no greater than 16. Payload nesting depth is zero when payload is a scalar; each array or map on a path beginning at payload increases depth by one, the payload depth is the maximum over all such paths, and the outer seven-element record array is not counted. A producer MAY accept conforming records outside this shared acceptance envelope under its documented resource policy, but a configured maximum below either dimension does not satisfy baseline producer conformance. These are minimum acceptance capabilities for bounded-resource interoperability, not protocol maxima, and the octet value is not derived from a network MTU. A record outside the envelope is not thereby nonconforming, but a producer that admits one can produce bundles a conforming baseline verifier rejects under identified policy. An implementation MAY validate a byte string incrementally, but admission applies to one complete byte string encoding exactly one canonical record; an upstream system MAY instead produce multiple independently conforming records, each a separate admitted occurrence, which this profile does not treat as chunks of one record.

Once admitted, the exact supplied octets are authoritative. The producer MUST use those octets, without modification, as the leaf preimage under Section 4.3, and MUST NOT decode and re-encode, normalize, enrich, remove, reorder, or otherwise substitute a different byte string. Each admission event contributes one record occurrence, including when two occurrences have identical byte strings. Source transport, framing, authentication, decryption, anti-replay processing, duplicate-submission policy, identifier and message-type mapping, payload interpretation, and construction of the byte string are upstream of this boundary and are neither specified nor verified here; a physical gateway MAY perform those functions and ledger production together, but its interoperability claim begins only with the exact byte string passed across this boundary.

Each canonical-record byte string encodes the following fixed seven-element CBOR array:

canonical-record = [
  version: 1,
  device_id: bstr .size 8,
  fc: uint,
  ingest_time: uint,
  device_time: uint / nil,
  kind: uint,
  payload: restricted-cbor-item
]

restricted-cbor-item =
  int / bstr / tstr / bool / nil / finite-float /
  [* restricted-cbor-item] /
  { * tstr => restricted-cbor-item }

finite-float = float ; Finite only; deterministic rules follow.

The positional labels are conventional names for inspecting disclosed records and do not move source-to-record mapping into this profile. version is the unsigned integer 1; device_id is an opaque 8-octet identifier assigned upstream; fc is an unsigned upstream-supplied sequence value with no replay or duplicate-suppression semantics defined here; ingest_time is an unsigned count of whole non-leap seconds since 1970-01-01T00:00:00Z and device_time is an unsigned application timestamp or null whose epoch and unit are upstream semantics. Neither value selects segment membership; kind is an unsigned application discriminator; and payload is one recursively restricted CBOR data item whose schema and meaning are outside this profile.

Canonical-record identity is exact-octet identity, not numeric or application-level equality. Within payload the integer 1 and the floating-point value 1.0 are distinct, as are positive and negative floating-point zero, whose shortest exact encodings are respectively f90000 and f98000; a producer or verifier MUST NOT normalize negative zero to positive zero or convert between an integer and a numerically equal floating-point value. A different record array shape or byte-level field type is a different commitment profile and requires a distinct commitment_profile_id. Application schemas MAY separately define field meanings, but successful verification does not validate those external semantics.

4.2. Deterministic CBOR Commitment Encoding

Commitment bytes MUST satisfy the core deterministic-encoding requirements of Section 4.2.1 of [RFC8949]. Items MUST use definite-length encoding; integers and finite floating-point values MUST use their shortest exact encodings; and map keys MUST be ordered in the bytewise lexicographic order of their deterministic encodings.

Map keys MUST be CBOR text strings. Duplicate map keys and invalid UTF-8 text strings MUST be rejected. CBOR tags, NaN, positive infinity, and negative infinity MUST NOT appear. Permitted values are integers, byte strings, text strings, arrays, maps, booleans, null, and finite deterministic floating-point values, including negative floating-point zero. The exact type, sign, and encoded-octet distinctions in Section 4.1 are preserved by admission and verification.

Because this profile permits only text-string map keys in their preferred deterministic encodings, bytewise ordering of their encoded heads orders them first by UTF-8 byte length and then by encoded bytes, coinciding with the length-first order of Section 4.2.3 of [RFC8949].

JSON projections are optional, non-authoritative, and MUST NOT be commitment inputs; implementations can use [RFC8785] when stable JSON projection octets are useful. Once a canonical-record byte string is admitted, its exact octets MUST be committed without re-encoding. Unless a rule explicitly says otherwise, every digest in this document is computed over the exact received or disclosed octets, and an implementation MUST NOT re-encode, normalize, or reserialize input before hashing.

4.3. Deterministic Commitment Tree Calculation

For a given segment, this profile computes segment_root from canonical-record bytes using the domain-separated tree shape of Section 2.1.1 of [RFC9162], specialized to SHA-256 and to the multiset ordering below. SHA-256 is defined by [FIPS180-4]; [RFC6234] provides informative implementation descriptions and test material.

Every SHA-256 invocation in this profile MUST produce the complete 32-octet digest; a truncated digest or a different SHA-2 function is nonconformant. For tree hashing an implementation MUST use exactly the 0x00 leaf prefix and 0x01 internal-node prefix and MUST NOT add, omit, or substitute another tree-domain prefix.

For each exact canonical-record byte string record, compute leaf_hash = SHA-256(0x00 || record). Sort the resulting raw 32-octet leaf hashes in ascending lexicographic (bytewise) order. The authoritative artifact represents hash values as CBOR byte strings; hexadecimal renderings in this document are for human-readable vectors only.

Let VTLRoot(L) operate on the sorted list of already-computed leaf hashes. VTLRoot([]) = SHA-256("") and VTLRoot([x]) = x. For a list of n > 1 hashes, let k be the largest power of two strictly less than n, and compute VTLRoot(L) = SHA-256(0x01 || VTLRoot(L[0:k]) || VTLRoot(L[k:n])). Implementations MUST NOT duplicate an unpaired final hash. The prefixes provide leaf/parent domain separation, and the recursive split gives every leaf count a unique tree shape.

For an emitted empty segment, record_count MUST be zero, batch_roots MUST be an empty array, and segment_root MUST equal VTLRoot([]), the SHA-256 digest of zero bytes: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855. For a non-empty segment, record_count MUST be positive and batch_roots MUST contain at least one root.

The resulting segment_root is deterministic for the multiset of committed canonical records. Because leaf digests are sorted before reduction, the root does not commit to admission order. Producer conformance requires sorting the complete segment leaf multiset before batching; verification of that global sort rule is complete only under the public_recompute scope, and weaker scopes provide only the ordering assurances defined for them in Section 6.5.3. Any future change to this calculation that alters commitment bytes MUST use a new commitment_profile_id.

4.4. Segment Formation and Closure

This section specifies how admitted records are assigned to logical intervals, how those intervals close, how durable state survives restart, and how the committed batch subtrees are derived.

4.4.1. Normal Interval Timing and Admission

Segment formation uses a non-decreasing producer elapsed-time source that MUST progress during normal operation. Its boundary-logic resolution MUST be no coarser than the smaller of 1000 milliseconds and closure_policy.interval_ms; finer sampling is permitted. The interval is a fixed positive uint64 millisecond duration and MUST NOT be aligned to civil-time events. The first interval opens when the producer durably activates the ledger, and reactivation resumes durable state after shutdown. Every closure except shutdown opens the next interval immediately. Intervals continue across idle periods; suppress affects emission and serial consumption, not the elapsed schedule.

The producer MUST serialize the interval-boundary transition with the admission linearization point so that each admitted input has exactly one segment assignment. For an interval whose elapsed-time deadline is T: a record whose acceptance linearizes before T belongs to the current interval; at T or later the producer MUST process the boundary before another acceptance can linearize, so a record admitted at or after T belongs to the next interval even when its upstream processing began earlier. Source arrival time, upstream validation or record-construction time, device_time, and ingest_time MUST NOT override this assignment.

Processing the boundary means completing the durable close transition in Section 4.4.5, not sorting leaves, encoding or persisting the artifact, submitting its digest, or obtaining a response. Deadline expiry MUST use checked elapsed-time subtraction, not potentially wrapping addition, and an unsafe elapsed-time comparison MUST invoke recovery before another record is accepted.

4.4.2. Policy Changes and Early Closure

The producer MUST keep the opening-time closure_policy snapshot unchanged through closure. A policy update that can wait applies to the next interval. A reconfiguration request that is to take effect before the current interval would otherwise close makes the reconfigure condition true. The producer MUST close the current interval under its old snapshot before applying the change. When another condition is also true, Section 4.4.3 selects the committed reason. Early closure is permitted only for its committed reason. Late processing MUST NOT move a post-deadline admission into the expired interval and SHOULD emit an operational signal as described in Section 10.1.

The manual condition is true only when closure is requested through a producer-provided administrative interface that is not driven by elapsed-time, record_limit, or size_limit_bytes evaluation. The producer MUST NOT select manual without such a request. Whether a human operator or automation acting through that interface initiated it does not change the condition. The request MUST be serialized with admission and close under the current snapshot; the committed reason follows Section 4.4.3 when multiple conditions are true. A manual closure opens a successor and empty emission follows empty_mode. shutdown closes for orderly cessation, MUST NOT open a successor until reactivation, and MUST emit and consume a serial even for an empty suppress interval.

4.4.3. Limits and Close-Reason Selection

interval applies at the snapshotted deadline when no higher condition applies. A non-null record_limit is the maximum admitted count; on reaching it the producer MUST close the interval before another acceptance can linearize. A non-null size_limit_bytes is a close threshold for the summed admitted-record lengths; at or above it the producer MUST close before another acceptance. The triggering record remains assigned, so the field is not a pre-admission maximum and a single oversized record forms a one-record interval. Byte-count updates and comparisons MUST use checked arithmetic; before acknowledging an unrepresentable total the producer MUST enter recovery or stop acceptance.

If multiple close conditions are simultaneously true, the committed close_reason MUST be selected by the following precedence, numbered from highest to lowest. The selected reason does not change which acceptances already linearized into the interval.

  1. recovery
  2. shutdown
  3. reconfigure
  4. manual
  5. size_limit
  6. record_limit
  7. interval

4.4.4. Empty Intervals and Timer Continuity

With timer continuity, emit produces an artifact for every interval, using the empty construction when needed. An empty suppress interval emits nothing and consumes no serial, except that shutdown and recovery always emit and consume one. The next artifact remains contiguous. Suppression makes no completeness or no-event claim.

A host suspend interval counts toward the current logical interval when the producer's elapsed-time source advances across the suspend. When it does not, or when elapsed-time-source continuity is otherwise uncertain, recovery MUST precede another acceptance.

4.4.5. Recovery and Durable State

Before post-restart or failover acceptance, the producer MUST recover ledger identity, next serial, policy, open state, and the ordered closed-unsealed queue. It MUST NOT infer an uncertain boundary from wall time; it MUST instead stop or close recoverable open material as recovery, and that closure MUST emit and consume a serial, including when empty under suppress. The producer MUST NOT synthesize missed historical empty artifacts. A previously fixed close reason MUST survive recovery; recovery applies only when the interval remained open or its open state was uncertain at failure.

Admission is a durable transaction boundary: before acknowledgment the producer MUST atomically make the exact record, membership, counts, and recovery state durable or recoverable, and after a crash an acceptance MUST be recovered exactly once in the same interval or MUST be treated as never completed. The producer MUST serialize close with admission. The close transition MUST atomically preserve an emitting interval's exact membership, policy, reason, counts, allocated serial, and ordered closed-unsealed state, and except at shutdown MUST also make the successor state recoverable before an admission to that successor is acknowledged. A suppressed empty interval creates neither queue entry nor serial, except for the shutdown and recovery cases above.

After close, successor admission and commitment preparation MAY proceed while intervals remain unsealed. Artifacts MUST be finalized durably in serial order with the exact predecessor and digest available for each non-epoch artifact; sealing MUST NOT alter fixed membership, policy, reason, counts, or serial, and MUST atomically preserve the artifact, digest, queue advancement, and timestamp-submission obligation, after which submission and response acquisition can be asynchronous. Open, closed-unsealed, and merely locally queued states have no conforming manifest: only an attempted submission can be pending, a usable response is present, a terminal attempt without one is unavailable, and a local job alone MUST NOT be represented as pending.

Outstanding state MUST have finite recoverable capacity. Before acknowledging an admission that would exceed it the producer MUST apply backpressure or stop acceptance, and before lifecycle closures exhaust it the producer MUST drain or shut down. Loss of exact closed-unsealed state MUST stop acceptance under that ledger; a new epoch is permitted, but the producer MUST NOT skip, reuse, or fabricate material for the affected old-ledger serial. These are observable outcomes, not mandated storage calls.

4.4.6. Deterministic Aligned Batch Subtrees

A batch is not a chronological admission chunk. It is a consecutive slice of the complete globally sorted leaf-hash list, formed only after segment membership is fixed.

For a non-empty interval, compute the complete sorted leaf-hash list defined in Section 4.3 and partition it into consecutive batches of B = closure_policy.batch_record_limit hashes. B MUST be a positive power of two no greater than 2^63. Batch zero contains the first B hashes, batch one the next, and so on; only the last batch can contain fewer than B. Compute each batch_roots[i] as VTLRoot over that batch's already-computed leaf hashes. This is the sole baseline batch partitioning rule and is independent of storage flushes, worker scheduling, or input-file layout. An implementation MAY obtain the required global leaf order through an order-maintaining index or external sort and reduce each aligned batch incrementally, but sorting tentative input chunks or batches independently is not equivalent and MUST NOT replace the global sort; every optimization MUST reproduce the exact commitment and artifact bytes defined here. Requiring a power of two makes every full batch align with a recursive VTLRoot split, so an aligned batch root can substitute directly for its subtree without an auxiliary sibling path.

Let N be record_count. For N > 0 the number of batch roots MUST be 1 + ((N - 1) / B) using integer division. To compose a range root from those aligned roots, return the sole supplied root when the range contains at most B leaves; otherwise let k be the largest power of two strictly less than the range's leaf count, which is a batch boundary because B is a power of two, recursively compose the roots before and after that boundary, and return SHA-256(0x01 || left || right). The result MUST equal segment_root. This is subtree substitution under the VTLRoot recursion in Sections 2.1.1 and 2.1.2 of [RFC9162]; it does not define a Certificate Transparency log or CT wire object.

4.5. Authoritative Segment Artifact Schema

The segment artifact is a deterministic CBOR-encoded segment record produced under Section 4.2 and is the stable segment-scoped object of commitment verification. It commits to one record multiset, its ledger identity, serial number, closure policy and reason, and predecessor artifact, and is not a transport envelope, publication statement, or complete verification claim. A JSON projection, filename, and storage path are non-authoritative, and a verifier MUST NOT trust a path or filename as the segment number. The artifact is interpreted only under its in-band commitment_profile_id: because that identifier is inside the artifact bytes, a valid timestamp token over its digest binds the commitment semantics. Profile dispatch follows Section 8.

The segment record contains exactly these fields:

  • version (uint): segment-record schema version, exactly 1.
  • commitment_profile_id (tstr): a lowercase RFC 9562 UUID string.
  • ledger_id (tstr): exactly 32 lowercase hexadecimal characters.
  • segment_number (uint): serial number in the range 0..(2^64-1).
  • closure_policy (map): the immutable segment closure policy snapshot, containing exactly version equal to 1; positive uint64 interval_ms; power-of-two uint64 batch_record_limit no greater than 2^63; record_limit and size_limit_bytes, each a positive uint64 or null; and empty_mode equal to emit or suppress.
  • close_reason (tstr): exactly one of interval, reconfigure, record_limit, size_limit, shutdown, recovery, or manual.
  • prev_segment_sha256 (bstr): 32-octet SHA-256 digest of the exact bytes of the previous segment artifact, or the epoch predecessor value in Section 4.6.
  • record_count (uint): number of canonical-record occurrences committed by the segment.
  • batch_roots (array of bstr): ordered aligned-subtree roots, each exactly 32 octets; empty for an empty emitted segment.
  • segment_root (bstr): 32-octet deterministic segment root.

When record_count is zero, closure_policy.empty_mode MUST be emit unless close_reason is shutdown or recovery, the mandatory lifecycle artifacts that retain a committed suppress value; any other empty artifact under suppress is invalid. Array position is the batch number: the root at position i MUST commit the consecutive sorted-leaf range beginning at i * batch_record_limit, with root count, final-batch width, and composed segment root derived as in Section 4.4.6.

A verifier MUST validate that the producer manifest segment_number is the shortest unsigned decimal form defined in Appendix B, parse it as a uint64, and compare that numeric value with the decoded CBOR uint. A non-shortest decimal form is a manifest schema violation and therefore a bundle-processing error; after a valid parse, a numeric mismatch with the artifact is commitment_mismatch. The artifact uses the registered application/cbor media type when media-type metadata is carried, and implementations SHOULD use application/vnd.vtl.segment+cbor once registered. Media-type metadata does not determine artifact conformance or profile selection.

4.6. Segment Chaining

Segment identity and chaining are defined within one stable ledger_id and do not depend on UTC dates or elapsed idle time.

  • The epoch segment MUST have segment_number equal to 0 and prev_segment_sha256 equal to a 32-octet all-zero byte string.
  • Every non-epoch segment MUST have a serial number exactly one greater than its predecessor, MUST retain the same ledger_id and commitment_profile_id, and MUST set prev_segment_sha256 to SHA-256 over the exact bytes of the predecessor's segment artifact.
  • A producer MUST NOT reuse, skip, or wrap segment_number. Before serial exhaustion it MUST stop the ledger and start a new one with a new ledger_id.
  • Configuration rollback follows these same transition rules and MUST NOT mutate sealed artifacts, reuse a serial, or conceal a chain discontinuity. A closure-policy change MUST take effect only in a newly opened logical interval and does not reset segment_number or ledger_id.

When a new ledger epoch begins, the producer MUST generate a new ledger_id so that the probability of collision with any other ledger identifier is negligible; a uniformly random 128-bit value from a cryptographically strong random source satisfies this. If durable sequence state cannot be recovered unambiguously, a producer MUST stop acceptance or begin a new ledger with a fresh identifier and MUST NOT reset the serial under the old identifier. Redundant producer instances MUST use single-writer fencing or an atomic shared allocator so that they cannot emit different artifacts for the same (ledger_id, segment_number).

A verifier given adjacent segment artifacts MUST validate serial continuity, exact commitment_profile_id equality, and predecessor-digest linkage. A profile mismatch is a segment_chain_mismatch. A partial disclosure omitting the predecessor can still support validation of the disclosed artifact and its timestamp artifacts, but the verifier MUST report chain_status as predecessor_not_disclosed.

5. Artifacts and Verification Bundles

A deployment can store canonical records, segment artifacts, producer manifests, verifier results, and timestamp artifacts in any layout; filenames and layouts are non-authoritative. Each disclosed record artifact MUST contain the exact admitted canonical-record octets without a wrapper or re-encoding. Every bundle claiming a disclosure class MUST disclose the segment artifact and a producer manifest.

5.1. Producer Manifest

The producer manifest repeats the ledger identity, segment number, commitment profile, claimed disclosure class, artifact digests, and claimed producer timestamp state; the artifact's in-band commitment_profile_id remains the cryptographically bound one. The manifest MUST NOT contain a chain conclusion, verifier status, failure reason, or overall outcome. Its schema is in Appendix B and its shape per claimed class in Section 7. It uses application/json, and application/vnd.vtl.manifest+json once registered, on the same terms as the segment artifact. Deployment-specific data can be carried only in explicit extension containers, whose presence MUST NOT alter the semantics defined here, select an additional evidence channel, or affect the baseline outcome.

For a non-epoch segment, a predecessor is disclosed for baseline verification if and only if artifacts.predecessor_segment_cbor is present and its referenced bytes are safely resolved; an extension member or out-of-band artifact does not substitute for it or change chain_status.

A batch opening is an artifacts.record_batches entry disclosing every canonical-record occurrence committed by the aligned batch subtree identified by its batch_number, permitting recomputation of that indexed root without disclosing other batches. Each nested records entry represents one admitted occurrence. Duplicate entries are permitted and MUST be counted separately; occurrences with identical bytes MAY reference the same disclosed artifact and a verifier MAY reuse a successful digest computation, but the leaf hash MUST contribute once per listed occurrence, and reference order does not assert admission order. An opening MUST disclose exactly the number of occurrences derived for that batch from record_count and batch_record_limit, and after hashing and sorting those exact octets their VTLRoot value MUST equal the indexed batch_roots value.

Producer timestamp state is present when the response is disclosed, pending when issuance was initiated but has not reached a terminal state, and unavailable when issuance terminated without a usable response; manifests MUST NOT use verifier states such as verified or failed. A pending or unavailable state MUST omit tsa_tsr and a present state MUST reference it. When a disclosed response contains a nonce the manifest MUST also reference tsa_req; producer obligations for a nonce-bearing request are in Section 6.4.

5.2. Verifier Result

Verification under Section 6.5 produces a logical verifier result. Only that result states the exercised scope, any chain conclusion, TSA channel status, verifier-policy identifier, overall outcome, and failure reasons; local diagnostics are implementation-specific. A producer manifest and a verifier result MUST NOT be represented as the same object. A verifier MAY serialize its result as JSON; a serialized result claiming conformance MUST use the schema in Appendix B and the application/json media type.

5.3. Portable Artifact References and Binding

Every path-bearing object consumed during verification, including a manifest artifact reference or an extension object, MUST satisfy this subsection. Each path is a UTF-8 string interpreted relative to the bundle root, using / as the only separator. A path MUST be non-empty, MUST NOT begin with /, \, or a drive-letter prefix, and MUST NOT contain \, a colon, a control character in the ranges U+0000 through U+001F or U+007F, an empty path component, or a component equal to . or ... Verifiers MUST resolve and open every consumed path through a safe resolver. These are semantic requirements and do not depend on a regular-expression dialect or operating-system pathname behavior.

Every artifact listed in the manifest MUST carry a lowercase hexadecimal SHA-256 digest entry. The manifest's ledger_id, segment_number, and commitment_profile_id MUST match the corresponding artifact fields whenever those fields can be decoded; segment-number comparison follows Section 4.5 and profile selection follows Section 8. Failure mapping for unsafe or digest-mismatched references is given once, in Section 6.5.5.

6. Anchoring and Verification

This section defines baseline conformance, the required timestamp channel, verification inputs and scopes, and the result reduction rules.

6.1. Baseline Conformance

A baseline producer MUST emit the fixed commitment_profile_id defined here, construct segment artifacts and producer manifests under this profile, and initiate timestamp submission through the baseline TSA channel for every emitted segment. Issuance MAY remain pending while incomplete.

A baseline verifier MUST implement this commitment profile and the baseline TSA channel, obtain the profile identifier from the segment artifact, and use the result vocabulary and fixed TSA channel key defined here. Every result claiming baseline conformance MUST contain the tsa channel key, including when its status is pending_claim, missing, or failed, and a baseline success result requires successful timestamp validation.

A baseline verifier MUST configure finite resource limits for variable-length primary and secondary inputs, decoded collections, record and batch counts, certificate counts, and parser recursion. Its canonical-record length and depth limits MUST admit the shared acceptance envelope in Section 4.1. This document defines no mandatory acceptance floor for the other dimensions. Rejection before the primary-input boundary is a bundle-processing error; rejection after it solely because an otherwise valid input exceeds identified policy uses verifier_policy_rejection.

Extension data is opaque to baseline producers and verifiers, and its presence, absence, or contents MUST NOT change a baseline channel status or overall outcome. A separate specification can define an additional evidence mechanism without changing the segment artifact or this commitment profile.

6.2. TSA Channel Status Vocabulary

Verifier results MUST use exactly this vocabulary for the TSA channel: verified, timestamp validation succeeded for the disclosed artifact binding; pending_claim, the manifest claims submission was attempted but no terminal response is disclosed, an incomplete producer claim rather than an independently verified fact; missing, an expected response is absent or is referenced but cannot be safely resolved; and failed, validation was attempted and did not succeed. Producer state pending maps deterministically to pending_claim and unavailable maps to missing. Baseline verification does not authenticate the claim or change its status based on elapsed time, manifest provenance, receipt context, or other out-of-band inputs; a later bundle disclosing a terminal response is a different fixed-byte input. Outcome effects are in Section 6.5.6.

The TSA-result reason value is human-readable diagnostic text. producer_pending_claim is the only baseline machine-defined token; any other value is free text and MUST NOT be interpreted as a portable machine-actionable code. A pending_claim result MUST set reason to producer_pending_claim; a missing or failed result MUST include a free-text reason; a verified result MUST NOT include one. Additional machine-readable detail MAY be placed in the channel result's extensions member and is opaque to baseline verification.

6.3. Anchoring Contract

A producer computes the SHA-256 digest of the segment artifact and submits it through the baseline TSA channel; verifiers MUST recompute that digest over the exact artifact bytes. Timestamp validation and message-imprint comparison do not depend on successful CBOR decoding of the artifact. A token that validates with the artifact digest as its message imprint binds the complete artifact bytes, including the in-band commitment_profile_id, but binds no disclosure class, producer manifest, verifier policy, or verifier result.

Consequently, a manifest received over an unauthenticated channel can under-claim the available disclosure as Class C, omit record openings, or direct the verifier to a different valid artifact. Manifest digest checks establish consistency with the received manifest, not its provenance. A relying party needing those properties MUST obtain the authoritative artifact bytes or their expected digest independently through an authenticated mechanism and rerun verification from those bytes.

The producer MUST seal and durably write the segment artifact without waiting for issuance completion. Alternative timestamping or attestation mechanisms require a separate specification and do not alter this baseline contract.

6.4. Baseline TSA Channel

This channel uses the timestamp protocol of [RFC3161], as updated by [RFC5816] and restricted here. The TimeStampReq, TimeStampResp, and token MUST conform to those specifications, and the token MUST contain exactly one CMS SignerInfo as defined by [RFC5652]. These restrictions define a narrower channel than generic use of that protocol, so a conforming implementation cannot assume an arbitrary deployed TSA is compatible; deployments SHOULD prequalify a TSA before relying on it.

The producer MUST set TimeStampReq.messageImprint.hashAlgorithm to SHA-256 and its hashedMessage to the exact 32-octet result of SHA-256 over the segment artifact bytes, MUST NOT hash that result again, and MUST set certReq to TRUE. A successful TimeStampResp.status.status MUST be granted (0); a verifier MUST reject grantedWithMods (1), and a producer receiving it MUST treat the terminal attempt as unavailable and MUST NOT publish that response as usable baseline evidence.

The signed attributes MUST contain exactly one SigningCertificateV2 attribute with exactly one value, as defined by [RFC5035], whose first ESSCertIDv2.certHash MUST equal SHA-256 over the complete DER encoding of the selected signer certificate; a present hashAlgorithm MUST identify SHA-256, omission denoting that default, and a present issuerSerial MUST identify that same certificate. The signed attributes MUST NOT contain the legacy SigningCertificate attribute of [RFC2634]. Because certReq is TRUE, SignedData.certificates MUST contain exactly one X.509 certificate matching both SignerInfo.sid and that first ESSCertIDv2; zero or multiple matches are a verification failure. The set MAY also contain unrelated or path certificates, and a verifier MUST NOT rewrite a received response or reject it merely for containing additional nonmatching certificates.

A baseline verifier MUST NOT report this channel as verified unless its identified verifier policy supplies accepted trust anchors, accepted TSA policy identifiers, revocation policy including acceptable evidence and the treatment of historical certificate status at TSTInfo.genTime, acceptable signature and certificate algorithms and strengths, any additional generation-time constraints, a maximum acceptable future skew, and the verifier-local time source used for that check. This document defines no long-term archival-PKI validation profile beyond those inputs. Missing policy input after the primary-input boundary is verifier_policy_rejection; if the identified time source is unavailable at runtime, validation fails, the channel status is failed, and the result includes channel_failure.

artifacts.tsa_tsr carries the exact DER-encoded TimeStampResp. A verifier MUST validate those exact referenced bytes, MUST NOT re-encode the response before any digest or channel check, and MUST recompute the segment artifact digest. Validation MUST include:

  • response status granted (0) and required token presence;
  • the CMS id-signedData and id-ct-TSTInfo content types;
  • exact SHA-256 message-imprint equality with the recomputed digest;
  • the CMS signature, signed attributes, and unambiguous signer-certificate binding;
  • the signer certificate's single critical extended-key-usage extension containing only id-kp-timeStamping;
  • path validation under [RFC5280] using TSTInfo.genTime as the date/time input and configured trust anchors;
  • configured TSA policy and certificate-validation constraints;
  • a genTime within the signer certificate's validity interval; and
  • the configured maximum future skew.

The optional artifacts.tsa_req carries the exact DER-encoded TimeStampReq as a non-authoritative supporting artifact. A producer sending a nonce-bearing request MUST retain those exact bytes at least as long as the corresponding response, MUST disclose the request with that response, and MUST NOT treat a response that omits or changes its nonce as successful issuance. Whenever tsa_req is disclosed the verifier MUST validate its SHA-256 digest, require the request message-imprint algorithm to be SHA-256 and its hashedMessage to equal the recomputed artifact digest, and require certReq=TRUE. If either disclosed object contains a nonce, both MUST contain one and the values MUST be equal. A nonce-bearing response without tsa_req, a nonce present in only one object, unequal nonces, or any failure of these checks is a timestamp-channel verification failure reported with channel_failure. If the response carries no nonce and tsa_req is not disclosed, a verifier cannot determine whether the original request contained one and the result makes no nonce-correlation claim. Any failure of the validations in this section is a timestamp-channel verification failure; the channel status MUST be verified on success and failed on failure.

6.5. Verification

Verification addresses four distinct questions. The verifier result identifies the selected scope, reports chain and timestamp status, and combines applicable checks into an overall outcome.

Artifact validity:
Do the exact disclosed bytes decode as a segment artifact under a supported commitment profile, with internally consistent counts, batch roots, and segment root?
Record recomputation:
Do the disclosed occurrences reproduce their indexed batch roots? Under public recomputation, does the complete globally sorted record multiset also reproduce the segment root?
Chain continuity:
Does an epoch artifact carry the required initial values, or are the artifact's serial number and predecessor digest consistent with a disclosed predecessor?
Timestamp validation:
Does a timestamp token bind the exact artifact bytes under the identified verifier policy?

These conclusions must be distinguished. An artifact that fails to decode can still carry a valid timestamp over its bytes, and a valid timestamp establishes nothing about record contents. Verification therefore begins at the primary-input boundary, selects one exercised scope, applies the validations that scope supports, and reduces the resulting conclusions to the verifier-result schema. The subsections below fix the inputs and scope selection (Section 6.5.1), the order in which validations are applied (Section 6.5.2), the structural and record-level checks themselves (Section 6.5.3 and Section 6.5.4), and how conclusions reduce to one outcome (Section 6.5.5 and Section 6.5.6).

6.5.1. Inputs and Scope

The verifier-result contract applies only after the verifier has obtained the exact producer-manifest bytes, fully parsed them as a UTF-8 JSON producer manifest, applied every structural, lexical, and manifest-only cross-field check that does not depend on the segment artifact, determined every mandatory result field and the exact artifacts.segment_cbor reference, and obtained the exact referenced artifact bytes. Failure to obtain either primary input, structurally invalid manifest bytes, a missing primary artifact reference, or unsafe resolution of either primary input is a bundle-processing error, which a verifier MUST reject but for which it MUST NOT emit a verifier result; it is outside the result schema and is not an insufficient_disclosure conclusion. Structurally invalid manifest bytes include present without tsa_tsr, pending or unavailable with tsa_tsr, and a missing, negative, non-integer, non-shortest, or uint64-overflowing segment_number or batch_number; a configured resource limit preventing the verifier from reaching the boundary likewise produces one. After artifact decoding, duplicate or out-of-range batch-opening numbers yield insufficient_disclosure.

Verifiers MUST then decode the artifact and determine the applicable commitment_profile_id from its in-band field, applying the dispatch precedence in Section 8. When that field decodes as a text string the result's optional commitment_profile_id MUST echo its exact value, including when unsupported. The identifier is the semantic key for interpreting the artifact: a structurally well-formed artifact and successful timestamp validation are insufficient for semantic verification when it is absent or unsupported. When the exact artifact bytes are present but do not decode as a valid segment artifact, the result MUST be failure and include invalid_segment_artifact; the verifier MUST still evaluate the TSA channel independently over those exact bytes, and decoding failure alone MUST NOT cause channel status failed or channel_failure.

The manifest's disclosure_class is the producer-claimed class and the result's claimed_disclosure_class MUST repeat it. The result's verification_scope identifies the scope selected for the invocation: public_recompute corresponds to Class A, disclosed_batch_recompute to Class B, and anchor_only to Class C, ordered from strongest to weakest. After satisfying the primary-input boundary the invocation MUST select an intended scope; selection does not assert support, which the verifier determines after artifact decoding and claimed-class shape validation, and earlier fail-fast termination still reports the selected scope without claiming it succeeded. The claimed class determines manifest shape; the selected scope determines which artifacts are consumed and which recomputation claims are made. Results MUST NOT claim assurances outside the exercised scope, and exercising a stronger scope does not rewrite the claimed class. A verifier exercising anchor_only MUST NOT consume record_batches, even when valid claimed-class shape requires those entries to be present, and MUST NOT claim record-level or batch-opening recomputation.

The result's manifest_sha256 MUST equal SHA-256 over the UTF-8 octets of the validated manifest, and artifact_sha256 MUST equal SHA-256 over the referenced bytes of artifacts.segment_cbor, both under the exact-octet rule in Section 4.2, with artifact_sha256 present even when those bytes are malformed. These fields identify the validated inputs and do not authenticate the result. The verifier MUST always compare the recomputed artifact digest with artifacts.segment_cbor.sha256, which checks manifest self-consistency only and MUST NOT be counted as a timestamp binding, and MUST likewise verify the sha256 value of every other manifest reference consumed. A verifier MAY reuse one successful digest computation for repeated references to the same bytes; duplicate occurrences nevertheless contribute once per listed occurrence.

6.5.2. Verification Procedure

A verifier MUST perform every applicable validation required by the sections referenced below. It SHOULD apply them in the fail-fast order shown. This list summarizes ordering and does not create requirements beyond those referenced sections.

  1. Manifest shape against the claimed disclosure class (Section 7). Claimed-class requirements do not determine which valid openings a weaker selected scope consumes.
  2. Segment artifact and manifest, including repeated identity fields and the digest of every manifest reference consumed in later steps (Section 6.5.1, Section 5.3).
  3. Chain status: serial zero and the zero predecessor for an epoch segment, or ledger identity, exact commitment-profile identity, contiguous serials, and prev_segment_sha256 against a disclosed predecessor (Section 4.6).
  4. Structural batch validations for every scope, including record_count, the power-of-two batch limit, batch-root cardinality, and composition of batch_roots into segment_root (Section 6.5.3).
  5. Scope-specific record and opening validation for public_recompute or disclosed_batch_recompute, including independent recomputation of segment_root under the stronger scope (Section 6.5.3, Section 6.5.4).
  6. Comparison of the recomputed artifact digest with the response's messageImprint.hashedMessage, whether or not decoding succeeded; a mismatch is a timestamp-channel verification failure, and a producer pending claim lacks the external binding and yields pending_claim.
  7. Timestamp response validation and channel status (Section 6.4). A referenced response that is unavailable is missing; one whose validation is attempted and does not succeed is failed.

6.5.3. Batch-Subtree Validation

Verifiers MUST apply the structural validations below before accepting any scope. Manifest-shape validation follows the claimed class; opening validation follows the selected scope.

  • batch_record_limit is a power of two in the permitted range;
  • the number of 32-octet batch_roots values equals the value derived from record_count and batch_record_limit, including the empty-segment case;
  • when record_count is zero, closure_policy.empty_mode is emit, or close_reason is shutdown or recovery;
  • when record_count is greater than zero, composition of the ordered aligned subtree roots equals the authoritative segment_root; for an empty segment the empty-root rule in Section 4.3 applies;
  • for claimed-class shape, batch-opening numbers are unique and in range and record_batches matches Section 7;
  • for public_recompute, every disclosed opening is consumed, contains exactly its derived number of occurrences, and has sorted recomputed leaves that reduce to the indexed batch root, and the globally sorted multiset of every disclosed occurrence reduces independently to segment_root;
  • for disclosed_batch_recompute, every opening selected for consumption is complete and reduces to its indexed root, and the selected batch numbers form a non-empty proper subset of the authoritative batches; and
  • when consumed openings for batch numbers i and i+1 are both disclosed, the greatest leaf hash in batch i is less than or equal to the least leaf hash in batch i+1.

The adjacent-opening comparison is required under either recompute scope whenever the relevant pair is consumed. A successful disclosed_batch_recompute result verifies the global sort rule only at those consumed boundaries, and Class C verifies no leaf-order boundary. A later public_recompute result exposing a global ordering violation MUST be failure with commitment_mismatch.

6.5.4. Disclosed Canonical-Record Validation

A verifier exercising public_recompute or disclosed_batch_recompute MUST validate every record artifact consumed under that scope as one exact canonical-record byte string before using it as a leaf preimage. The verifier MUST use the exact disclosed artifact bytes and MUST NOT reconstruct a record from source telemetry. The bytes MUST contain exactly one CBOR data item with no trailing data, MUST have the seven-element array shape and field constraints in Section 4.1, and MUST satisfy the deterministic encoding, text-only map-key, duplicate-key, UTF-8, tag, finite floating-point, and permitted-value rules in Section 4.2 at every recursive payload level, including the restricted payload shape.

The verifier MUST hash the exact validated octets. If consumed bytes violate any canonical-record rule the result MUST be failure with invalid_canonical_record, even when those bytes hash to the leaf value the producer committed; if a separately observed record digest, batch root, segment root, artifact digest, or binding also mismatches, the result MUST additionally include commitment_mismatch.

A baseline verifier exercising either scope MUST accept every conforming disclosed canonical-record artifact consumed under that scope that lies within the shared acceptance envelope of Section 4.1. It MAY accept conforming records outside that envelope under identified policy. A conforming record outside the envelope is not an invalid_canonical_record; rejection after the primary-input boundary is verifier_policy_rejection.

6.5.5. Failure Reasons

A result whose overall value is failure MUST contain one or more machine-usable failure_reasons drawn only from the categories below. A category MUST NOT occur more than once in one result, and failure reasons MUST be absent when overall is success or incomplete. Deployment-specific detail belongs in extensions; this document creates no extension namespace for failure-reason values. A missing primary segment artifact is a bundle-processing error, not a failure reason.

unsupported_commitment_profile:
The in-band profile identifier is present but unsupported.
invalid_canonical_record:
A disclosed canonical-record byte string violates the required record shape, field constraints, restricted payload model, or deterministic CBOR profile.
invalid_segment_artifact:
Present artifact bytes are malformed or violate the required artifact syntax or cross-field rules.
commitment_mismatch:
Commitment material, including a manifest/artifact identity field, record digest, batch, Merkle root, or artifact digest, does not validate. A safely resolved reference whose bytes fail their declared digest uses this category.
segment_chain_mismatch:
A disclosed predecessor cannot be safely resolved or fails ledger identity, commitment-profile identity, serial-continuity, or predecessor-digest validation. The accompanying chain_status is failed.
channel_failure:
The timestamp response is missing, invalid, or otherwise unacceptable, or its binding to the artifact digest fails. An absent or unsafely referenced response is reported with channel status missing and this category.
insufficient_disclosure:
The disclosed artifacts do not support the claimed class or asserted scope. This includes a required batch opening or record artifact that is absent or cannot be safely resolved.
scope_not_exercised:
The inputs support a stronger scope required by identified policy, but the verifier intentionally exercised a weaker one.
verifier_policy_rejection:
The inputs do not satisfy an applicable local verifier policy for a reason not more specifically represented above.

6.5.6. Overall Outcome

A chain_status of predecessor_not_disclosed does not by itself degrade the outcome; it makes no chain-adjacency claim. The following reduction is normative and total after the primary-input boundary has been satisfied and the exercised scope and identified verifier policy have been fixed. A verifier MUST evaluate every applicable failure condition and include every distinct failure reason it reaches; multiple failure conditions can apply to one invocation, and the two terminal conditions apply only when no failure condition applies. Extension data is not a channel and MUST NOT affect this evaluation.

Table 1: Overall Outcome Reduction
Condition Required effect
The in-band profile identifier is a well-formed but unsupported UUID. Add unsupported_commitment_profile.
After the primary-input boundary, an otherwise valid input or required validation is rejected solely by identified verifier policy, including a configured resource limit or missing required policy input. Add verifier_policy_rejection.
The verifier intentionally exercises a weaker supported scope when identified policy requires the stronger scope. Add scope_not_exercised.
A required structural, commitment, chain, disclosure, artifact-reference, canonical-record, or other non-channel validation fails. Add every applicable baseline failure category.
The baseline TSA channel is failed or missing. Add channel_failure.
One or more failure reasons were added above. Set overall to failure.
No failure condition applies and the channel is pending_claim. Set overall to incomplete; failure_reasons absent.
No failure condition applies and the channel is verified. Set overall to success; failure_reasons absent.

Exercising a weaker supported scope has no independent outcome effect when identified policy does not require the stronger scope, though the selected scope still limits the result's claims. A bundle-processing error produces no result and does not enter this reduction.

Every result MUST include a non-empty verifier_policy_id that stably identifies the complete policy applied to the invocation within the issuing verifier's namespace, and the same issuing verifier MUST NOT reuse one identifier for materially different policies. That policy includes trust anchors, accepted TSA policy identifiers, revocation policy, algorithm constraints, and generation-time and future-skew constraints, and baseline pending-claim reduction does not depend on it or on out-of-band context. When exact bytes of a corresponding policy artifact are available the result MUST include verifier_policy_sha256 computed over those exact bytes; otherwise that member MAY be absent, and a bare digest is not sufficient policy identification. Verifier output MUST NOT be represented as proving more than the exercised scope.

6.5.7. Chain-Claim Composition

A manifest and its verifier result cover one segment artifact. A relying party claiming continuous chaining across a disclosed range MUST have one validated manifest/result pair for every segment in that range. Every pair MUST carry the same ledger_id and commitment_profile_id under their applicable exact-comparison rules, and segment numbers MUST be contiguous. The first result MUST have chain_status equal to epoch, or to validated after validation against its disclosed predecessor, and every later result MUST have validated. A gap, identity mismatch, failed link, or unavailable required predecessor prevents a continuous-range claim. Successful composition establishes continuity only across that range; it does not establish global dataset completeness or the absence of undisclosed ledgers.

7. Disclosure Classes

Verification claims depend on what is disclosed. This section is the single normative statement of manifest shape per claimed class. The producer claims one class in the manifest; the verifier independently reports the scope selected for its invocation (Section 6.5.1).

Table 2: Disclosure Classes and Manifest Shape
Class Required record_batches shape Supported scope
A (Public Recompute) Non-empty segment: MUST open every batch exactly once, with all record artifacts needed to recompute the claimed segment root. Empty segment: MUST omit. public_recompute or anchor_only; disclosed_batch_recompute only when the artifact contains at least two batches
B (Partner Audit) MUST open a non-empty proper subset of a non-empty segment's batches, each opening complete. Empty and single-batch segments cannot support Class B. disclosed_batch_recompute or weaker
C (Anchor-Only) MUST omit. anchor_only

The disclosed_batch_recompute scope is supported only when the authoritative artifact contains at least two batches and the verifier consumes a non-empty proper subset of them. Selecting that scope for an empty or single-batch segment MUST produce failure with insufficient_disclosure; a verifier can instead select anchor_only.

Every bundle MUST include the segment artifact, the producer manifest, and the tsa member in anchoring, with the response disclosed when the producer state is present. An emitted empty segment MAY be disclosed as Class A or Class C and MUST NOT be disclosed as Class B; both omit record_batches, and the claimed class and exercised scope distinguish an empty public recomputation from anchor-only verification.

A Class A manifest claims disclosure sufficient for public_recompute; it does not claim that a verifier exercised that scope. A missing required opening or record artifact yields insufficient_disclosure. A successful public_recompute result additionally requires complete record and root recomputation; unsupported profiles, invalid records, and digest or commitment failures use their separately defined reasons. Class B outputs MUST NOT be represented as publicly recomputable; a Class B bundle MAY include a policy artifact describing withheld material, and a verifier exercising disclosed_batch_recompute makes no claim about unopened batch preimages. Every scope composes the complete authoritative batch_roots list into segment_root. Class C does not mean without leaf hashes: a singleton batch root equals its unsalted leaf hash, so Class C is not a confidentiality guarantee for low-entropy records. A Class C disclosure MUST be labeled as such and MUST NOT claim record-level reproducibility.

This caveat applies to every successful result: VTL establishes internal consistency and timestamp binding for the disclosed bundle, not completeness of observed or emitted telemetry, physical truth, omitted device lifecycle state, or authorization for autonomous action.

8. Versioning

A document revision does not select commitment semantics, and schema version fields identify only individual object shapes; neither substitutes for commitment_profile_id. This document defines one shape each for the canonical record, segment record, closure policy, producer manifest, and verifier result, so their CDDL rule names carry no version suffix. The first four carry a local version value of 1; an incompatible change to one of them MUST increment that value and a parser MUST reject an unsupported one, but that needs a new commitment identifier only if commitment semantics also change. The verifier result carries no version member: it is verifier-authored output rather than a producer-supplied input, and the verifier_policy_id required by Section 6.5.6 already identifies the rules under which it was produced. A consumer MUST interpret a verifier result under the member semantics defined in Appendix B and MUST NOT infer them from a version member, a media type, a filename, storage, or carriage; none of these are dispatch inputs.

This document defines one randomly generated UUIDv4 commitment_profile_id and requests no registry for it (Section 11). Baseline producers MUST emit exactly that UUID, and verifiers MUST apply dispatch in this order:

  1. a missing or non-text identifier, or a text value that is not a well-formed lowercase RFC 9562 UUID, is an invalid_segment_artifact;
  2. a well-formed lowercase UUID the verifier does not support is an unsupported_commitment_profile, and MUST NOT be classified as invalid_segment_artifact solely because it differs from this document's UUID; and
  3. exact string equality with this document's UUID selects the semantics defined here.

A verifier encountering an unsupported well-formed UUID MUST report unsupported_commitment_profile rather than applying fallback semantics. The identifier selects the canonical-record syntax (Section 4.1), deterministic byte encoding (Section 4.2), tree calculation (Section 4.3), aligned-subtree calculation (Section 4.4.6), artifact bytes (Section 4.5), and chain-link calculation (Section 4.6). It does not select minimum-size capability, output-preserving scheduling or persistence, media registration or carriage, manifest or result schemas, or external evidence profiles, and implementations MUST NOT infer commitment semantics from those properties, a filename, a manifest, an informal name, or identifier parsing. No namespace for additional values is defined.

A selected-semantic change MUST use a new identifier and ledger epoch; an excluded, output-preserving change does not, and the UUID MUST NOT be rebound. A verifier with policy knowledge of conflicting semantics MUST NOT use this profile and MUST report verifier_policy_rejection. A future profile MUST publish a distinct UUID and begin a new epoch with a fresh ledger_id, serial zero, and the all-zero predecessor; producers MUST NOT link across profiles or relabel, re-encode, or reinterpret sealed artifacts. Rollout SHOULD preserve historical verification support and rollback MUST begin another new epoch. A baseline chain claim cannot cross a profile boundary; associating epochs requires a separately authenticated statement that is not a VTL commitment input. Until RFC publication the identifier binding is provisional and artifacts make only an experimental Internet-Draft conformance claim. If the selected commitment semantics change before publication, the changed profile MUST use a new UUID. Publication can retain this UUID only if those selected semantics remain unchanged.

9. Conformance Vectors

This section is informative. Appendix A supplies the normative commitment known-answer vectors: exact record bytes and numeric distinctions, leaf hashes, aligned batch roots, a segment root and artifact, empty lifecycle closure, and duplicate occurrences. It does not supply a complete protocol test suite or timestamp-response fixtures; a non-normative machine-readable corpus maintained with the draft source can carry the larger and malformed cases.

A positive suite ought to cover:

A negative suite ought to cover:

Cross-implementation checks verify byte parity across at least two implementations. The normative vector's interval_ms value of 86400000 is an elapsed duration retained for vector continuity and implies no civil-day, time-zone, daylight-saving, or leap-second alignment.

10. Operational Considerations

10.1. Operational Signals and Deployment

This section follows [RFC5706] and is guidance; the normative rules are in the sections referenced. Deployments SHOULD expose admission, sealing, submission, issuance completion, and verification as distinct states so delayed issuance is not confused with a malformed artifact, and SHOULD emit structured signals with stable event codes for handoff and durability failures, ledger identity and serial conflicts, elapsed-timer health and closure delay, artifact writes and roots, TSA backlog and per-segment status, and storage pressure. Deployments SHOULD document the supported record size and depth, admission backpressure, closure policy, retention, accepted TSA identities and policies, generation-time and skew constraints, verifier policy, and whether reconfiguration can request application before the current interval would otherwise close; and SHOULD periodically verify a recent representative bundle, alerting on failures and on issuance pending beyond policy. Before first admission an operator SHOULD durably provision a fresh ledger_id, configure verifier policy and TSA trust inputs, and prequalify the TSA against Section 6.4. Long-lived deployments can use an external renewal profile such as [RFC4998]. This document defines no management data model, event-code registry, or management-protocol binding.

For predictable bounded operation a deployment SHOULD configure at least one of record_limit or size_limit_bytes, or enforce a finite admission capacity with backpressure; capacity planning for size_limit_bytes needs to accommodate the admitted record that crosses the threshold. For N > 0 records and batch limit B the artifact carries 1 + ((N - 1) / B) roots, each costing 34 CBOR octets, so 100000 records with B = 1024 produce 98 roots and about 3332 octets. Larger batches reduce artifact size but enlarge the minimum Class B disclosure unit, and when a non-null record_limit is no greater than batch_record_limit every segment formed under that policy has at most one batch and cannot support Class B. Timestamp request rate is one per emitted segment, so sealing and response acquisition SHOULD be decoupled through finite recoverable state sized on the maximum emission rate and the expected service outage rather than on interval_ms alone. Verifiers SHOULD avoid resolving artifacts outside the selected scope and MAY cache a successful record digest for identical referenced bytes while counting every disclosed occurrence separately.

11. IANA Considerations

This document has no IANA actions. The application/vnd.vtl.segment+cbor and application/vnd.vtl.manifest+json media types used by this profile are being registered independently of this document under Section 3.2 of [RFC6838]; the vendor tree is used for these project-specific VTL artifact and manifest formats, and this Independent Submission requests no standards-tree registration. Until those registrations complete, implementations use the registered generic application/cbor and application/json types as specified in Section 4.5 and Section 5.1. No specialized media type is requested for verifier results, which use application/json.

This document also requests no CBOR tag, CoAP Content-Format, or IANA registry for commitment_profile_id: the fixed identifier is a randomly generated UUIDv4 under [RFC9562] and needs no central allocation. The absence of a registry provides no discovery of conflicting third-party semantic bindings, a residual issue addressed in Section 12.1; a future registry requires a separate specification defining its registration policy. The explicit statement of no IANA actions follows [RFC8126].

12. Security Considerations

This profile introduces no new cryptographic primitives. Its security depends on a byte-preserving handoff, deterministic commitment encoding, trustworthy admission and elapsed-time handling, accurate verifier reporting, and disciplined artifact and timestamp handling, and assumes the collision and second-preimage resistance of SHA-256 and the soundness of the [RFC9162] construction as specialized here. Threats are stated in the style of [RFC3552]. Unless stated otherwise, success establishes only internal consistency with this profile and successful timestamp validation.

The specialization sorts leaf hashes before reduction and so commits to a multiset rather than an ordered list. Because mapping a multiset of leaf hashes, including occurrence counts, to its ascending sorted list is injective, distinct multisets yield distinct ordered inputs and the collision-resistance argument of [RFC9162] applies unchanged. The 0x00 leaf and 0x01 parent prefixes preserve domain separation and prevent a leaf hash from being reinterpreted as an internal node without finding a collision.

12.1. Artifact and Bundle Integrity

The formats contain no executable content or intrinsic compression but provide no confidentiality. A manifest directs a verifier to bundle-relative paths, creating traversal, indirection, and substitution risks; verifiers MUST use the safe resolver in Section 5.3, because a pathname check followed by an independent open is unsafe. A timestamp over the artifact authenticates neither the manifest nor the result.

An attacker can exploit differences in CBOR validation, re-encoding, occurrence handling, or tree composition, or present metadata that does not match the artifact; fixed syntax and encoding, exact-octet hashing, the in-band profile binding, and identity comparison mitigate these. With no profile registry there is no discovery mechanism for a third party assigning different commitment semantics to the same UUID, so verifiers obtain mappings from identified policy and deployments accepting unoperated producers SHOULD pin the expected identifier. Independent artifact-digest recomputation detects mutation or substitution, but bare leaves and roots omit ledger, serial, and profile identity, so identical multisets produce identical roots across ledgers: a verifier or relying party MUST NOT substitute a bare leaf, batch root, or segment_root for authoritative-artifact and external-binding validation.

12.2. Producer, Sequence, and Lifecycle Threats

An attacker or failed multi-writer deployment can reuse or roll back a serial, emit conflicting artifacts, substitute a predecessor, or disclose only a chain fragment. Fresh ledger identity, fencing, contiguous serials, and predecessor-digest validation detect conflicts when the relevant artifacts are disclosed but do not prove global completeness. A ledger_id must remain unique while evidence can be compared, whereas predictability of segment_number is a required consequence of serial contiguity; operators SHOULD audit identifier generation and alert on reuse of a ledger or ledger/serial pair.

A compromised producer or upstream handoff can fabricate, suppress, substitute, replay, or misassign byte strings. VTL makes outputs tamper-evident only after commitment; upstream transport, credentials, decryption, mapping, and parsing are outside this document, and successful verification MUST NOT be reported as validating those properties. Each admission is a distinct occurrence, so a replay is indistinguishable here from a genuine repeated observation, and successful verification of a false record can make it durably and independently recomputable: a relying party MUST NOT treat a successful result as corroboration of the recorded value.

An attacker can alter the interval, elapsed-time source, policy, close reason, or boundary processing. Committing the snapshot and reason makes the producer claim tamper-evident after sealing but does not prove honest elapsed time or the event that caused closure; a verifier cannot distinguish an honest manual or reconfigure claim from another asserted close reason. False upstream UTC values remain false admitted values, and UTC changes MUST NOT affect membership or reopen a sealed segment. A closed-unsealed interval has no artifact or external binding, so compromise or loss of that state before sealing can block finalization of every later artifact in the chain; durable close state, ordered sealing, finite recoverable queues, and backpressure limit accidental loss but do not make that state externally tamper-evident. An operator MUST NOT represent such state as a sealed segment or a locally queued timestamp job as initiated issuance.

12.3. TSA Trust and Algorithm Lifetime

This profile defines no producer signing key. The TSA is the only signing authority in the baseline and signs an artifact digest rather than attesting the producer's identity or the truth of producer claims; deployments requiring producer attribution MUST obtain it outside this profile.

An attacker can delay or withhold responses, present pending placeholders as final, or exploit ignored missing responses; the status vocabulary and outcome reduction constrain these cases. An incomplete outcome contains no validated external binding and is the outcome an adversary can induce by suppressing a response, so a relying party MUST NOT treat incomplete as equivalent to success and SHOULD reverify from a later bundle disclosing a terminal response. Successful validation depends on the TSA key and trust anchors: a key holder can mint a well-formed token over any digest and asserted genTime. Validation proves only that bound bytes existed no later than the asserted time under identified policy; it proves neither producer nor source identity, nor when the segment opened. A nonce-free response disclosed without tsa_req provides no nonce-correlation assurance.

The identified verifier policy controls acceptable signature and key strength; this profile sets no additional universal strength floor. That policy MUST define later-compromise reevaluation, acceptable historical revocation evidence, and whether a retired trust anchor remains acceptable for a token at its TSTInfo.genTime, and a verifier MUST NOT report verified unless the selected path and anchor are acceptable under the policy identified in that result. Relying parties comparing results from different verifiers therefore need to compare policy identifiers and, when present, policy digests.

SHA-256 is embedded in every commitment in this profile. Retiring it requires a new commitment profile, and Section 8 requires that profile to begin a new epoch with a fresh ledger_id and serial zero. No VTL commitment links the terminated chain to its successor; association requires a separately authenticated statement. Deployments whose retention horizon can exceed the acceptable service life of SHA-256 MUST plan how pre-migration evidence remains verifiable and how the epoch boundary is authenticated. An external renewal profile such as [RFC4998] can support long-term preservation but is not defined here.

12.4. Resource Exhaustion

Producers need input-size limits, rate controls, finite recoverable queues, and backpressure, with the normative rules in Section 4.4.5. Verifiers need finite limits on encoded lengths, collection and certificate counts, and recursion, with baseline requirements and failure mapping in Section 6.1 and Section 6.5.6. Limits need checking before unbounded allocation or recursion, and arithmetic needs to be checked. A limit preventing acquisition or sufficient parsing before the primary-input boundary produces no result; after that boundary, rejection of an otherwise valid input solely for an identified policy limit reports verifier_policy_rejection. Because this document sets acceptance floors only for canonical-record length and depth, two conforming verifiers can differ on inputs exceeding their other limits. These controls reduce exposure but are not a complete denial-of-service defense.

12.5. Verification Scope, Disclosure, and Privacy

Success can be misread as completeness, physical truth, or authorization; the caveat in Section 7 applies to every result and stronger claims require external controls. Class B establishes sorting only within consumed openings and across consumed adjacent boundaries and anchor-only establishes none, so a later Class A disclosure can reveal a global-sort violation without contradicting an earlier narrower result. Likewise a segment with a non-conforming record can verify under anchor_only but MUST fail with invalid_canonical_record under any later scope consuming that record.

Leaf hashes are unsalted and a singleton batch root is exactly its leaf hash, which occurs for every batch when batch_record_limit is one and for the final batch whenever the record count modulo the batch limit is one; even a Class C artifact can therefore disclose individual unsalted leaf hashes, and disclosed low-entropy material permits offline guessing, since omitting record preimages does not omit leaf hashes. A high-entropy application-layer nonce can help but is outside this profile. Because batches are contiguous sorted slices, an opening bounds earlier and later unopened hashes and openings on both sides bound the intervening range, so deployments SHOULD minimize the number and sorted-order spread of openings. Identical segment_root values reveal identical multisets and MUST be considered when roots cross privacy domains.

Even without payloads, manifests, artifacts, and timestamp artifacts reveal counts, layout, outage windows, delay, intervals, close reasons, and emission cadence, and repeated pseudonymous identifiers can link records and enable re-identification. Operators SHOULD minimize personal data, avoid unnecessary stable identifiers, assess Class A linkability, and apply suitable access and retention controls. Privacy-preserving disclosures MUST NOT be called publicly recomputable unless Class A conditions are met.

12.6. Extension Boundaries

Separate specifications can define upstream admission, exported bundle types, SCITT or COSE publication, other timestamping or attestation, withheld-material disclosure, or other commitment profiles; opaque extensions do not affect baseline results. A separate disclosure specification can staple RFC 9162 paths or a reduced-tree construction inspired by [RFC4998], whose objects are not interchangeable with VTL batch roots. Any change to commitment bytes or calculations requires a distinct commitment_profile_id.

13. References

13.1. Normative References

[FIPS180-4]
National Institute of Standards and Technology, "Secure Hash Standard (SHS)", FIPS PUB 180-4, DOI 10.6028/NIST.FIPS.180-4, , <https://doi.org/10.6028/NIST.FIPS.180-4>.
[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
[RFC2634]
Hoffman, P., "Enhanced Security Services for S/MIME", RFC 2634, DOI 10.17487/RFC2634, , <https://www.rfc-editor.org/info/rfc2634>.
[RFC3161]
Adams, C., Cain, P., Pinkas, D., and R. Zuccherato, "Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)", RFC 3161, DOI 10.17487/RFC3161, , <https://www.rfc-editor.org/rfc/rfc3161>.
[RFC5035]
Schaad, J., "Enhanced Security Services (ESS) Update: Adding CertID Algorithm Agility", RFC 5035, DOI 10.17487/RFC5035, , <https://www.rfc-editor.org/rfc/rfc5035>.
[RFC5280]
Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and T. Polk, "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, , <https://www.rfc-editor.org/rfc/rfc5280>.
[RFC5652]
Housley, R., "Cryptographic Message Syntax (CMS)", STD 70, RFC 5652, DOI 10.17487/RFC5652, , <https://www.rfc-editor.org/rfc/rfc5652>.
[RFC5816]
Santesson, S. and N. Pope, "ESSCertIDv2 Update for RFC 3161", RFC 5816, DOI 10.17487/RFC5816, , <https://www.rfc-editor.org/rfc/rfc5816>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
[RFC8259]
Bray, T., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10.17487/RFC8259, , <https://www.rfc-editor.org/rfc/rfc8259>.
[RFC8610]
Bormann, C. and P. Hoffman, "Concise Data Definition Language (CDDL): A Notational Convention to Express CBOR and JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, , <https://www.rfc-editor.org/rfc/rfc8610>.
[RFC8949]
Bormann, C. and P. Hoffman, "Concise Binary Object Representation (CBOR)", STD 94, RFC 8949, DOI 10.17487/RFC8949, , <https://www.rfc-editor.org/rfc/rfc8949>.
[RFC9162]
Laurie, B., Messeri, E., and R. Stradling, "Certificate Transparency Version 2.0", RFC 9162, DOI 10.17487/RFC9162, , <https://www.rfc-editor.org/rfc/rfc9162>.
[RFC9562]
Davis, K., Peabody, B., and P. Leach, "Universally Unique IDentifiers (UUIDs)", RFC 9562, DOI 10.17487/RFC9562, , <https://www.rfc-editor.org/info/rfc9562>.

13.2. Informative References

[RFC3552]
Rescorla, E. and B. Korver, "Guidelines for Writing RFC Text on Security Considerations", BCP 72, RFC 3552, DOI 10.17487/RFC3552, , <https://www.rfc-editor.org/rfc/rfc3552>.
[RFC4998]
Gondrom, T., Brandner, R., and U. Pordesch, "Evidence Record Syntax (ERS)", RFC 4998, DOI 10.17487/RFC4998, , <https://www.rfc-editor.org/info/rfc4998>.
[RFC5706]
Harrington, D., "Guidelines for Considering Operations and Management of New Protocols and Protocol Extensions", RFC 5706, DOI 10.17487/RFC5706, , <https://www.rfc-editor.org/info/rfc5706>.
[RFC6234]
Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, DOI 10.17487/RFC6234, , <https://www.rfc-editor.org/info/rfc6234>.
[RFC6838]
Freed, N., Klensin, J., and T. Hansen, "Media Type Specifications and Registration Procedures", BCP 13, RFC 6838, DOI 10.17487/RFC6838, , <https://www.rfc-editor.org/info/rfc6838>.
[RFC8126]
Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, DOI 10.17487/RFC8126, , <https://www.rfc-editor.org/info/rfc8126>.
[RFC8785]
Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, DOI 10.17487/RFC8785, , <https://www.rfc-editor.org/rfc/rfc8785>.
[RFC9052]
Schaad, J., "CBOR Object Signing and Encryption (COSE): Structures and Process", STD 96, RFC 9052, DOI 10.17487/RFC9052, , <https://www.rfc-editor.org/info/rfc9052>.
[RFC9943]
Birkholz, H., Delignat-Lavaud, A., Fournet, C., Deshpande, Y., and S. Lasker, "An Architecture for Trustworthy and Transparent Digital Supply Chains", RFC 9943, DOI 10.17487/RFC9943, , <https://www.rfc-editor.org/info/rfc9943>.

Appendix A. Compact Commitment Known-Answer Vectors

Wrapped hexadecimal values in this appendix are presentation-only; a verifier or implementer obtains each exact value by concatenating adjacent lines without inserting whitespace.

The following normative canonical-record vectors differ only in the final payload item. They demonstrate that the integer 1, floating-point 1.0, positive floating-point zero, and negative floating-point zero retain distinct deterministic encodings and produce distinct leaf hashes.

integer_1_record =
  87014800000000000000010100f60001
integer_1_leaf =
  61d6b5be58a51d90b833cc8f2558d3b0e30a0c41cae971a81a92596013ca4e5c

float_1_record =
  87014800000000000000010100f600f93c00
float_1_leaf =
  793c53c545002c8b872ff84d1f479c1243f36e4ba57dbc8b387ec52c47fe62df

positive_zero_record =
  87014800000000000000010100f600f90000
positive_zero_leaf =
  5cd7c34a8083929449188288272e339f87fe06f64738b2f8f84a2c317ee40c27

negative_zero_record =
  87014800000000000000010100f600f98000
negative_zero_leaf =
  a45dabed6caf13fe4901f9bd596325e65e13d006dbb1ec8f1180b671537ffccb

The following compact known-answer vector is normative for the profile UUID defined in Section 2. Hexadecimal values are lowercase and unprefixed. It encodes the epoch segment: the outer segment uses segment_number = 0, and prev_segment_sha256 is a 32-octet all-zero byte string. The segment uses batch_record_limit = 2 and carries two aligned subtree roots.

segment_cbor is 462 octets and commits three leaves in two aligned batch subtrees. segment_sha256 is SHA-256 over those exact segment_cbor octets. It is the authoritative artifact digest submitted through the baseline TSA channel and carried by a successor's prev_segment_sha256; it is not an additional field encoded in this artifact.

record_1 =
  87014800000000000000010100f600f6
leaf_1 =
  b83bc27f2d8be3a66373af24e6af3eeffff99ff0696ae18c70e379e853796d26
record_2 =
  87014800000000000000020201f600f6
leaf_2 =
  b5227357cf5d0619914971d8dc5218c5a99260bc5d5e1b60cb1083a911a2acaf
record_3 =
  87014800000000000000030302f600f6
leaf_3 =
  4f82e3e7ee90a111774dd951471a31d4582e0908a0bd5fd63c0080c0231f40cc

sorted_leaves =
  4f82e3e7ee90a111774dd951471a31d4582e0908a0bd5fd63c0080c0231f40cc
  b5227357cf5d0619914971d8dc5218c5a99260bc5d5e1b60cb1083a911a2acaf
  b83bc27f2d8be3a66373af24e6af3eeffff99ff0696ae18c70e379e853796d26
batch_0_root =
  554491e4edf28061622396b83a870db4652211557127c664c6be1c4ad66471ff
batch_1_root =
  b83bc27f2d8be3a66373af24e6af3eeffff99ff0696ae18c70e379e853796d26
segment_root =
  bc6502552ed0c515f58d1c632e54db37594042609b59838eb0d5b3d5842aa054

segment_cbor =
  aa6776657273696f6e01696c65646765725f6964782062376131643565343063
  366634333865396137356462323763393666333161616b62617463685f726f6f
  7473825820554491e4edf28061622396b83a870db4652211557127c664c6be1c
  4ad66471ff5820b83bc27f2d8be3a66373af24e6af3eeffff99ff0696ae18c70
  e379e853796d266c636c6f73655f726561736f6e68696e74657276616c6c7265
  636f72645f636f756e74036c7365676d656e745f726f6f745820bc6502552ed0
  c515f58d1c632e54db37594042609b59838eb0d5b3d5842aa0546e636c6f7375
  72655f706f6c696379a66776657273696f6e016a656d7074795f6d6f64656873
  757070726573736b696e74657276616c5f6d731a05265c006c7265636f72645f
  6c696d6974f67073697a655f6c696d69745f6279746573f67262617463685f72
  65636f72645f6c696d6974026e7365676d656e745f6e756d6265720073707265
  765f7365676d656e745f73686132353658200000000000000000000000000000
  00000000000000000000000000000000000075636f6d6d69746d656e745f7072
  6f66696c655f6964782463303861646534652d313738352d346562362d393634
  382d623730303364373632383864
segment_sha256 =
  2672cb72d5f06863110af1b30660c7e5ba495c0b2ce7d084b0436e010e99388d

three_identical_record_1_root =
  05ddc48e556d67534bf7960a70209696ca9eeb6f18d5595358e7f4804ec87701
four_identical_record_1_root =
  d5d26faa3f54d81d8173700a48d9286179c9d35685d464183a99b1e966f1dfd8

The following 394-octet artifact is the empty successor to the segment above. It uses the same ledger, policy, and profile, sets segment_number to 1 and prev_segment_sha256 to the preceding segment_sha256, and exercises the lifecycle exception in which an empty shutdown artifact retains empty_mode equal to suppress. Its empty batch_roots array and segment_root equal to SHA-256 over zero octets are normative.

empty_shutdown_suppress_segment_cbor =
  aa6776657273696f6e01696c65646765725f6964782062376131643565343063
  366634333865396137356462323763393666333161616b62617463685f726f6f
  7473806c636c6f73655f726561736f6e6873687574646f776e6c7265636f7264
  5f636f756e74006c7365676d656e745f726f6f745820e3b0c44298fc1c149afb
  f4c8996fb92427ae41e4649b934ca495991b7852b8556e636c6f737572655f70
  6f6c696379a66776657273696f6e016a656d7074795f6d6f6465687375707072
  6573736b696e74657276616c5f6d731a05265c006c7265636f72645f6c696d69
  74f67073697a655f6c696d69745f6279746573f67262617463685f7265636f72
  645f6c696d6974026e7365676d656e745f6e756d6265720173707265765f7365
  676d656e745f73686132353658202672cb72d5f06863110af1b30660c7e5ba49
  5c0b2ce7d084b0436e010e99388d75636f6d6d69746d656e745f70726f66696c
  655f6964782463303861646534652d313738352d346562362d393634382d6237
  30303364373632383864
empty_shutdown_suppress_segment_sha256 =
  cde8b546adc5afa446195bb9cc4e519d4bd1c9529d1678bf6e076ef6d24e229e

three_identical_record_1_root and four_identical_record_1_root are segment_root values for segments containing respectively three and four occurrences of the exact record_1 bytes. They are independent of batch_record_limit; that field changes the aligned subtree representation, not the RFC 9162 reduction of the complete sorted leaf multiset.

A published machine-readable vector set can carry exact canonical bytes, digests, expected roots, and the applicable commitment_profile_id. The exact known-answer vectors in this appendix are normative.

Appendix B. Producer Manifest and Verifier Result CDDL

This appendix defines the CDDL ([RFC8610]) shape for the producer manifest and the separate verifier result. Both MUST be UTF-8 JSON texts under [RFC8259]; parsers MUST reject duplicate object member names and generators MUST NOT emit them.

These prose constraints apply in addition to the shape below. json-hex32 is exactly 32 lowercase hexadecimal characters and json-hex64 exactly 64. segment_number is a JSON string holding the shortest unsigned base-10 form of a uint64 (0, or a digit 1 through 9 followed by zero or more digits) whose value MUST NOT exceed 18446744073709551615, which avoids loss of precision where a JSON implementation's exact integer range ends at 2^53-1. relative-path is intentionally structural because RFC 8610 regular expressions cannot portably express cross-platform containment checks; implementations MUST apply the semantic path rules in Section 5.3 to every consumed path-bearing object, including extensions.

Both commitment_profile_id members are generic tstr so that a manifest can repeat, and a result can echo, a future or unsupported artifact value; neither relaxes the baseline producer requirement in Section 8, and the result member is optional because a malformed artifact might not decode far enough to recover it. chain_status MUST be present whenever the artifact decodes far enough to support one of the chain conclusions defined here and MUST be absent otherwise, absence making no chain claim; failure_reasons MUST be present and non-empty exactly when overall is failure. Both the anchoring and channels maps MUST contain exactly the tsa key, and extension containers MUST NOT add channel keys or alter a baseline status or outcome. Violating either direction of the state-to-reference rule in Section 5.1 makes the manifest structurally invalid, whereas a nonce-bearing response without tsa_req is a timestamp-channel verification failure rather than a manifest-structure error, because a nonce is learned only by inspecting the response.

producer-manifest = {
  "version": 1,
  "ledger_id": json-hex32,
  "segment_number": uint64-decimal,
  "commitment_profile_id": tstr,
  "disclosure_class": disclosure-class,
  "artifacts": artifacts,
  "anchoring": anchoring,
  ? "extensions": { * tstr => json-data },
}

artifacts = {
  "segment_cbor": artifact-ref,
  ? "predecessor_segment_cbor": artifact-ref,
  ? "record_batches": [+ record-batch-opening],
  ? "tsa_req": artifact-ref,
  ? "tsa_tsr": artifact-ref,
  ? "extensions": { * tstr => artifact-ref }
}

record-batch-opening = {
  "batch_number": uint64-decimal,
  "records": [+ artifact-ref]
}

artifact-ref = {
  "path": relative-path,
  "sha256": json-hex64
}

anchoring = {
  "tsa": producer-tsa-state
}

producer-tsa-state = {
  "status": "present" / "pending" / "unavailable"
}

verifier-result = {
  "artifact_sha256": json-hex64,
  "manifest_sha256": json-hex64,
  ? "commitment_profile_id": tstr,
  "claimed_disclosure_class": disclosure-class,
  "verification_scope":
    "public_recompute" / "disclosed_batch_recompute" / "anchor_only",
  ? "chain_status": chain-status,
  "channels": channels,
  "verifier_policy_id": non-empty-tstr,
  ? "verifier_policy_sha256": json-hex64,
  "overall": "success" / "incomplete" / "failure",
  ? "failure_reasons": [+ baseline-failure-reason],
  ? "extensions": { * tstr => json-data }
}

chain-status =
  "epoch" /
  "validated" /
  "predecessor_not_disclosed" /
  "failed"

baseline-failure-reason =
  "unsupported_commitment_profile" /
  "invalid_canonical_record" /
  "invalid_segment_artifact" /
  "commitment_mismatch" /
  "segment_chain_mismatch" /
  "channel_failure" /
  "insufficient_disclosure" /
  "scope_not_exercised" /
  "verifier_policy_rejection"

channels = { "tsa": verifier-tsa-result }

verifier-tsa-result =
  {
    "status": "verified",
    ? "extensions": { * tstr => json-data }
  } /
  {
    "status": "pending_claim",
    "reason": "producer_pending_claim",
    ? "extensions": { * tstr => json-data }
  } /
  {
    "status": "missing" / "failed",
    "reason": non-empty-tstr,
    ? "extensions": { * tstr => json-data }
  }

disclosure-class = "A" / "B" / "C"
relative-path = tstr
uint64-decimal = tstr
json-hex32 = (tstr .regexp "[0-9a-f]{32}") .size 32
json-hex64 = (tstr .regexp "[0-9a-f]{64}") .size 64
non-empty-tstr = tstr .size (1..18446744073709551615)
json-data =
  ; For JSON null; finite-float restrictions are normative prose.
  nil / bool / int / float / tstr /
  [* json-data] / { * tstr => json-data }

Appendix C. Authoritative Segment Artifact CDDL

This appendix gives the structural CDDL for the authoritative segment artifact. The lexical and cross-field requirements in Section 4.5, the encoding rules in Section 4.2, the commitment-tree and empty-segment rules in Section 4.3, the formation rules in Section 4.4, and the chain rules in Section 4.6 are normative in addition to this shape. cbor-hex32 is distinct from the JSON rule in Appendix B and is exactly 32 lowercase hexadecimal characters. CDDL cannot express the power-of-two restriction or the upper bound of 2^63 on batch_record_limit; both prose constraints apply in addition to power-of-two-uint64.

segment-record = {
  "version": 1,
  "commitment_profile_id": tstr,
  "ledger_id": cbor-hex32,
  "segment_number": uint64,
  "closure_policy": segment-closure-policy,
  "close_reason": close-reason,
  "prev_segment_sha256": sha256-value,
  "record_count": uint64,
  "batch_roots": [* sha256-value],
  "segment_root": sha256-value
}

segment-closure-policy = {
  "version": 1,
  "interval_ms": positive-uint64,
  "batch_record_limit": power-of-two-uint64,
  "record_limit": positive-uint64 / nil,
  "size_limit_bytes": positive-uint64 / nil,
  "empty_mode": "emit" / "suppress"
}

close-reason =
  "interval" /
  "reconfigure" /
  "record_limit" /
  "size_limit" /
  "shutdown" /
  "recovery" /
  "manual"

uint64 = 0..18446744073709551615
positive-uint64 = 1..18446744073709551615
power-of-two-uint64 = positive-uint64
cbor-hex32 = (tstr .regexp "[0-9a-f]{32}") .size 32
sha256-value = bstr .size 32

Acknowledgments

The author thanks Joe Clarke for OPSDIR review feedback that improved the operational considerations and deployment guidance.

Author's Address

Bilal El Khatabi
Independent
Rabat
Morocco