Internet-Draft VTL August 2026
El Khatabi Expires 27 February 2027 [Page]
Workgroup:
Independent Submission
Internet-Draft:
draft-elkhatabi-verifiable-telemetry-ledgers-10
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 admission and assignment of those byte strings to 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 authoritative segment artifact digest to timestamp artifacts under the RFC 3161 profile. Transport framing, decryption, anti-replay processing, payload interpretation, and source-telemetry-to-record mapping are outside this profile. Segment closure uses a deployment-configured elapsed-time interval and does not depend on calendar dates from either the source or the ledger producer. Every baseline producer uses RFC 3161 timestamping, as updated by RFC 5816 and profiled by this document, for every emitted segment. Alternative timestamping and attestation mechanisms are outside this profile and require a separate specification.

The profile enables independent recomputation and audit of disclosed evidence from the admitted canonical-record bytes onward. It does not verify how source telemetry was authenticated, interpreted, or mapped to those bytes, and it does not cover device onboarding, end-to-end security of sensor values, or 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 27 February 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 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 timestamp artifacts are mutually consistent. This profile standardizes the second question.

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

Protocol at a Glance

The following informative summary provides a reading path through the normative requirements that follow:

  1. Admission. The ledger producer validates the byte-level profile and admits the unchanged canonical-record bytes into the logical interval open at the admission linearization point. Each admission is one record occurrence, including when two admitted occurrences have identical bytes.
  2. Commitment. The producer computes each leaf as SHA-256(0x00 || record), sorts the leaf hashes, and calculates segment_root over the complete sorted list using the domain-separated tree construction in Section 4.3. It partitions the same list into aligned batch subtrees. The authoritative segment artifact carries those subtree roots, and their RFC 9162 tree composition reproduces segment_root.
  3. Sealing. The producer encodes the authoritative segment artifact using deterministic CBOR. The artifact carries the commitment profile identifier, ledger and segment identity, closure claim, predecessor-artifact digest, record count, batch-subtree roots, and segment root.
  4. Timestamp artifacts. The producer computes SHA-256 over the exact authoritative segment artifact bytes and submits that digest using [RFC3161] for every emitted segment.
  5. Verification. A verifier starts from the authoritative segment artifact and performs the validations supported by the disclosed bundle. Class A supports public record-level recomputation. Class B supports controlled audit when some record material is withheld. Class C provides segment-level commitment and timestamp artifacts without record preimages and does not support opening batch contents. The verifier reports the exercised scope, segment-chain status, TSA channel status, and overall outcome; it claims validated chain adjacency only when the predecessor artifact is disclosed.

Segment intervals are ledger batching rules, not source-reporting periods or civil-time claims. The producer assigns each admitted record to the logical interval open at its admission linearization point and uses a non-decreasing elapsed-time source for interval closure. Source arrival time, upstream processing time, ingest_time, device_time, and UTC do not select segment membership. Segment identity is (ledger_id, segment_number), and a sealed segment is not reopened. Section 4.4 specifies boundary ordering, empty intervals, size-triggered closure, policy changes, and recovery behavior.


Source telemetry
      |
      v
+----------------------------+       OUTSIDE THIS PROFILE
| Upstream admission system  |
| auth / replay / projection |
+----------------------------+
      | exact canonical-record bytes
======|================================================ boundary
      v
+----------------------------+ --> authoritative segment
| Ledger producer            |     artifact ----------> Verifier
| admit / assign / commit    | --> artifact digest
| seal / chain               |          |
+----------------------------+          +--> timestamp artifacts
Figure 1: Evidence Path and Interoperability Boundary

The boundary is admission of an already-formed canonical-record byte string. From that point, the ledger producer is responsible for byte preservation, segment assignment, durable state, artifact construction, and timestamp submission. Verification establishes consistency of disclosed post-admission evidence, not source authenticity, dataset completeness, or physical truth.

Document Roadmap. Section 2 and Section 3 define the common terminology and system roles. The producer implementation path continues with Section 4, Section 5, Section 6, Section 8, and Section 9. The verifier implementation path additionally includes Section 7 and Appendices A through C. The operator path additionally includes Section 10 and Section 12.

1.1. Relationship to Existing Work

VTL reuses the Merkle Tree Hash construction from [RFC9162] to commit a producer-local segment, but it does not define a Certificate Transparency log or protocol. In particular, VTL defines no transparency service, public append-only log, statement-registration protocol, receipt, inclusion proof, consistency proof, gossip protocol, or log-auditing API.

Unlike Certificate Transparency, VTL commits to a multiset rather than an append-ordered sequence: it sorts leaf hashes before tree reduction. The resulting root is independent of admission order and cannot support append-order, prefix, or consistency claims between partial snapshots. Chaining orders sealed segment artifacts, not records within a segment.

The SCITT architecture in [RFC9943] defines signed statements, registration with transparency services, verifiable data structures, and receipts. Those services and claims are outside the VTL baseline. A separate specification can publish a VTL artifact or bundle through SCITT, but doing so does not replace or alter the VTL commitment and verification rules.

[RFC9052] defines COSE signing and encryption structures. VTL does not use a COSE object as its authoritative segment artifact and does not require a COSE producer signature. A deployment can wrap or publish VTL artifacts using COSE under a separate profile without changing the bytes committed by VTL.

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.

Terms:

The following JSON excerpt is illustrative; the authoritative segment artifact is CBOR and contains additional required fields:

{
  "commitment_profile_id": "c08ade4e-1785-4eb6-9648-b7003d76288d",
  "ledger_id": "0123456789abcdef0123456789abcdef",
  "segment_number": 42
}

The profile identifier has that same fixed value wherever this document carries the field. ledger_id and segment_number identify a particular ledger and segment and therefore vary.

3. System Roles

The roles below participate at different sides of the profile boundary. Baseline producer and verifier conformance is defined in Section 6.1.

4. Data and Commitment Model

The normative input to this section is an exact canonical-record byte string that has already been produced by an upstream admission system. The interoperable core begins at that byte-level handoff and covers record admission, segment assignment, deterministic hashing and tree calculation, authoritative segment artifacts, disclosure, and verification. No source transport, decryption, anti-replay, payload interpretation, or source-to-record projection is defined by this document.

4.1. Canonical-Record Input Boundary

The protocol input defined by this document is one exact canonical-record byte string. The byte string MUST encode exactly one CBOR data item using the record shape below and the deterministic encoding constraints in Section 4.2. A ledger producer MUST reject a proposed 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. A producer MAY accept larger conforming records under its documented resource policy. A configured maximum below 4096 octets does not satisfy baseline producer conformance. The 4096-octet value is a shared minimum acceptance capability that permits bounded-resource implementations; it is not derived from a network MTU and is not a protocol maximum or a default deployment limit.

A larger byte string remains in this commitment profile when it otherwise conforms. An implementation MAY receive or validate such a byte string incrementally, but admission still applies to one complete byte string encoding exactly one canonical record. An upstream system MAY instead produce multiple independently conforming canonical records, but each is a separate admitted occurrence. This profile does not treat such records as transparent chunks of one canonical record or define their reassembly semantics.

Once admitted, the exact supplied octets are authoritative. The ledger producer MUST use those octets, without modification, as the leaf preimage under Section 4.3. It MUST NOT decode and re-encode, normalize, enrich, remove, reorder, or otherwise substitute a different byte string for commitment. Each admission event contributes one record occurrence, including when two admitted occurrences have identical byte strings.

Source transport, framing, authentication, decryption, anti-replay processing, duplicate-submission policy, device-identifier mapping, message-type mapping, payload interpretation, and construction of the canonical-record byte string are upstream of this boundary and are not specified or verified here. A physical gateway MAY perform those functions and ledger production in one implementation, but its interoperability claim under this document begins only with the exact byte string passed across this logical boundary.

