| Internet-Draft | VTL | August 2026 |
| El Khatabi | Expires 27 February 2027 | [Page] |
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.¶
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.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.¶
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:¶
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.¶
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
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.¶
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.¶
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:¶
commitment_profile_id): The opaque text value that selects exactly one commitment profile. This document defines one profile, informally named "VTL Canonical CBOR", and assigns it the UUIDv4 c08ade4e-1785-4eb6-9648-b7003d76288d under [RFC9562]. The identifier uses the RFC 9562 lowercase hex-and-dash representation; the informal name is descriptive and is not an identifier. Normative identifier handling is specified in Section 8.¶
ledger_id generated so that collisions are negligible, rendered as 32 lowercase hexadecimal characters. Segment serial numbers are scoped to this identifier.¶
ledger_id. The epoch segment is segment zero and carries the zero predecessor value.¶
empty_mode, a segment can be empty. Segment membership is selected by the admission linearization point and a non-decreasing elapsed-time close rule, not by record timestamps.¶
artifacts.segment_cbor references the exact bytes of this object and does not name a separate protocol object. Its storage path and filename are not protocol fields.¶
batch_record_limit. Its root is an aligned subtree of the segment Merkle tree and is commitment material, not a storage-layout convention.¶
TimeStampResp: The [RFC3161] protocol response containing status information and, on success, a timestamp token. This document also calls it the timestamp response.¶
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.¶
The roles below participate at different sides of the profile boundary. Baseline producer and verifier conformance is defined in Section 6.1.¶
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.¶
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.¶
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.¶
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.¶
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:¶
T belongs to the current logical interval.¶
T or later, the producer MUST process the boundary before another acceptance can linearize.¶
T, but if canonical-record admission linearizes at or after T, the record belongs to the next logical interval.¶
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:¶
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.¶
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.¶
Segment identity and chaining are defined within one stable
ledger_id. They do not depend on UTC dates or elapsed idle
time.¶
segment_number equal to 0 and prev_segment_sha256 equal to a 32-octet all-zero byte string.¶
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.¶
segment_number. Before serial exhaustion, it MUST stop the ledger and start a new ledger with a new ledger_id.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
Verifiers SHOULD apply the following validation steps in fail-fast order, subject to the exercised verification scope:¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
missing; a response whose validation is attempted and does not succeed is failed.¶
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;¶
batch_roots values equals the value derived from record_count, including the empty-segment case;¶
record_count is zero, closure_policy.empty_mode is emit, or close_reason is shutdown or recovery;¶
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;¶
record_batches;¶
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;¶
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;¶
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¶
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.¶
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.¶
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_profilefailure.¶
invalid_canonical_recordinvalid_segment_artifactcommitment_mismatchsegment_chain_mismatchchannel_failureinsufficient_disclosurescope_not_exercisedverifier_policy_rejection
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.¶
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.¶
| 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.¶
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.¶
Verification claims depend on what artifacts are disclosed. This profile defines three disclosure classes, whose manifest-shape rules are normative in Section 5.1.¶
present. An empty Class A bundle omits record_batches and its public recomputation validates the empty construction in Section 4.3. A public-recompute claim is invalid if required record artifacts are missing, root recomputation was not performed, the in-band profile is absent or unsupported, or a required digest validation fails.¶
batch_roots list into segment_root. A verifier exercising partial_verification MUST recompute each consumed batch root from its disclosed record occurrences and makes no claim about unopened batch preimages. Empty and single-batch segments cannot support Class B.¶
record_batches, and MUST NOT claim record-level reproducibility. A verifier exercising anchor_only can validate artifact digest binding, producer-manifest digests, chain existence, and TSA channel status. For a non-epoch segment whose predecessor artifact is not disclosed, it MUST report chain_status as predecessor_not_disclosed rather than validated.¶
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.¶
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.¶
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.¶
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.¶
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].¶
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.¶
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.¶
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 }
¶
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
¶
The author thanks Joe Clarke for OPSDIR review feedback that improved the operational considerations and deployment guidance.¶