Under the commitment profile defined by this document, 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; they do not move source-to-record mapping into this profile. Their byte-level requirements are:

  • version is the unsigned integer 1.
  • device_id is an opaque 8-octet identifier assigned upstream. This document defines no mapping from a transport or deployment alias to this value.
  • fc is an unsigned upstream-supplied sequence value. This document defines no replay or duplicate-suppression semantics for it.
  • ingest_time is an unsigned count of whole non-leap seconds since 1970-01-01T00:00:00Z. Its assignment and accuracy are upstream responsibilities, and it MUST NOT select segment membership.
  • device_time is an unsigned application timestamp or null. Its epoch and unit are upstream semantics, and it MUST NOT select segment membership.
  • kind is an unsigned application discriminator. This document defines no message-type or payload-family mapping for it.
  • payload is one recursively restricted CBOR data item of the type above. Its 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 values and have distinct deterministic encodings. Positive floating-point zero and negative floating-point zero are also distinct: both are finite and permitted, and their shortest exact CBOR 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 different byte-level field type is a different commitment profile and requires a distinct commitment_profile_id. Application schemas MAY separately define the meanings of device_id, fc, device_time, kind, and payload, but successful verification under this document does not validate those external semantics.

Class A recomputation starts with the disclosed canonical-record byte strings. It demonstrates that those exact bytes produce the disclosed leaf digests, roots, and authoritative segment artifact. It does not reconstruct the bytes from source telemetry. Conformance vectors for this profile therefore begin with exact canonical-record byte strings; transport messages and upstream projection fixtures, if supplied, are informative and are not inputs to baseline conformance.

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. 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. Negative floating-point zero is finite and is permitted. Exact type, sign, and encoded-octet distinctions described in Section 4.1 are preserved by admission and verification.

Because this profile permits only text-string map keys and requires their preferred deterministic encodings, bytewise ordering of their encoded heads first orders them by UTF-8 byte length and then orders equal-length keys by their encoded bytes. The resulting order is also the length-first order described in Section 4.2.3 of [RFC8949]. This coincidence does not replace any other core deterministic-encoding requirement in Section 4.2.1.

JSON projections are optional and 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; no implementation may re-encode, normalize, or reserialize input before hashing.

4.3. Deterministic Commitment Tree Calculation

For a given segment S, the current commitment 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 for the SHA family.

Every SHA-256 invocation in this profile MUST produce the complete 32-octet SHA-256 digest; a truncated digest or a different SHA-2 function is nonconformant. For commitment-tree hashing, an implementation MUST use exactly the 0x00 leaf prefix and 0x01 internal-node prefix specified below 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 byte 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 0x00 and 0x01 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. Its closure_policy.empty_mode MUST be emit, except that a mandatory empty shutdown or recovery lifecycle artifact retains the committed suppress value as specified in Section 4.4. Any other empty artifact under suppress mode is invalid. 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. Admission order selects segment membership at the boundary but is not itself a verifier-visible total-order claim. 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 defined in Section 6.5.1; weaker scopes provide only the ordering assurances explicitly defined for those scopes.

Any future change to this calculation that alters commitment bytes MUST use a new commitment_profile_id.

4.4. Segment Formation and Closure

Normal Interval Timing and Admission

Segment formation uses an elapsed-time source maintained by the ledger producer. That source MUST be non-decreasing and MUST progress during normal operation while an interval is open. Its resolution MUST be no coarser than the smaller of 1000 milliseconds and the configured closure_policy.interval_ms value. Resolution here means the greatest quantization step exposed to the boundary logic. It constrains comparison granularity, not the precision of the underlying clock. A producer MAY sample the elapsed-time source or evaluate the boundary more frequently and MAY use a finer-resolution source. The policy field is a positive uint64 number of milliseconds and specifies a fixed elapsed duration. It is not a civil-time duration and MUST NOT be aligned by local midnight, UTC midnight, time zone, daylight-saving transition, or leap second. The first logical interval of an epoch opens when the ledger is activated. Activation is the deployment-defined start action by which the producer makes the ledger_id and initial segment state durable before admitting a record. Reactivation resumes that durable state after a shutdown closure. Each normal or early closure other than shutdown immediately opens the next logical interval. Logical intervals continue across idle periods in both empty modes; suppress changes artifact emission and serial consumption, not the elapsed-time 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 logical interval.
  • At T or later, the producer MUST process the boundary before another acceptance can linearize.
  • Upstream processing can begin before T, but if canonical-record admission linearizes at or after T, the record belongs to the next logical interval.
  • Source arrival time, upstream validation or record-construction time, device_time, and ingest_time MUST NOT override this assignment.

The producer MUST determine deadline expiry by checked elapsed-time subtraction; it MUST NOT add closure_policy.interval_ms to an absolute counter in a way that can wrap. If elapsed time cannot be represented or compared safely, the producer MUST enter the recovery procedure below before accepting another record.

Policy Changes and Early Closure

The producer snapshots closure_policy when a logical interval opens. That snapshot MUST remain unchanged through closure. A normal configuration update becomes effective for the next logical interval. An immediate update MUST first close the current logical interval under the old snapshot. It emits an artifact with close_reason equal to reconfigure unless the interval is empty under suppress mode, in which case it emits no artifact and consumes no segment number.

Early closure is permitted only for a condition represented by the committed close_reason. A producer MUST NOT place a record whose acceptance linearizes after the interval deadline into the expired interval merely because closure processing was late. Late closure is an operational fault and SHOULD generate an alarm.

A manual closure occurs when an operator-initiated action, external to the producer's automatic timer and limit evaluation, requests immediate closure. A producer MUST NOT use manual for a closure triggered by its own timer or limit evaluation. The producer MUST serialize that action with admission, close the current logical interval under its existing policy snapshot, and immediately open the next logical interval. Artifact emission and serial consumption for an empty manual interval follow the committed empty_mode. A shutdown closure occurs when the producer closes the current logical interval as part of an orderly cessation of ledger operation. It MUST NOT open another logical interval until the ledger is reactivated. A shutdown closure MUST emit an artifact and consume a segment number even when the interval is empty and its committed empty_mode is suppress.

Limits and Close-Reason Selection

The interval reason applies when the snapshotted interval_ms elapsed-time deadline expires and no higher-precedence close condition applies.

closure_policy.record_limit, when non-null, is the maximum number of admitted records in one logical interval. The interval closes immediately after the acceptance that makes the record count equal to record_limit.

closure_policy.size_limit_bytes, when non-null, is a close threshold for the sum of the lengths of the exact admitted canonical-record byte strings. After an acceptance has linearized, if the resulting byte total is greater than or equal to size_limit_bytes, the producer MUST close that logical interval before another acceptance can linearize. The triggering record remains assigned to the interval because segment assignment is final at the admission linearization point. Consequently, size_limit_bytes is not a hard pre-admission maximum and an emitted segment can exceed the threshold by the contribution of its final admitted record. If a single record admitted to an empty interval exceeds size_limit_bytes, that record forms a one-record interval, which MUST close before another acceptance can linearize. Threshold comparisons and byte-count updates MUST use checked arithmetic; the producer MUST enter recovery or stop acceptance before acknowledging a record if the new total cannot be represented safely.

If multiple close conditions are simultaneously true, the committed close_reason MUST be selected by the following precedence, numbered from highest to lowest:

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

The selected reason does not change which acceptances already linearized into the interval.

Empty Intervals and Timer Continuity

While elapsed-timer continuity is maintained, in emit mode every logical interval produces an artifact; an empty interval uses the empty construction in Section 4.3. In suppress mode, an empty logical interval produces no artifact and consumes no segment number, except that shutdown and recovery closures always emit the empty construction and consume a segment number. The next emitted artifact uses the next contiguous number. A suppressed interval makes neither a completeness claim nor a claim that no event occurred.

Suspend time counts toward closure_policy.interval_ms when the selected elapsed-time source advances across suspend. If it does not, or if its continuity is uncertain, the producer MUST apply the recovery procedure before accepting another record.

Recovery and Durable State

After restart or failover, the producer MUST recover the active ledger_id, next segment number, policy snapshot, and any open interval state before accepting more telemetry. If elapsed-time or open-interval state is uncertain, the producer MUST NOT infer the boundary from a possibly regressed wall clock. It MUST either stop acceptance or seal recoverable material with close_reason equal to recovery before accepting into a new interval. A recovery closure MUST emit an artifact and consume a segment number even when the recoverable interval is empty and its committed empty_mode is suppress. It MUST NOT synthesize historical empty artifacts and represent them as having been sealed at missed deadlines.

The admission linearization point is a durable transaction boundary. Before acknowledging acceptance, the producer MUST make the exact canonical-record octets, their membership in the open interval, the interval record count and byte count, and the corresponding recovery state atomically durable or recoverable. After a crash, an acceptance MUST be recovered exactly once in the same logical interval, or it MUST be treated as never having completed. Segment sealing MUST atomically persist the authoritative segment artifact, its digest, the next serial allocation, and the new open-interval state before admission resumes.

These durability requirements specify the observable crash-recovery outcome, not a particular operating-system call. An implementation can use fsync(), a transactional store, replicated durable state, or another mechanism only if the required state is durable or recoverable before acceptance is acknowledged. Calling fsync() alone is not sufficient when the surrounding storage and transaction design does not provide that outcome.

Deterministic Aligned Batch Subtrees

In this profile, 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. Partition that list 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 B hashes, and so on; only the last batch can contain fewer than B hashes. Compute each batch_roots[i] as VTLRoot over that batch's already-computed leaf hashes. This rule is the sole baseline batch partitioning rule and is independent of storage flushes, worker scheduling, or input-file layout.

Requiring a power of two makes every full batch align with a recursive VTLRoot split. An aligned batch root can therefore substitute directly for its subtree when composing segment_root, without an auxiliary sibling path or a storage-dependent tree shape.

Let N be the segment 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 batch 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. Because B is a power of two and the range spans multiple batches, k is a batch boundary. Recursively compose the roots before and after that boundary, then 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 a CT wire object.

4.5. Authoritative Segment Artifact Schema

The authoritative segment artifact is a deterministic CBOR-encoded segment record produced under Section 4.2. It 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. It is not a transport envelope, publication statement, or complete verification claim.

An authoritative segment 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. A producer manifest MUST repeat the same value and verifiers MUST report a mismatch as commitment_mismatch. Profile dispatch follows the canonical rules in 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. A baseline producer MUST emit exactly the profile UUID defined in Section 2.
  • ledger_id (tstr): stable ledger identifier consisting of exactly 32 lowercase hexadecimal characters.
  • segment_number (uint): serial number in the range 0..(2^64-1).
  • closure_policy (map): immutable policy snapshot for this logical interval, 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. When record_count is zero, closure_policy.empty_mode MUST be emit unless close_reason is shutdown or recovery.
  • prev_segment_sha256 (bstr): 32-octet SHA-256 digest of the exact bytes of the previous authoritative segment artifact, or the epoch predecessor value defined 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; exactly empty for an empty emitted segment.
  • segment_root (bstr): 32-octet deterministic segment root.

Array position is the batch number. The root at position i MUST commit the consecutive sorted-leaf range beginning at i * batch_record_limit. The total root count, final-batch width, and composed segment root are derived from record_count and the committed closure policy as specified in Section 4.4.

The deterministic-CBOR object is the authoritative segment artifact. A JSON projection, filename, and storage path are non-authoritative. 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 producer manifest schema violation and therefore a bundle-processing error without a verifier result under Section 6.5.1. After a valid parse, a numeric mismatch with the artifact is commitment_mismatch. A verifier MUST NOT trust a path or filename as the segment number.

A serialized authoritative segment artifact conforming to this document MUST use the application/vnd.vtl.segment+cbor media type.

4.6. Segment Chaining

Segment identity and chaining are defined within one stable ledger_id. They 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 authoritative segment artifact.
  • A producer MUST NOT reuse, skip, or wrap segment_number. Before serial exhaustion, it MUST stop the ledger and start a new ledger with a new ledger_id.
  • A closure-policy change MUST take effect only in a newly opened logical interval. It does not reset segment_number or ledger_id.

When a new 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 requirement. If durable sequence state cannot be recovered unambiguously, a producer MUST stop acceptance or begin a new ledger with a fresh identifier; it 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 authoritative 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 that omits 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, authoritative segment artifacts, producer manifests, verifier results, and timestamp artifacts in any suitable 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 verification bundle that claims a disclosure class under this document MUST disclose the authoritative segment artifact and a producer manifest.

5.1. Producer Manifest

The artifact's in-band commitment_profile_id is the cryptographically bound profile identifier. The producer manifest repeats the ledger identity, segment number, commitment profile, claimed disclosure class, artifact digests, and claimed producer timestamp state. It MUST NOT contain a segment-chain validation conclusion, verifier status, failure reasons, or an overall verification outcome.

Each artifacts.record_batches entry opens the aligned batch subtree identified by its batch_number. Each nested records entry represents one admitted record occurrence. Duplicate entries are permitted and MUST be counted separately. When occurrences have identical canonical-record bytes, their entries MAY reference the same disclosed artifact. A verifier MAY reuse a successful digest computation for identical bytes, but the resulting leaf hash MUST contribute once for each listed occurrence. Artifact-reference order does not assert admission order.

For a non-empty segment, a Class A manifest MUST open every batch exactly once. For an emitted empty segment, a Class A manifest MUST omit record_batches. A Class B manifest MUST open a non-empty proper subset of a non-empty segment's batches and therefore cannot claim an empty segment. A Class C manifest MUST omit record_batches. An opening MUST disclose exactly the number of record occurrences derived for that batch from record_count and batch_record_limit. After hashing and sorting those exact record octets, their VTLRoot value MUST equal the indexed batch_roots value.

Producer timestamp state is limited to present when the corresponding timestamp response is disclosed, pending when issuance was initiated but has not reached a terminal state, and unavailable when issuance terminated without a usable response. Producer manifests MUST NOT use verifier states such as verified or failed.

A pending or unavailable state MUST omit tsa_tsr. A present TSA state MUST reference tsa_tsr. When a disclosed response contains a nonce, the manifest MUST also reference tsa_req. Producer obligations for a nonce-bearing request are specified in Section 6.4.

A serialized producer manifest conforming to this document MUST use the application/vnd.vtl.manifest+json media type.

The producer manifest is the primary disclosure surface for verifier-visible producer claims. Its schema is defined in Appendix B. Deployment-specific data can be carried only in explicit extension containers and MUST preserve the semantics defined here. Extension data is opaque to baseline verification and does not select an additional evidence channel or affect the baseline outcome.

5.2. Verifier Result

Verification under Section 6.5 produces a logical verifier result. Only that verifier-authored result states the exercised scope, any segment-chain conclusion, TSA channel status, verifier-policy identifier, overall outcome, and failure reasons. The result reports conclusions needed by a relying party; local step-by-step diagnostics and execution traces 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 that claims conformance with this document MUST use the schema in Appendix B and the application/json media type. This document does not request a specialized media type for verifier results.

5.3. Portable Artifact References and Binding

Every path-bearing object consumed during verification, including a producer manifest artifact reference or an extension object, MUST satisfy the requirements in this subsection. Each path is a UTF-8 string interpreted relative to the disclosed evidence bundle root, using / as the only separator. A path MUST be non-empty; MUST NOT begin with /, \, or a drive-letter prefix; 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 that returns an object confined below the bundle root and prevents substitution of that object before it is consumed.

These are semantic requirements and do not depend on a regular-expression dialect or operating-system pathname behavior. Every artifact listed in the producer manifest MUST carry a lowercase hexadecimal SHA-256 digest entry.

After the primary-input boundary in Section 6.5.1 has been satisfied, an unsafe secondary reference is reported according to the role of the referenced object. A record or batch-opening artifact required for the claimed class or selected scope yields insufficient_disclosure. An unsafe referenced timestamp response yields TSA channel status missing and channel_failure. A disclosed predecessor that cannot be safely resolved yields chain_status equal to failed and segment_chain_mismatch. A reference that is safely opened but whose bytes fail the declared digest uses commitment_mismatch instead.

The producer manifest binds claim semantics to the authoritative segment artifact. The producer manifest's ledger_id, segment_number, and commitment_profile_id MUST match the corresponding fields in the authoritative segment artifact. An identity mismatch MUST be reported as commitment_mismatch. Segment-number comparison follows the rule in Section 4.5; profile selection follows Section 8.

6. Anchoring and Verification

6.1. Baseline Conformance

A baseline producer MUST emit the fixed commitment_profile_id defined in this document, construct authoritative segment artifacts and producer manifests under this profile, and initiate [RFC3161] timestamping for every emitted segment. Timestamp issuance MAY remain pending while incomplete.

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

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

6.2. TSA Channel Status Vocabulary

Verifier results MUST use the following status vocabulary for the TSA channel:

  • verified: timestamp validation succeeded for the disclosed artifact binding.
  • pending: timestamp issuance was initiated but its response is not yet available; this is not equivalent to invalid.
  • missing: an expected timestamp response is absent without a valid pending producer timestamp state.
  • failed: validation was attempted and did not succeed.

A verifier that determines that valid producer timestamp state identifies issuance as incomplete MAY report pending only when its identified verifier policy accepts two out-of-band inputs: an authenticated channel or signature establishing manifest provenance, and an authenticated creation or receipt time for the pending claim, obtained independently of the manifest, that falls within the policy-defined maximum age. This document defines no manifest field carrying either property. A verifier without both inputs MUST report missing. Producer timestamp state unavailable maps to verifier status missing. A baseline verifier always evaluates the TSA channel and uses only the four states defined above.

The outcome effects of missing and pending are defined in Section 6.1 and Section 6.5.6.

The optional TSA-result reason value is human-readable diagnostic text. This document defines issuance_pending as its only baseline machine-defined token. Any other value is free text and MUST NOT be interpreted as a portable machine-actionable code. A channel result whose status is pending, missing, or failed MUST include reason. A verified channel result MAY include it. Additional machine-readable details MAY be placed in the channel result's extensions member under keys defined by the applicable extension specification or deployment. They are opaque to baseline verification.

6.3. Anchoring Contract

The anchoring contract is simple: a producer computes the SHA-256 digest of the authoritative segment artifact and submits that digest using RFC 3161. Verifiers MUST recompute that digest over the exact artifact bytes. Timestamp validation and comparison of its message imprint with that digest do not depend on successful CBOR decoding of the artifact.

An RFC 3161 timestamp over the authoritative segment artifact digest binds the complete artifact bytes, including the in-band commitment_profile_id. It does not bind a disclosure class, producer manifest, verifier policy, or verifier result.

Consequently, a producer 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 authoritative artifact. Manifest digest checks establish consistency with the received manifest; they do not establish its provenance or that it names the artifact intended by the relying party. A relying party that needs those properties MUST obtain the authoritative artifact bytes or their expected digest independently through an authenticated mechanism and rerun verification from those bytes.

Baseline timestamping and success requirements are defined in Section 6.1. The producer MUST seal and durably write the authoritative segment artifact without waiting for timestamp issuance completion. Alternative timestamping or attestation mechanisms require a separate specification and do not alter this baseline contract.

6.4. RFC 3161 Timestamp Channel Profile

Baseline selection and implementation requirements for this channel are defined in Section 6.1.

When this channel is used, the producer MUST set TimeStampReq.messageImprint.hashAlgorithm to SHA-256 and its hashedMessage to the exact 32-octet result of SHA-256 over the authoritative segment artifact bytes. The producer MUST NOT hash that 32-octet result again. The producer MUST set TimeStampReq.certReq to TRUE.

The TimeStampReq, TimeStampResp, and timestamp token MUST conform to [RFC3161], as updated by [RFC5816] and restricted by this section. The token MUST contain exactly one CMS SignerInfo as defined by [RFC5652].

A successful TimeStampResp.status.status MUST be granted (0). A verifier MUST reject grantedWithMods (1) under this profile.

The SignerInfo signed attributes MUST contain exactly one SigningCertificateV2 attribute with exactly one attribute value, as defined by [RFC5035]. The first ESSCertIDv2.certHash MUST equal SHA-256 over the complete DER encoding of the selected signer certificate. If hashAlgorithm is present, it MUST identify SHA-256; omission denotes the SHA-256 default. If issuerSerial is present, it MUST identify that same certificate. The signed attributes MUST NOT contain a legacy SigningCertificate attribute.

Because this profile requires TimeStampReq.certReq=TRUE, SignedData.certificates MUST contain exactly one X.509 certificate that matches both SignerInfo.sid and the first ESSCertIDv2; zero or multiple matching certificates are a verification failure. The set MAY also contain unrelated certificates or certification-path certificates.

These requirements deliberately define a narrower profile than generic RFC 3161 interoperability. In particular, this profile rejects grantedWithMods, the legacy SigningCertificate attribute, multiple SignerInfo values, and signer-certificate arrangements or ESS certificate identifiers that do not satisfy the exact rules above. A conforming VTL implementation therefore cannot assume that an arbitrary deployed RFC 3161 TSA is compatible. Deployments SHOULD prequalify a TSA against this profile before relying on it for baseline timestamping.

A deployment using this channel MUST define accepted TSA trust anchors, accepted TSA policy identifiers, certificate-revocation policy, acceptable signature and certificate algorithms, and any additional constraints applied to the token's generation time. It MUST define a maximum acceptable future skew relative to the verifier-local time source identified by the verifier policy. That source MUST be available when the future-skew validation is performed. When operators control the TSA, responses SHOULD include necessary intermediates and SHOULD omit an already provisioned trust anchor. A verifier MUST NOT rewrite a received response or reject it merely because it contains additional nonmatching certificates.

A verifier evaluating disclosed timestamp artifacts MUST recompute the authoritative segment artifact digest and validate the timestamp response and token under [RFC3161], as updated by [RFC5816]. Validation MUST include a response status of granted (0) and required token presence; the CMS id-signedData content type and id-ct-TSTInfo encapsulated content type; exact SHA-256 message-imprint equality; the CMS signature and signed attributes; unambiguous signer-certificate binding; the signer certificate's single critical extended-key-usage extension containing only id-kp-timeStamping; certification-path validation under [RFC5280] using the token's TSTInfo.genTime as its date/time input and configured trust anchors; configured TSA policy and certificate-validation constraints; a genTime value within the selected signer certificate's validity interval; and the configured maximum future skew.

The deployment's certificate-revocation policy MUST define acceptable revocation evidence and the treatment of historical certificate status at TSTInfo.genTime. This profile does not define a long-term archival-PKI validation profile beyond those policy inputs.

The optional producer-manifest artifact artifacts.tsa_req carries the exact DER-encoded TimeStampReq as a non-authoritative supporting artifact. A producer that sends a request containing a nonce MUST retain those exact request bytes for at least as long as it retains the corresponding response bytes and MUST disclose the request with that response. The producer MUST NOT treat a response that omits or changes its request nonce as successful timestamp issuance.

When a disclosed response contains a nonce, the producer manifest MUST reference tsa_req. Whenever tsa_req is disclosed, the verifier MUST validate that reference's SHA-256 digest, require the request message-imprint algorithm to be SHA-256, require its hashedMessage to equal the recomputed authoritative segment artifact digest, and require certReq=TRUE. If either disclosed object contains a nonce, the request and response MUST both contain a nonce and the two values MUST be equal. A nonce-bearing response without tsa_req, a disclosed nonce present in only one object, unequal disclosed nonces, or any failure of those checks is a timestamp-channel verification failure reported with channel_failure. A nonce-free disclosed request is validated under the same non-nonce rules.

If the response contains no nonce and tsa_req is not disclosed, a verifier cannot determine whether the original request contained a nonce. The verifier can validate the signed response and its artifact-digest binding, but the result makes no claim of request-response nonce correlation. The producer obligations above remain applicable and cannot be inferred from the baseline disclosure in this case.

Any failure of the RFC 3161 validations above is a timestamp-channel verification failure. The corresponding TSA channel status in a verifier result MUST be verified on success and failed on failure.

6.5. Verification

6.5.1. Inputs and Scope

The verifier-result contract applies only after the verifier has obtained the exact producer-manifest bytes, parsed them sufficiently to determine every mandatory result field and the exact artifacts.segment_cbor reference, and obtained the exact referenced segment-artifact bytes. Failure to obtain either primary input; malformed or structurally invalid producer-manifest bytes; a missing primary artifact reference; or unsafe resolution of either primary input is a bundle-processing error. A verifier MUST reject that bundle-processing error but MUST NOT emit a verifier result for it. Such an error is outside the verifier-result schema and is not an insufficient_disclosure conclusion.

Structurally invalid producer-manifest bytes include violations of the JSON, CDDL, lexical, and manifest-only cross-field requirements that can be evaluated before authoritative artifact verification. In particular, present without tsa_tsr, pending or unavailable with tsa_tsr, and a missing, negative, non-integer, non-shortest, or uint64-overflowing manifest segment_number are bundle-processing errors for which no verifier result is emitted. A deployment-configured resource limit that prevents the verifier from reaching the invocation boundary above likewise produces a bundle-processing error without a verifier result. Once that boundary has been satisfied, a rejection solely because a syntactically and structurally valid input exceeds an identified verifier-policy limit uses verifier_policy_rejection as specified in Section 12, Paragraph 10.

Verifiers MUST first decode the authoritative segment artifact and determine the applicable commitment_profile_id from its in-band field. Dispatch precedence is fixed: a missing or non-text identifier yields invalid_segment_artifact; a text value that is a well-formed lowercase RFC 9562 UUID but is not supported yields unsupported_commitment_profile; and the UUID defined by this document selects the commitment profile defined in Sections 4.1 through 4.6, with structural CDDL in Appendix C. An unsupported UUID MUST NOT be classified as invalid_segment_artifact solely because it differs from this document's UUID. When the field can be decoded as a text string, the verifier result's optional commitment_profile_id MUST echo its exact artifact value, including when that value is unsupported. The producer manifest's ledger_id, segment_number, and commitment_profile_id MUST exactly match the corresponding artifact fields whenever those fields can be decoded. Segment-number comparison follows the rule in Section 4.5.

The verifier MUST treat commitment_profile_id as the semantic key for interpreting the authoritative segment artifact. A structurally well-formed authoritative segment artifact and successful timestamp validation are insufficient for semantic verification if the applicable commitment_profile_id is absent or unsupported. A producer manifest identity mismatch is a verification failure reported as commitment_mismatch even when the artifact and timestamp are otherwise valid.

The producer manifest's disclosure_class is the producer-claimed class. The result's claimed_disclosure_class MUST repeat that value. The result's verification_scope identifies the scope selected for the verification invocation: public_recompute corresponds to Class A, partial_verification to Class B, and anchor_only to Class C. A scope is weaker in the order A, B, C. After satisfying the primary input boundary, the invocation MUST select an intended scope; selection does not assert support. The verifier determines support after structural artifact decoding and claimed-class shape validation but before scope-specific validation. Earlier fail-fast termination still reports the selected scope without claiming it succeeded. If the verifier intentionally exercises a weaker scope than the inputs support and no verifier policy requires the stronger scope, the overall result MUST be partial. If applicable verifier policy requires a stronger supported scope, the overall result MUST be failure with scope_not_exercised. If the disclosed inputs are genuinely inadequate for the claimed class or asserted scope, the result instead uses insufficient_disclosure. Exercising a stronger scope does not rewrite the producer-claimed class.

The claimed class determines manifest shape; the selected scope determines which valid disclosed artifacts are consumed and which recomputation claims are made. Scope support is determined from the decoded artifact, disclosures, claimed class, and verifier policy. Inputs support a scope when they contain the manifest structure and artifacts required to attempt it; support does not assert that the scope's validations would succeed. Results MUST NOT claim assurances outside the exercised scope.

The result's manifest_sha256 MUST equal SHA-256 over the exact UTF-8 octets of the producer manifest that was validated, under the exact-octet rule in Section 4.2. This field identifies the validated input; it does not authenticate the verifier result.

The result's artifact_sha256 MUST equal SHA-256 over the exact referenced bytes of artifacts.segment_cbor, under the exact-octet rule in Section 4.2. It MUST be present even when those bytes are malformed or cannot be decoded. This result field identifies the authoritative artifact input and is distinct from the producer manifest's artifact-reference digest claim.

When the exact primary artifact bytes are present but do not decode as a valid authoritative segment artifact under the applicable artifact syntax and cross-field rules, the verifier result MUST be failure and include invalid_segment_artifact. The result's verification_scope records the scope selected for the invocation; it does not claim that that scope succeeded. The verifier MUST evaluate the TSA channel independently over the exact artifact bytes and report it as verified, failed, or missing as applicable. Artifact decoding failure alone MUST NOT cause TSA channel status failed or channel_failure.

The verifier MUST always compute SHA-256 over the exact referenced bytes of artifacts.segment_cbor and compare it with artifacts.segment_cbor.sha256. That value checks producer manifest self-consistency only; it is not an external timestamp binding and MUST NOT be counted as a timestamp-channel binding.

For every producer manifest artifact reference consumed during verification, the verifier MUST compute SHA-256 over the exact referenced file bytes and compare it with the reference's sha256 value. A mismatch is a verification failure. A verifier MAY reuse one successful file-digest computation for repeated references to the same bytes. Duplicate record occurrences nevertheless contribute once per listed occurrence to record-level and multiset computations.

6.5.2. Verification Procedure

Verifiers SHOULD apply the following validation steps in fail-fast order, subject to the exercised verification scope:

  1. Validate manifest shape against the producer-claimed disclosure class. For non-empty Class A, record_batches opens every authoritative batch; for Class B, it opens a non-empty proper subset of the authoritative batches; and for Class C, it is absent. Apply the empty-segment exceptions in Section 7. These claimed-class requirements do not determine which valid openings a weaker selected scope consumes.
  2. Validate the authoritative segment artifact and the producer manifest, including their repeated identity fields. An artifact that is malformed or fails artifact syntax or cross-field validation yields invalid_segment_artifact; a repeated identity mismatch yields commitment_mismatch. Always validate artifacts.segment_cbor.sha256, and validate the digest of every other producer manifest artifact reference consumed during the remaining steps. Do not count the producer manifest digest as an external timestamp binding.
  3. For an epoch segment, validate serial zero and the zero predecessor value and set chain_status to epoch. For a non-epoch segment whose predecessor is disclosed, validate ledger identity, exact commitment-profile identity, contiguous serials, and prev_segment_sha256; set chain_status to validated on success or failed on failure. When the predecessor is unavailable, set chain_status to predecessor_not_disclosed.
  4. For every scope, validate record_count, the power-of-two batch limit, batch-root cardinality, and composition of batch_roots into segment_root as specified in Section 6.5.3.
  5. For public_recompute over a non-empty segment, validate every disclosed batch opening and every canonical-record CBOR artifact under Section 6.5.4, hash exact octets under Section 4.2, compare every recomputed batch root with the authoritative indexed root, and independently recompute segment_root from the complete sorted leaf list. For an empty segment, require record_batches to be absent, record_count and the complete leaf count to be zero, batch_roots to be empty, and segment_root to equal SHA-256 over zero octets. Do not reconstruct a record from source telemetry.
  6. For partial_verification, reject an empty or single-batch segment. Consume a non-empty proper subset of authoritative batches: selected complete openings from a valid Class A manifest, or the disclosed openings of a valid Class B manifest. Validate each consumed record, complete opening, indexed root, and adjacent consumed boundary under Section 6.5.4 and Section 6.5.3. Unconsumed openings make no record-level or batch-opening claim and their record artifacts need not be fetched or digested. Other withheld-batch validation is deployment-specific and MAY appear in extensions.
  7. For anchor_only, do not consume record_batches, regardless of whether valid claimed-class shape requires them to be present, and do not claim record-level or batch-opening recomputation.
  8. Compare the recomputed authoritative segment artifact digest with the timestamp response's messageImprint.hashedMessage, whether or not artifact decoding succeeded. A mismatch is a timestamp-channel verification failure. A policy-accepted pending producer timestamp state can lack the external binding and yields partial. The producer-timestamp-state and tsa_tsr cross-field violations identified in Section 6.5.1 are rejected at the primary-input boundary without a verifier result.
  9. Validate the [RFC3161] timestamp response and report its status in the TSA channel result. A referenced response that is unavailable is missing; a response 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 disclosure 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, 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 separate empty-root rule in Section 4.3 applies;
  • for claimed-class shape, batch-opening numbers are unique and in range; non-empty Class A opens every batch, Class B opens a non-empty proper subset, and Class C and empty Class A omit record_batches;
  • for public_recompute, every disclosed opening is consumed, contains exactly its derived number of record occurrences, and has sorted recomputed leaves that reduce to the indexed batch root;
  • for partial_verification, every opening selected for consumption is complete and has sorted recomputed leaves that reduce to the indexed batch root; the selected batch numbers form a non-empty proper subset of the authoritative batches;
  • 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; and
  • for public_recompute, the globally sorted multiset of every disclosed occurrence reduces independently to segment_root.

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

6.5.4. Disclosed Canonical-Record Validation

A verifier exercising public_recompute or partial_verification MUST validate every record artifact consumed under the exercised scope as one exact canonical-record byte string before using it as a leaf preimage. 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. The recursively restricted payload shape is part of this validation.

The verifier MUST hash the exact validated octets under Section 4.2. If consumed record bytes violate any canonical-record rule, the overall result MUST be failure with invalid_canonical_record, even when those bytes hash to the leaf value committed by the producer. If a separately observed record digest, batch root, segment root, artifact digest, or binding also mismatches, the result MUST additionally include commitment_mismatch. Each category occurs at most once.

A baseline verifier exercising public_recompute or partial_verification MUST accept every conforming disclosed canonical-record artifact consumed under the exercised scope whose total encoded length is no greater than 4096 octets. It MAY accept larger records under identified verifier policy and configured resource limits.

6.5.5. Failure Reasons

A verifier result whose overall value is failure MUST contain one or more machine-usable failure_reasons. This profile defines the following baseline categories:

unsupported_commitment_profile
The in-band profile identifier is present but unsupported; this conclusion makes the overall result failure.
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 authoritative segment-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, artifact digest, or consumed digest binding, does not validate.
segment_chain_mismatch
A disclosed predecessor cannot be safely resolved or fails ledger identity, commitment-profile identity, serial-continuity, or predecessor-digest validation.
channel_failure
The timestamp response is missing, invalid, or otherwise unacceptable, or its binding to the authoritative segment artifact digest fails.
insufficient_disclosure
The disclosed artifacts do not support the producer-claimed disclosure class or the asserted verification scope.
scope_not_exercised
The disclosed inputs support a stronger scope required by identified verifier policy, but the verifier intentionally exercised a weaker scope.
verifier_policy_rejection
The inputs do not satisfy an applicable local verifier policy for a reason not more specifically represented above.

An absent referenced timestamp response is reported with TSA channel status missing and channel_failure. An absent batch opening or canonical-record artifact required by the claimed disclosure class is insufficient_disclosure. A missing primary segment artifact is a bundle-processing error under Section 6.5.1. If referenced bytes are present but fail their declared digest or commitment check, the reason is commitment_mismatch.

A conforming result MUST use only those baseline categories in failure_reasons. Deployment-specific detail belongs in the result's extensions member; this document creates no extension namespace for failure-reason values. Failure reasons are conclusions, not a trace of internal verifier steps. A category MUST NOT occur more than once in one result. Failure reasons MUST be absent when overall is success or partial; TSA channel status explains a timestamp-pending partial outcome.

Verifier output states the claimed disclosure class, the verification scope selected for the invocation, any segment-chain conclusion, TSA channel status, failure reasons when applicable, and whether the resulting claim is public recompute, partial verification, or anchor-only evidence.

6.5.6. Overall Outcome

Overall outcome is determined after checking whether the disclosed artifacts satisfy the producer-claimed disclosure class, applying the validations required by the scope actually exercised and by verifier policy, and determining TSA channel status. A chain_status of predecessor_not_disclosed does not by itself degrade the outcome; it makes no chain-adjacency claim. A chain_status of failed makes the overall outcome failure.

The following decision table is normative. A verifier MUST evaluate every applicable row; failure takes precedence over partial, and partial takes precedence over success. Extension data is not a channel under this profile and MUST NOT affect this evaluation.

Table 1: Overall Outcome Decision Table
Condition Additional condition Overall Failure reason
A required structural, commitment, chain, disclosure, artifact-reference, or external-binding validation failed. Any failure The applicable baseline category
The verifier intentionally exercised a weaker supported scope. The stronger scope is required. failure scope_not_exercised
The verifier intentionally exercised a weaker supported scope. The stronger scope is not required. partial None
The baseline TSA channel is failed or missing. Any failure channel_failure
The baseline TSA channel is validly pending. Every other applicable requirement succeeded. partial None
The TSA channel is verified and every other required validation succeeded. All applicable requirements succeeded. success None

A manifest claim of present without the required tsa_tsr reference, or of pending or unavailable with a tsa_tsr reference, is structurally invalid and is rejected at the primary-input boundary without a verifier result. If a required tsa_tsr reference is present but cannot be safely resolved, the TSA channel status is missing and the overall outcome is failure with channel_failure.

Every verifier result MUST include a non-empty verifier_policy_id that stably identifies the complete policy applied to the invocation. That policy includes RFC 3161 trust anchors, accepted TSA policy identifiers, revocation policy, algorithm constraints, generation-time constraints, future-skew constraints, and the rules for accepting a producer pending claim. A result MAY include verifier_policy_sha256 only when the corresponding policy artifact is available to the verifier or otherwise discoverable. The digest MUST be SHA-256 over the exact policy-artifact bytes. A bare policy digest is not sufficient policy identification.

Verifier output MUST NOT be represented as proving more than the exercised verification scope. The scope caveat in Section 7 applies to every result.

6.5.7. Chain-Claim Composition

A producer manifest and its corresponding verifier result cover one authoritative segment artifact. A relying party claiming continuous chaining across a disclosed range MUST have one validated producer-manifest/verifier-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 equal to validated after validation against its disclosed predecessor. Every later result MUST have chain_status equal to validated. A gap, identity mismatch, failed link, or unavailable required predecessor prevents a continuous-range claim. Successful composition establishes continuity only across that disclosed range; it does not establish global dataset completeness or the absence of undisclosed ledgers.

7. Disclosure Classes

Verification claims depend on what artifacts are disclosed. This profile defines three disclosure classes, whose manifest-shape rules are normative in Section 5.1.

The producer claims one disclosure class in the producer manifest. The verifier independently reports the scope selected for its invocation as specified in Section 6.5.1. A verifier claim MUST be limited to the disclosed artifacts and validation scope actually exercised.

An emitted empty segment MAY be disclosed as Class A or Class C and MUST NOT be disclosed as Class B. Both permitted classes omit record_batches; the claimed class and exercised scope distinguish an empty public recomputation from anchor-only verification.

Every producer manifest MUST contain the tsa member in anchoring, subject to the state-to-reference rules in Section 5.1.

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

8. Versioning

Document revisions are editorial and do not alter commitment output by themselves. Schema version fields identify individual object shapes; neither a document revision nor a schema version substitutes for the commitment_profile_id defined in Section 2.

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. Their local version fields are fixed at 1, identify object syntax, and do not select commitment rules. A verifier that encounters an unsupported well-formed UUID in commitment_profile_id MUST report unsupported_commitment_profile rather than applying fallback semantics.

This document defines exactly one commitment_profile_id and requests no IANA registry for it; see Section 11. The profile UUID assigned in Section 2 is a randomly generated UUIDv4 under [RFC9562] and is bound exclusively to the commitment profile specified by this document. Baseline producers MUST emit exactly that UUID. Verifiers MUST apply dispatch in this order: a missing or non-text identifier is an invalid_segment_artifact; a well-formed lowercase RFC 9562 UUID not supported by the verifier is an unsupported_commitment_profile; and exact string equality with this document's UUID selects the commitment profile defined in Sections 4.1 through 4.6, with structural CDDL in Appendix C. Implementations MUST NOT infer profile semantics from a schema version, media type, filename, producer manifest, informal profile name, or identifier parsing. This document defines no general or private-use namespace for additional values. Reusing the UUID for different rules is nonconformant, MUST NOT be done, and creates ambiguous verifier semantics even though the identifier remains bound into the authoritative segment artifact digest. A verifier configured, through its identified verifier policy, with knowledge that this UUID has been bound to rules other than those defined by this document MUST NOT verify artifacts carrying it under this profile and MUST report verifier_policy_rejection. This document defines no in-band signal for such a conflict; detection is an out-of-band policy input.

Adding, removing, or changing external timestamping or attestation evidence without changing canonical-record bytes, commitment calculations, or authoritative segment artifact bytes does not require a new commitment_profile_id. Such evidence is outside the baseline unless a separate specification defines it.

A future commitment profile MUST use a distinct UUID assigned and published by the specification that defines that profile and MUST begin a new ledger epoch. Under this decentralized model, neither this document nor IANA assigns future values; generating a fresh UUID under [RFC9562] provides the required collision resistance. The first artifact under that profile MUST use a newly generated ledger_id, segment_number equal to zero, and the all-zero predecessor value. A producer MUST NOT create a predecessor link across commitment profiles. Existing sealed artifacts retain their original identifier and semantics. Changing the UUID in a sealed artifact changes its exact bytes and digest and would force corresponding predecessor-link changes through the chain of successor artifacts. Such artifacts therefore MUST NOT be renamed or reinterpreted in place.

A deployment that introduces a future commitment profile SHOULD use a backward-compatible rollout that preserves verification support for artifacts sealed under this profile. A rollback to an earlier profile is another profile transition and MUST also begin a new ledger epoch. A rollback affects only subsequently formed artifacts; it MUST NOT relabel or re-encode sealed artifacts, reuse a ledger identifier or segment number, or create a cross-profile predecessor link.

This rule makes a hash, canonicalization, or tree-profile migration an explicit epoch boundary rather than an in-place algorithm upgrade. A baseline chain claim cannot span that boundary. Deployments that need to associate the old and new epochs require a separately authenticated migration statement or operational record, and verifiers need support for each profile whose historical artifacts they continue to accept. Such a migration statement is not a VTL commitment input and does not turn the two epochs into one baseline chain.

9. Conformance Vectors

This section is informative; normative requirements are in Sections 4 through 8, and the known-answer vectors in Appendix A are normative.

Vector suites begin with exact canonical-record byte strings. Recommended positive coverage includes the normative integer, floating-point, and signed-zero cases in Appendix A; a conforming 4096-octet record; an epoch and linked successor; empty intervals in both modes, including lifecycle exceptions; single, odd, power-of-two, and duplicate leaf counts; boundary assignment; policy transitions and close-reason precedence; restart recovery; profile migration; multi-batch composition; complete Class A and proper-subset Class B openings; every outcome-table row; deliberate down-scoping of multi-batch Class A to partial_verification or anchor_only; Class B to anchor_only; unsupported-profile reporting; malformed artifact bytes with independently successful timestamp validation; and the RFC 3161 nonce, certificate-selection, and generation-time cases in Section 6.4.

Recommended negative coverage includes malformed manifest segment numbers rejected without a verifier result; numeric identity mismatch reported as commitment_mismatch; every canonical-record violation; serial and predecessor faults; incorrect boundary assignment; invalid empty-mode and policy transitions; tree and batch faults; malformed openings; Class B over an empty or single-batch segment; partial_verification over either such segment, reported as insufficient_disclosure; invalid TSA state/reference combinations rejected without a result; missing policy identity or TSA channel; unsafe references; malformed artifacts that incorrectly force successful independent timestamp validation to be reported as failed; unsupported UUIDs misclassified as malformed; unacceptable pending claims; a timestamp token over segment_root; double-hashed imprints; and every RFC 3161 status, nonce, token, attribute, and certificate violation, including grantedWithMods.

General close-reason accuracy is a producer-side test requiring a controlled elapsed-time source and injected closure conditions. Recommended cross-implementation checks use a fake elapsed-time source and injected persistence failures, verify byte-for-byte parity across at least two independent implementations, and carry the applicable commitment_profile_id.

10. Operational Considerations

This section follows [RFC5706]. Deployments SHOULD expose admission, sealing, timestamp submission, timestamp issuance completion, retention, and verification as distinct states so delayed issuance is not confused with a malformed artifact.

Operators SHOULD monitor canonical-record handoff, validation, admission and durability failures; ledger identity, sequence state, fencing, and serial conflicts; elapsed-timer health, closure delay, reason, and policy revision; encoding and size rejection; artifact writes, digests, and roots; RFC 3161 backlog, reachability, delay, and per-segment status; and storage pressure. Signals SHOULD be structured and machine-readable with stable event codes and sufficient correlation data. Uncertain handoff, durability, sequence, or timer continuity SHOULD be reported explicitly and does not become a baseline verifier-result field.

Deployments SHOULD document the supported record size, admission backpressure, durable-write behavior, closure policy, retention, accepted TSA identities and policies, generation-time and skew constraints, and verifier policy. Effective defaults and overrides SHOULD be visible. Configuration rollback MUST follow the interval transition rules and MUST NOT mutate sealed artifacts, reuse a serial, or conceal a chain discontinuity.

For N records and batch limit B, the artifact carries ceil(N/B) roots. Each 32-octet root requires 34 CBOR octets before array overhead; 100000 records with B = 1024 produce 98 roots and approximately 3332 octets before array and fixed-field overhead. Larger batches reduce artifact size but enlarge the minimum Class B disclosure unit. RFC 3161 request rate is one per emitted segment, so interval_ms and empty_mode directly determine TSA load.

Operators SHOULD periodically verify a recent representative bundle and alert on failures, timestamp issuance pending beyond policy, and admission, timer, serial, fencing, or artifact-write faults. A policy-accepted pending claim yields TSA channel status pending and overall partial; a rejected pending claim, unavailable producer timestamp state, or unavailable referenced response yields missing, channel_failure, and overall failure.

Before first admission, an operator SHOULD durably provision a fresh ledger_id, validate closure policy, 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.

11. IANA Considerations

This document has no IANA actions.

The application/vnd.vtl.segment+cbor and application/vnd.vtl.manifest+json vendor-tree media types used by this document were submitted separately under the procedures of [RFC6838] and are currently under Expert Review. Their registrations are not actions requested as part of publishing this document; the normative references to those types in Sections 4.5 and 5.1 depend on the separate registrations completing. No specialized media type is requested for verifier results, which use application/json.

This document 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 does not require central allocation. The absence of a registry does not provide discovery of conflicting third-party semantic bindings; that residual issue is addressed in Section 12, Paragraph 5. A future registry requires a separate specification defining its registration policy. This explicit no-action statement 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-artifact handling. Its tamper-evidence properties assume the collision and second-preimage resistance of SHA-256 and the soundness of the RFC 9162 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.

Bundle paths and object authentication. 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; a pathname check followed by an independent open is unsafe. A timestamp over the artifact authenticates neither the manifest nor the result.

Sequence rollback, fork, and truncation. 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. In the terminology of [RFC9416], ledger_id is a dynamically selected identifier that must remain unique while evidence can be compared. Operators SHOULD audit its generation and alert on reuse of a ledger or ledger/serial pair.

Producer compromise and upstream substitution. A compromised producer or handoff can fabricate, suppress, substitute, or misassign byte strings. VTL makes outputs tamper-evident only after commitment. Upstream transport, replay, credentials, decryption, mapping, and parsing are outside this document; 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.

Canonicalization, profile, and metadata confusion. 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 authoritative artifact, its in-band profile binding, and identity comparison mitigate these attacks. With no profile registry, this document provides no discovery mechanism for a third party assigning different rules to the same UUID. Verifiers obtain mappings from identified policy, and deployments accepting unoperated producers SHOULD pin the expected identifier.

Artifact mutation and root transplantation. Independent artifact-digest recomputation detects mutation or substitution. 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.

Timestamp withholding, downgrade, and meaning. An attacker can delay or withhold timestamp responses, present pending placeholders as final, or exploit ignored missing responses. The status vocabulary and outcome table constrain these cases. 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. Revocation policy SHOULD define later-compromise reevaluation and historical validation across key rotation. Validation proves only that bound bytes existed no later than the asserted time under configured policy; it proves neither producer or source identity nor when the segment opened.

When a nonce-free response is disclosed without tsa_req, a verifier cannot determine whether the original request contained a nonce. Baseline success then validates the signed response and artifact-digest binding but provides no request-response nonce-correlation assurance and does not establish producer compliance with request-side nonce obligations.

Elapsed-time and policy manipulation. An attacker can alter the interval, elapsed-time source, policy, close reason, or boundary processing. Committing the snapshot and reason makes the claim tamper-evident after sealing, but does not prove honest elapsed time. False upstream UTC values likewise remain false admitted values; UTC changes MUST NOT affect membership or reopen a sealed segment.

Resource exhaustion. Producers need input-size limits, rate controls, and backpressure. Before allocating or recursing, verifiers MUST enforce configured limits on lengths, depth, collection sizes, records, batches, DER objects, certificates, and total work using checked arithmetic. A limit preventing acquisition or sufficient parsing before the invocation boundary produces no result; afterward, rejection of an otherwise valid input solely for a policy limit MUST report verifier_policy_rejection with policy identity. This profile is not a complete denial-of-service defense.

Scope, completeness, and disclosure. Success can be misread as completeness, physical truth, or authorization. Results state scope, but stronger claims require external controls. Class B establishes sorting only within consumed openings and across consumed adjacent boundaries; anchor-only establishes none. 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 public_recompute or partial_verification that consumes that record.

Guessing, correlation, and metadata. Leaf hashes are unsalted. Disclosed low-entropy material and any future individual leaf hashes permit offline guessing. 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; openings on both sides bound the intervening range. 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, timestamp artifacts, and summaries reveal counts, layout, outage windows, delay, intervals, close reasons, and emission cadence. Repeated pseudonymous identifiers can link records and enable re-identification. Operators SHOULD minimize personal data, separate identity metadata where possible, 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.

Future extension points. 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]; RFC 4998 objects are not interchangeable with VTL batch roots. Any change to commitment bytes or calculations requires a distinct commitment_profile_id.

13. Normative References

[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>.
[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>.
[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>.
[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>.
[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>.
[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>.
[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>.
[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>.
[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>.

14. Informative References

[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>.
[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>.
[RFC9416]
Gont, F. and I. Arce, "Security Considerations for Transient Numeric Identifiers Employed in Network Protocols", BCP 72, RFC 9416, DOI 10.17487/RFC9416, , <https://www.rfc-editor.org/info/rfc9416>.
[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>.
[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>.
[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>.
[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>.

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 using RFC 3161 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 Concise Data Definition Language (CDDL) ([RFC8610]) shape for the producer manifest and the separate verifier result. It captures the verification surface described here and provides explicit containers for deployment-specific additions. Producer manifests and verifier results MUST be UTF-8 JSON texts under [RFC8259]. Parsers MUST reject duplicate object member names; generators MUST NOT emit them.

The producer manifest and verifier result each carry version equal to 1. Neither value is independently negotiated and neither is part of the schema rule name.

Baseline failure reasons are defined in Section 6.5.5. Extension member keys and their semantics are defined by the applicable extension specification or deployment and are opaque to baseline verification.

The producer manifest's commitment_profile_id is a generic tstr so that a manifest can repeat a future or unsupported artifact value. It MUST exactly equal the authoritative artifact's in-band text value. Baseline producers conforming to this document MUST emit the UUID defined here; the generic structural type does not relax that baseline requirement.

The verifier result's commitment_profile_id is optional because a malformed authoritative segment artifact might not be decodable far enough to recover it. When the artifact field is decodable as a text string, the result MUST include and exactly echo that value. It is a generic tstr so that a result can report unsupported_commitment_profile without replacing the unsupported artifact value with this document's UUID.

chain_status MUST be present whenever the authoritative segment artifact can be decoded far enough to make one of the chain conclusions defined here. It MUST be absent otherwise, and absence makes no chain claim. Its value is epoch only after the epoch segment's serial and zero predecessor have been validated; validated only after a disclosed non-epoch predecessor and its linkage have been validated; predecessor_not_disclosed when that artifact is unavailable; and failed when an attempted chain validation fails. failure_reasons MUST be present and non-empty exactly when overall is failure. The sole TSA channel result is tsa, whose validation profile is defined by Section 6.4.

The relative-path rule below is intentionally structural. RFC 8610 regular expressions use the XML Schema dialect and cannot portably express all cross-platform path and filesystem-containment checks. Implementations MUST apply the semantic path rules in Section 5.3 to every consumed path- bearing object, including extensions. Those rules include backslash, drive-prefix, colon, control- character, dot-component, containment, and safe-resolver checks.

segment_cbor is universally required. For a non-empty segment, a Class A producer manifest MUST contain openings for every batch in record_batches; for an empty segment it MUST omit that member. Class B MUST contain a non-empty proper subset and is invalid for an empty segment. Class C MUST omit record_batches. JSON projections, standalone digest files, TSA-info projections, and operational summaries are not baseline producer manifest fields.

For a producer manifest claiming Class A, B, or C, anchoring MUST contain exactly tsa. A baseline producer manifest therefore identifies producer timestamp state for every emitted segment.

A tsa state of pending or unavailable MUST omit tsa_tsr. A state of present requires tsa_tsr. Violating either direction of the state/tsa_tsr rule makes the producer manifest structurally invalid. Whether a response contains a nonce is learned only by inspecting the timestamp response: a nonce-bearing response without tsa_req is therefore a timestamp-channel verification failure, not a manifest-structure error. A producer that used a nonce-bearing request has the additional retention, disclosure, and response-acceptance obligations in Section 6.4. When a required reference is present but its bytes cannot be safely obtained, the verifier reports missing as specified in Section 6.5.6.

The channels map MUST contain exactly the tsa key. A channel reason is human-readable diagnostic text. issuance_pending is the only baseline machine-defined reason token; every other reason string is free text and is not a portable machine-actionable code. A reason member is required when the TSA channel status is pending, missing, or failed, and optional when it is verified. Extension containers are opaque and MUST NOT add channel keys or alter a baseline status or overall outcome.

segment_number is a JSON string containing the shortest unsigned base-10 representation of a uint64: 0, or a digit from 1 through 9 followed by zero or more digits. Its numeric value MUST NOT exceed 18446744073709551615. This avoids loss of precision in JSON implementations whose exact integer range ends at 2^53-1. The lexical rules for relative-path, uint64-decimal, json-hex32, and json-hex64 are normative prose constraints in addition to the structural CDDL. json-hex32 is exactly 32 lowercase hexadecimal characters, and json-hex64 is exactly 64 lowercase hexadecimal characters. Prose also governs finite-float validation, deterministic CBOR encoding, text-only CBOR map keys, class-to-artifact requirements, producer timestamp state-to-artifact dependencies, extension names, and all other cross-field requirements that this structural CDDL cannot express.

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 = {
  "version": 1,
  "artifact_sha256": json-hex64,
  "manifest_sha256": json-hex64,
  ? "commitment_profile_id": tstr,
  "claimed_disclosure_class": disclosure-class,
  "verification_scope":
    "public_recompute" / "partial_verification" / "anchor_only",
  ? "chain_status": chain-status,
  "channels": channels,
  "verifier_policy_id": non-empty-tstr,
  ? "verifier_policy_sha256": json-hex64,
  "overall": "success" / "partial" / "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",
    ? "reason": tstr,
    ? "extensions": { * tstr => json-data }
  } /
  {
    "status": "pending" / "missing" / "failed",
    "reason": 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 structural shape.

The tstr shape for commitment_profile_id does not relax the baseline producer requirement: a baseline producer MUST emit the profile UUID defined in Section 2. The generic structural type permits a verifier to distinguish a well-formed but unsupported UUID as specified in Section 8.

The cbor-hex32 rule is distinct from the JSON rule in Appendix B and is exactly 32 lowercase hexadecimal characters. Commitment digests and Merkle roots are 32-octet byte strings in the authoritative CBOR artifact. CDDL cannot express the power-of-two restriction or the upper bound of 2^63 on batch_record_limit; both normative 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
Morocco