| Internet-Draft | VTL | September 2026 |
| El Khatabi | Expires 10 March 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 their admission into serial-numbered segments, deterministic commitment-tree calculation, an authoritative segment artifact encoded in Concise Binary Object Representation (CBOR), a producer manifest, three disclosure classes, and binding of the artifact digest through a required external timestamp channel. Segment closure uses a deployment-configured elapsed-time interval and does not depend on calendar dates.¶
The profile enables independent recomputation and audit of disclosed evidence from the admitted bytes onward. Transport framing, decryption, anti-replay processing, payload interpretation, and source-telemetry-to-record mapping are outside it, as are device onboarding, end-to-end security of sensor values, and safety decisions.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 10 March 2027.¶
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 much later.¶
Two trust questions must be distinguished. The first is whether a source was genuine, authorized, and mapped correctly. The second is whether, after exact record bytes were selected for commitment, a later verifier can determine that those bytes, the ledger artifact, and the timestamp artifacts are mutually consistent. This profile standardizes the second question only.¶
This memo is an Independent Submission. It is not an Internet Standard and has not been evaluated as having IETF community consensus.¶
Segment intervals are ledger batching rules, not source-reporting periods or civil-time claims. Segment membership follows admission into intervals governed by a non-decreasing elapsed-time source; timestamps carried in records do not select membership (Section 4.4.1).¶
The profile boundary is admission of an already-formed canonical-record byte string. This document specifies admission and the subsequent commitment, disclosure, and verification of those bytes.¶
This informative example shows why the machinery in the rest of this document exists. It is illustrative only; every rule it depends on is specified normatively later.¶
A factory gateway admits sensor readings as canonical-record byte strings throughout the day. At each interval boundary it fixes the membership of a segment. It then hashes the exact admitted bytes, sorts the resulting leaf hashes, computes a segment root, seals a segment artifact naming the ledger, the segment's serial number, its closure claim, and the digest of its predecessor, and submits SHA-256 over the artifact bytes to a Timestamp Authority.¶
Months later a customer disputes one shipment. For this example, the disputed readings fall within two batches of a segment containing several batches. The operator does not want to disclose all the segment's telemetry, so it supplies a producer manifest, the segment artifact, its predecessor artifact, the timestamp response, and any request required for nonce correlation, along with complete openings for those two batches. Because batches are slices of the sorted leaf-hash list, those openings also disclose any unrelated records in the same batches.¶
The auditor recomputes the two batch roots from the disclosed record bytes, checks that the artifact's complete list of batch roots composes to the committed segment root, confirms that the timestamp token binds the exact artifact bytes, and validates the artifact's link to its predecessor.¶
The auditor's result then says exactly that much: the two disclosed batches were recomputed and matched, the artifact existed no later than the timestamp under the auditor's stated policy, and the predecessor link held. It claims nothing about the batches that were not opened, nothing about whether the readings were true, and nothing about whether other segments exist.¶
Disclosing some batches but not all is one of three disclosure levels a producer can claim. They are defined normatively in Section 7; in outline:¶
What a producer discloses and what a verifier actually checks are separate throughout this document. A Class A bundle does not by itself mean anyone performed full recomputation, and a successful narrower check makes no claim about unopened records. Every verifier result states the scope it exercised, and that scope bounds what the result asserts.¶
VTL reuses the Merkle Tree Hash construction of [RFC9162] to commit a producer-local segment. It defines no Certificate Transparency log, transparency service, public append-only log, statement-registration protocol, receipt, inclusion or consistency proof, gossip protocol, or log-auditing API. Unlike Certificate Transparency, VTL sorts leaf hashes before reduction and so commits to a multiset rather than an append-ordered sequence: the root is independent of admission order and supports no append-order, prefix, or consistency claim between partial snapshots. Chaining orders sealed artifacts, not records within a segment.¶
The SCITT architecture in [RFC9943] defines signed statements, registration with transparency services, and receipts; [RFC9052] defines COSE signing and encryption structures. Neither is used by the VTL baseline: the authoritative artifact is not a COSE object and no producer signature is required. A separate specification can publish or wrap VTL artifacts through either without altering the rules defined here.¶
The following informative figure shows the profile boundary and the artifact and timestamp flows used for baseline verification.¶
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.¶
commitment_profile_id. This document assigns the UUIDv4
c08ade4e-1785-4eb6-9648-b7003d76288d; identifier handling is
specified in Section 8.¶
ledger_id. Segment numbers are scoped to that identifier.¶
ledger_id. Its first segment is number zero and carries the
all-zero predecessor value.¶
artifacts.segment_cbor and is also called the segment artifact.¶
tsa.¶
TimeStampResp and any disclosed supporting
TimeStampReq.¶
Batch subtrees and batch openings are defined where they are specified, in Section 4.4.6 and Section 5.1.¶
These roles participate at different sides of the profile boundary; conformance is defined in Section 6.1. The upstream admission system produces canonical-record byte strings after deployment-specific source validation and mapping and is outside this profile. The ledger producer accepts those exact byte strings at the profile boundary, assigns them to serial-numbered segments, constructs segment artifacts, and submits their digests for timestamping; one physical gateway can implement both roles, but conformance begins only at the byte-level handoff. The verifier recomputes commitments and validates timestamp artifacts from disclosed bundles. The Timestamp Authority issues timestamp responses over artifact digests.¶
The input to this section is an exact canonical-record byte string already produced by an upstream admission system. This section covers admission, segment assignment, deterministic hashing and tree calculation, segment formation, artifact construction, and chaining.¶
The profile input is one exact canonical-record byte string. It MUST encode exactly one CBOR data item using the record shape below and the deterministic encoding constraints in Section 4.2, and a ledger producer MUST reject an input that does not satisfy those byte-level constraints.¶
A baseline producer MUST accept every conforming
canonical-record byte string whose total encoded length is no greater
than 4096 octets and whose payload nesting depth is no
greater than 16. Payload nesting depth is zero when payload
is a scalar; each array or map on a path beginning at
payload increases depth by one, the payload depth is the
maximum over all such paths, and the outer seven-element record array
is not counted. A producer MAY accept conforming
records outside this shared acceptance envelope under its documented
resource policy, but a configured maximum below either dimension does
not satisfy baseline producer conformance. These are minimum
acceptance capabilities for bounded-resource interoperability, not
protocol maxima, and the octet value is not derived from a network
MTU. A record outside the envelope is not thereby nonconforming, but a
producer that admits one can produce bundles a conforming baseline
verifier rejects under identified policy. An implementation
MAY validate a byte string incrementally, but admission
applies to one complete byte string encoding exactly one canonical
record; an upstream system MAY instead produce multiple
independently conforming records, each a separate admitted occurrence,
which this profile does not treat as chunks of one record.¶
Once admitted, the exact supplied octets are authoritative. The producer MUST use those octets, without modification, as the leaf preimage under Section 4.3, and MUST NOT decode and re-encode, normalize, enrich, remove, reorder, or otherwise substitute a different byte string. Each admission event contributes one record occurrence, including when two occurrences have identical byte strings. Source transport, framing, authentication, decryption, anti-replay processing, duplicate-submission policy, identifier and message-type mapping, payload interpretation, and construction of the byte string are upstream of this boundary and are neither specified nor verified here; a physical gateway MAY perform those functions and ledger production together, but its interoperability claim begins only with the exact byte string passed across this boundary.¶
Each canonical-record byte string encodes the following fixed seven-element CBOR array:¶
canonical-record = [
version: 1,
device_id: bstr .size 8,
fc: uint,
ingest_time: uint,
device_time: uint / nil,
kind: uint,
payload: restricted-cbor-item
]
restricted-cbor-item =
int / bstr / tstr / bool / nil / finite-float /
[* restricted-cbor-item] /
{ * tstr => restricted-cbor-item }
finite-float = float ; Finite only; deterministic rules follow.
¶
The positional labels are conventional names for inspecting disclosed
records and do not move source-to-record mapping into this profile.
version is the unsigned integer 1;
device_id is an opaque 8-octet identifier assigned upstream;
fc is an unsigned upstream-supplied sequence value with no
replay or duplicate-suppression semantics defined here;
ingest_time is an unsigned count of whole non-leap seconds
since 1970-01-01T00:00:00Z and device_time is an
unsigned application timestamp or null whose epoch and unit
are upstream semantics. Neither value selects segment membership;
kind is an unsigned application discriminator;
and payload is one recursively restricted CBOR data item
whose schema and meaning are outside this profile.¶
Canonical-record identity is exact-octet identity, not numeric or
application-level equality. Within payload the integer
1 and the floating-point value 1.0 are distinct, as
are positive and negative floating-point zero, whose shortest exact
encodings are respectively f90000 and f98000; a
producer or verifier MUST NOT normalize negative zero
to positive zero or convert between an integer and a numerically equal
floating-point value. A different record array shape or byte-level
field type is a different commitment profile and requires a distinct
commitment_profile_id. Application schemas
MAY separately define field meanings, but successful
verification does not validate those external semantics.¶
Commitment bytes MUST satisfy the core deterministic-encoding requirements of Section 4.2.1 of [RFC8949]. Items MUST use definite-length encoding; integers and finite floating-point values MUST use their shortest exact encodings; and map keys MUST be ordered in the bytewise lexicographic order of their deterministic encodings.¶
Map keys MUST be CBOR text strings. Duplicate map keys and invalid UTF-8 text strings MUST be rejected. CBOR tags, NaN, positive infinity, and negative infinity MUST NOT appear. Permitted values are integers, byte strings, text strings, arrays, maps, booleans, null, and finite deterministic floating-point values, including negative floating-point zero. The exact type, sign, and encoded-octet distinctions in Section 4.1 are preserved by admission and verification.¶
Because this profile permits only text-string map keys in their preferred deterministic encodings, bytewise ordering of their encoded heads orders them first by UTF-8 byte length and then by encoded bytes, coinciding with the length-first order of Section 4.2.3 of [RFC8949].¶
JSON projections are optional, non-authoritative, and MUST NOT be commitment inputs; implementations can use [RFC8785] when stable JSON projection octets are useful. Once a canonical-record byte string is admitted, its exact octets MUST be committed without re-encoding. Unless a rule explicitly says otherwise, every digest in this document is computed over the exact received or disclosed octets, and an implementation MUST NOT re-encode, normalize, or reserialize input before hashing.¶
For a given segment, this profile computes segment_root from
canonical-record bytes using the domain-separated tree shape of
Section 2.1.1 of [RFC9162], specialized to SHA-256 and
to the multiset ordering below. SHA-256 is defined by
[FIPS180-4]; [RFC6234] provides
informative implementation descriptions and test material.¶
Every SHA-256 invocation in this profile MUST produce
the complete 32-octet digest; a truncated digest or a different SHA-2
function is nonconformant. For tree hashing an implementation
MUST use exactly the 0x00 leaf prefix and
0x01 internal-node prefix and MUST NOT add,
omit, or substitute another tree-domain prefix.¶
For each exact canonical-record byte string record, compute
leaf_hash = SHA-256(0x00 || record). Sort the resulting raw
32-octet leaf hashes in ascending lexicographic (bytewise) order. The
authoritative artifact represents hash values as CBOR byte strings;
hexadecimal renderings in this document are for human-readable
vectors only.¶
Let VTLRoot(L) operate on the sorted list of already-computed
leaf hashes. VTLRoot([]) = SHA-256("") and
VTLRoot([x]) = x. For a list of n > 1 hashes, let
k be the largest power of two strictly less than n,
and compute
VTLRoot(L) = SHA-256(0x01 || VTLRoot(L[0:k]) || VTLRoot(L[k:n])).
Implementations MUST NOT duplicate an unpaired final
hash. The prefixes provide leaf/parent domain separation, and the
recursive split gives every leaf count a unique tree shape.¶
For an emitted empty segment, record_count
MUST be zero, batch_roots
MUST be an empty array, and segment_root
MUST equal VTLRoot([]), the SHA-256 digest of
zero bytes:
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
For a non-empty segment, record_count MUST be
positive and batch_roots MUST contain at least
one root.¶
The resulting segment_root is deterministic for the multiset
of committed canonical records. Because leaf digests are sorted before
reduction, the root does not commit to admission order. Producer
conformance requires sorting the complete segment leaf multiset before
batching; verification of that global sort rule is complete only under
the public_recompute scope, and weaker scopes provide only the
ordering assurances defined for them in
Section 6.5.3. Any future change to this
calculation that alters commitment bytes MUST use a new
commitment_profile_id.¶
This section specifies how admitted records are assigned to logical intervals, how those intervals close, how durable state survives restart, and how the committed batch subtrees are derived.¶
Segment formation uses a non-decreasing producer elapsed-time source
that MUST progress during normal operation. Its
boundary-logic resolution MUST be no coarser than the
smaller of 1000 milliseconds and
closure_policy.interval_ms; finer sampling is permitted.
The interval is a fixed positive uint64 millisecond duration and
MUST NOT be aligned to civil-time events. The first
interval opens when the producer durably activates the ledger, and
reactivation resumes durable state after shutdown. Every closure
except shutdown opens the next interval immediately.
Intervals continue across idle periods; suppress affects
emission and serial consumption, not the elapsed schedule.¶
The producer MUST serialize the interval-boundary
transition with the admission linearization point so that each
admitted input has exactly one segment assignment. For an interval
whose elapsed-time deadline is T: a record whose acceptance
linearizes before T belongs to the current interval; at
T or later the producer MUST process the
boundary before another acceptance can linearize, so a record
admitted at or after T belongs to the next interval even
when its upstream processing began earlier. Source arrival time,
upstream validation or record-construction time,
device_time, and ingest_time
MUST NOT override this assignment.¶
Processing the boundary means completing the durable close transition in Section 4.4.5, not sorting leaves, encoding or persisting the artifact, submitting its digest, or obtaining a response. Deadline expiry MUST use checked elapsed-time subtraction, not potentially wrapping addition, and an unsafe elapsed-time comparison MUST invoke recovery before another record is accepted.¶
The producer MUST keep the opening-time
closure_policy snapshot unchanged through closure. A policy
update that can wait applies to the next interval. A reconfiguration
request that is to take effect before the current interval would
otherwise close makes the reconfigure condition true. The
producer MUST close the current interval under its
old snapshot before applying the change. When another condition is also true,
Section 4.4.3 selects the committed
reason. Early closure is permitted only for its committed reason.
Late processing MUST NOT move a
post-deadline admission into the expired interval and
SHOULD emit an operational signal as described in
Section 10.1.¶
The manual condition is true only when closure is requested
through a producer-provided administrative interface that is not
driven by elapsed-time, record_limit, or
size_limit_bytes evaluation. The producer
MUST NOT select manual without such a
request. Whether a human operator or automation acting through that
interface initiated it does not change the condition. The request
MUST be serialized with admission and close under the
current snapshot; the committed reason follows
Section 4.4.3 when multiple conditions are
true. A manual closure opens a successor and empty emission follows
empty_mode. shutdown closes for orderly cessation,
MUST NOT open a successor until reactivation, and
MUST emit and consume a serial even for an empty
suppress interval.¶
interval applies at the snapshotted deadline when no higher
condition applies. A non-null record_limit is the maximum
admitted count; on reaching it the producer MUST
close the interval before another acceptance can linearize. A
non-null size_limit_bytes is a close threshold for the
summed admitted-record lengths; at or above it the producer
MUST close before another acceptance. The triggering
record remains assigned, so the field is not a pre-admission maximum
and a single oversized record forms a one-record interval.
Byte-count updates and comparisons MUST use checked
arithmetic; before acknowledging an unrepresentable total the
producer MUST enter recovery or stop acceptance.¶
If multiple close conditions are simultaneously true, the committed
close_reason MUST be selected by the
following precedence, numbered from highest to lowest. The selected
reason does not change which acceptances already linearized into the
interval.¶
With timer continuity, emit produces an artifact for every
interval, using the empty construction when needed. An empty
suppress interval emits nothing and consumes no serial,
except that shutdown and recovery always emit and
consume one. The next artifact remains contiguous. Suppression makes
no completeness or no-event claim.¶
A host suspend interval counts toward the current logical interval when the producer's elapsed-time source advances across the suspend. When it does not, or when elapsed-time-source continuity is otherwise uncertain, recovery MUST precede another acceptance.¶
Before post-restart or failover acceptance, the producer
MUST recover ledger identity, next serial, policy,
open state, and the ordered closed-unsealed queue. It
MUST NOT infer an uncertain boundary from wall time;
it MUST instead stop or close recoverable open
material as recovery, and that closure
MUST emit and consume a serial, including when empty
under suppress. The producer MUST NOT
synthesize missed historical empty artifacts. A previously fixed
close reason MUST survive recovery;
recovery applies only when the interval remained open or
its open state was uncertain at failure.¶
Admission is a durable transaction boundary: before acknowledgment the producer MUST atomically make the exact record, membership, counts, and recovery state durable or recoverable, and after a crash an acceptance MUST be recovered exactly once in the same interval or MUST be treated as never completed. The producer MUST serialize close with admission. The close transition MUST atomically preserve an emitting interval's exact membership, policy, reason, counts, allocated serial, and ordered closed-unsealed state, and except at shutdown MUST also make the successor state recoverable before an admission to that successor is acknowledged. A suppressed empty interval creates neither queue entry nor serial, except for the shutdown and recovery cases above.¶
After close, successor admission and commitment preparation
MAY proceed while intervals remain unsealed.
Artifacts MUST be finalized durably in serial order
with the exact predecessor and digest available for each non-epoch
artifact; sealing MUST NOT alter fixed membership,
policy, reason, counts, or serial, and MUST
atomically preserve the artifact, digest, queue advancement, and
timestamp-submission obligation, after which submission and response
acquisition can be asynchronous. Open, closed-unsealed, and merely
locally queued states have no conforming manifest: only an attempted
submission can be pending, a usable response is
present, a terminal attempt without one is
unavailable, and a local job alone
MUST NOT be represented as pending.¶
Outstanding state MUST have finite recoverable capacity. Before acknowledging an admission that would exceed it the producer MUST apply backpressure or stop acceptance, and before lifecycle closures exhaust it the producer MUST drain or shut down. Loss of exact closed-unsealed state MUST stop acceptance under that ledger; a new epoch is permitted, but the producer MUST NOT skip, reuse, or fabricate material for the affected old-ledger serial. These are observable outcomes, not mandated storage calls.¶
A batch is not a chronological admission chunk. It is a consecutive slice of the complete globally sorted leaf-hash list, formed only after segment membership is fixed.¶
For a non-empty interval, compute the complete sorted leaf-hash list
defined in Section 4.3 and partition it into
consecutive batches of B = closure_policy.batch_record_limit
hashes. B MUST be a positive power of two no
greater than 2^63. Batch zero contains the first B hashes,
batch one the next, and so on; only the last batch can contain fewer
than B. Compute each batch_roots[i] as
VTLRoot over that batch's already-computed leaf hashes.
This is the sole baseline batch partitioning rule and is independent of storage flushes, worker scheduling, or input-file layout. An implementation MAY obtain the required global leaf order through an order-maintaining index or external sort and reduce each aligned batch incrementally, but sorting tentative input chunks or batches independently is not equivalent and MUST NOT replace the global sort; every optimization MUST reproduce the exact commitment and artifact bytes defined here.
Requiring a power of two makes every full batch align with a
recursive VTLRoot split, so an aligned batch root can
substitute directly for its subtree without an auxiliary sibling
path.¶
Let N be record_count. For N > 0 the
number of batch roots MUST be
1 + ((N - 1) / B) using integer division. To compose a
range root from those aligned roots, return the sole supplied root
when the range contains at most B leaves; otherwise let
k be the largest power of two strictly less than the
range's leaf count, which is a batch boundary because B is
a power of two, recursively compose the roots before and after that
boundary, and return SHA-256(0x01 || left || right). The
result MUST equal segment_root. This is
subtree substitution under the VTLRoot recursion in
Sections 2.1.1 and 2.1.2 of [RFC9162]; it does not
define a Certificate Transparency log or CT wire object.¶
The segment artifact is a deterministic CBOR-encoded segment record
produced under Section 4.2 and is the stable
segment-scoped object of commitment verification. It commits to one
record multiset, its ledger identity, serial number, closure policy
and reason, and predecessor artifact, and is not a transport envelope,
publication statement, or complete verification claim. A JSON
projection, filename, and storage path are non-authoritative, and a
verifier MUST NOT trust a path or filename as the
segment number. The artifact is interpreted only under its in-band
commitment_profile_id: because that identifier is inside the
artifact bytes, a valid timestamp token over its digest binds the
commitment semantics. Profile dispatch follows
Section 8.¶
The segment record contains exactly these fields:¶
version (uint): segment-record schema version, exactly 1.¶
commitment_profile_id (tstr): a lowercase RFC 9562 UUID string.¶
ledger_id (tstr): exactly 32 lowercase hexadecimal characters.¶
segment_number (uint): serial number in the range 0..(2^64-1).¶
closure_policy (map): the immutable segment closure policy snapshot, containing exactly version equal to 1; positive uint64 interval_ms; power-of-two uint64 batch_record_limit no greater than 2^63; record_limit and size_limit_bytes, each a positive uint64 or null; and empty_mode equal to emit or suppress.¶
close_reason (tstr): exactly one of interval, reconfigure, record_limit, size_limit, shutdown, recovery, or manual.¶
prev_segment_sha256 (bstr): 32-octet SHA-256 digest of the exact bytes of the previous segment artifact, or the epoch predecessor value in Section 4.6.¶
record_count (uint): number of canonical-record occurrences committed by the segment.¶
batch_roots (array of bstr): ordered aligned-subtree roots, each exactly 32 octets; empty for an empty emitted segment.¶
segment_root (bstr): 32-octet deterministic segment root.¶
When record_count is zero,
closure_policy.empty_mode MUST be
emit unless close_reason is shutdown or
recovery, the mandatory lifecycle artifacts that retain a
committed suppress value; any other empty artifact under
suppress is invalid. Array position is the batch number: the
root at position i MUST commit the consecutive
sorted-leaf range beginning at i * batch_record_limit, with
root count, final-batch width, and composed segment root derived as in
Section 4.4.6.¶
A verifier MUST validate that the producer manifest
segment_number is the shortest unsigned decimal form defined
in Appendix B, parse it as a uint64, and compare that
numeric value with the decoded CBOR uint. A non-shortest decimal form
is a manifest schema violation and therefore a bundle-processing
error; after a valid parse, a numeric mismatch with the artifact is
commitment_mismatch. The artifact uses the registered
application/cbor media type when media-type metadata is
carried, and implementations SHOULD use
application/vnd.vtl.segment+cbor once registered. Media-type
metadata does not determine artifact conformance or profile selection.¶
Segment identity and chaining are defined within one stable
ledger_id and 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 segment artifact.¶
segment_number. Before serial exhaustion it MUST stop the ledger and start a new one with a new ledger_id.¶
segment_number or ledger_id.¶
When a new ledger epoch begins, the producer MUST
generate a new ledger_id so that the probability of collision
with any other ledger identifier is negligible; a uniformly random
128-bit value from a cryptographically strong random source satisfies
this. If durable sequence state cannot be recovered unambiguously, a
producer MUST stop acceptance or begin a new ledger
with a fresh identifier and MUST NOT reset the serial
under the old identifier. Redundant producer instances
MUST use single-writer fencing or an atomic shared
allocator so that they cannot emit different artifacts for the same
(ledger_id, segment_number).¶
A verifier given adjacent segment artifacts MUST
validate serial continuity, exact commitment_profile_id
equality, and predecessor-digest linkage. A profile mismatch is a
segment_chain_mismatch. A partial disclosure omitting the
predecessor can still support validation of the disclosed artifact and
its timestamp artifacts, but the verifier MUST report
chain_status as predecessor_not_disclosed.¶
A deployment can store canonical records, segment artifacts, producer manifests, verifier results, and timestamp artifacts in any layout; filenames and layouts are non-authoritative. Each disclosed record artifact MUST contain the exact admitted canonical-record octets without a wrapper or re-encoding. Every bundle claiming a disclosure class MUST disclose the segment artifact and a producer manifest.¶
The producer manifest repeats the ledger identity, segment number,
commitment profile, claimed disclosure class, artifact digests, and
claimed producer timestamp state; the artifact's in-band
commitment_profile_id remains the cryptographically bound
one. The manifest MUST NOT contain a chain conclusion,
verifier status, failure reason, or overall outcome. Its schema is in
Appendix B and its shape per claimed class in
Section 7. It uses
application/json, and
application/vnd.vtl.manifest+json once registered, on the
same terms as the segment artifact. Deployment-specific data can be
carried only in explicit extension containers, whose presence
MUST NOT alter the semantics defined here, select an
additional evidence channel, or affect the baseline outcome.¶
For a non-epoch segment, a predecessor is disclosed for baseline
verification if and only if
artifacts.predecessor_segment_cbor is present and its
referenced bytes are safely resolved; an extension member or
out-of-band artifact does not substitute for it or change
chain_status.¶
A batch opening is an artifacts.record_batches entry
disclosing every canonical-record occurrence committed by the aligned
batch subtree identified by its batch_number, permitting
recomputation of that indexed root without disclosing other batches.
Each nested records entry represents one admitted occurrence.
Duplicate entries are permitted and MUST be counted
separately; occurrences with identical bytes MAY
reference the same disclosed artifact and a verifier
MAY reuse a successful digest computation, but the leaf
hash MUST contribute once per listed occurrence, and
reference order does not assert admission order. An opening
MUST disclose exactly the number of occurrences derived
for that batch from record_count and
batch_record_limit, and after hashing and sorting those exact
octets their VTLRoot value MUST equal the
indexed batch_roots value.¶
Producer timestamp state is present when the response is
disclosed, pending when issuance was initiated but has not
reached a terminal state, and unavailable when issuance
terminated without a usable response; manifests
MUST NOT use verifier states such as
verified or failed. A pending or
unavailable state MUST omit
tsa_tsr and a present state MUST
reference it. When a disclosed response contains a nonce the manifest
MUST also reference tsa_req; producer
obligations for a nonce-bearing request are in
Section 6.4.¶
Verification under Section 6.5 produces a logical verifier
result. Only that result states the exercised scope, any chain
conclusion, TSA channel status, verifier-policy identifier, overall
outcome, and failure reasons; local diagnostics are
implementation-specific. A producer manifest and a verifier result
MUST NOT be represented as the same object. A verifier
MAY serialize its result as JSON; a serialized result
claiming conformance MUST use the schema in
Appendix B and the application/json media
type.¶
Every path-bearing object consumed during verification, including a
manifest artifact reference or an extension object,
MUST satisfy this subsection. Each path is a UTF-8
string interpreted relative to the bundle root, using / as the
only separator. A path MUST be non-empty,
MUST NOT begin with /, \, or a
drive-letter prefix, and MUST NOT contain \, a
colon, a control character in the ranges U+0000 through U+001F or
U+007F, an empty path component, or a component equal to . or
... Verifiers MUST resolve and open every
consumed path through a safe resolver. These are semantic requirements
and do not depend on a regular-expression dialect or operating-system
pathname behavior.¶
Every artifact listed in the manifest MUST carry a
lowercase hexadecimal SHA-256 digest entry. The manifest's
ledger_id, segment_number, and
commitment_profile_id MUST match the
corresponding artifact fields whenever those fields can be decoded;
segment-number comparison follows
Section 4.5 and profile selection follows
Section 8. Failure mapping for unsafe or
digest-mismatched references is given once, in
Section 6.5.5.¶
This section defines baseline conformance, the required timestamp channel, verification inputs and scopes, and the result reduction rules.¶
A baseline producer MUST emit the fixed
commitment_profile_id defined here, construct segment
artifacts and producer manifests under this profile, and initiate
timestamp submission through the baseline TSA channel for every
emitted segment. Issuance MAY remain pending
while incomplete.¶
A baseline verifier MUST implement this commitment
profile and the baseline TSA channel, obtain the profile identifier
from the segment artifact, and use the result vocabulary and fixed TSA
channel key defined here. Every result claiming baseline conformance
MUST contain the tsa channel key, including
when its status is pending_claim, missing, or
failed, and a baseline success result requires
successful timestamp validation.¶
A baseline verifier MUST configure finite resource
limits for variable-length primary and secondary inputs, decoded
collections, record and batch counts, certificate counts, and parser
recursion. Its canonical-record length and depth limits
MUST admit the shared acceptance envelope in
Section 4.1. This document defines no
mandatory acceptance floor for the other dimensions. Rejection before
the primary-input boundary is a bundle-processing error; rejection
after it solely because an otherwise valid input exceeds identified
policy uses verifier_policy_rejection.¶
Extension data is opaque to baseline producers and verifiers, and its presence, absence, or contents MUST NOT change a baseline channel status or overall outcome. A separate specification can define an additional evidence mechanism without changing the segment artifact or this commitment profile.¶
Verifier results MUST use exactly this vocabulary for
the TSA channel: verified, timestamp validation succeeded for
the disclosed artifact binding; pending_claim, the manifest
claims submission was attempted but no terminal response is disclosed,
an incomplete producer claim rather than an independently verified
fact; missing, an expected response is absent or is
referenced but cannot be safely resolved; and failed,
validation was attempted and did not succeed. Producer state
pending maps deterministically to pending_claim and
unavailable maps to missing. Baseline verification
does not authenticate the claim or change its status based on elapsed
time, manifest provenance, receipt context, or other out-of-band
inputs; a later bundle disclosing a terminal response is a different
fixed-byte input. Outcome effects are in
Section 6.5.6.¶
The TSA-result reason value is human-readable diagnostic
text. producer_pending_claim is the only baseline
machine-defined token; any other value is free text and
MUST NOT be interpreted as a portable
machine-actionable code. A pending_claim result
MUST set reason to
producer_pending_claim; a missing or
failed result MUST include a free-text
reason; a verified result
MUST NOT include one. Additional machine-readable
detail MAY be placed in the channel result's
extensions member and is opaque to baseline verification.¶
A producer computes the SHA-256 digest of the segment artifact and
submits it through the baseline TSA channel; verifiers
MUST recompute that digest over the exact artifact
bytes. Timestamp validation and message-imprint comparison do not
depend on successful CBOR decoding of the artifact. A token that
validates with the artifact digest as its message imprint binds the
complete artifact bytes, including the in-band
commitment_profile_id, but binds no disclosure class,
producer manifest, verifier policy, or verifier result.¶
Consequently, a manifest received over an unauthenticated channel can under-claim the available disclosure as Class C, omit record openings, or direct the verifier to a different valid artifact. Manifest digest checks establish consistency with the received manifest, not its provenance. A relying party needing those properties MUST obtain the authoritative artifact bytes or their expected digest independently through an authenticated mechanism and rerun verification from those bytes.¶
The producer MUST seal and durably write the segment artifact without waiting for issuance completion. Alternative timestamping or attestation mechanisms require a separate specification and do not alter this baseline contract.¶
This channel uses the timestamp protocol of
[RFC3161], as updated by [RFC5816] and
restricted here. The TimeStampReq, TimeStampResp,
and token MUST conform to those specifications, and the
token MUST contain exactly one CMS
SignerInfo as defined by [RFC5652]. These
restrictions define a narrower channel than generic use of that
protocol, so a conforming implementation cannot assume an arbitrary
deployed TSA is compatible; deployments SHOULD
prequalify a TSA before relying on it.¶
The producer MUST set
TimeStampReq.messageImprint.hashAlgorithm to SHA-256 and its
hashedMessage to the exact 32-octet result of SHA-256 over
the segment artifact bytes, MUST NOT hash that result
again, and MUST set certReq to TRUE.
A successful TimeStampResp.status.status
MUST be granted (0); a verifier
MUST reject grantedWithMods (1), and a
producer receiving it MUST treat the terminal attempt
as unavailable and MUST NOT publish that
response as usable baseline evidence.¶
The signed attributes MUST contain exactly one
SigningCertificateV2 attribute with exactly one value, as
defined by [RFC5035], whose first
ESSCertIDv2.certHash MUST equal SHA-256 over
the complete DER encoding of the selected signer certificate; a
present hashAlgorithm MUST identify SHA-256,
omission denoting that default, and a present issuerSerial
MUST identify that same certificate. The signed
attributes MUST NOT contain the legacy
SigningCertificate attribute of [RFC2634].
Because certReq is TRUE,
SignedData.certificates MUST contain exactly
one X.509 certificate matching both SignerInfo.sid and that
first ESSCertIDv2; zero or multiple matches are a
verification failure. The set MAY also contain
unrelated or path certificates, and a verifier
MUST NOT rewrite a received response or reject it
merely for containing additional nonmatching certificates.¶
A baseline verifier MUST NOT report this channel as
verified unless its identified verifier policy supplies
accepted trust anchors, accepted TSA policy identifiers, revocation
policy including acceptable evidence and the treatment of historical
certificate status at TSTInfo.genTime, acceptable signature
and certificate algorithms and strengths, any additional
generation-time constraints, a maximum acceptable future skew, and the
verifier-local time source used for that check. This document defines
no long-term archival-PKI validation profile beyond those inputs.
Missing policy input after the primary-input boundary is
verifier_policy_rejection; if the identified time source is
unavailable at runtime, validation fails, the channel status is
failed, and the result includes channel_failure.¶
artifacts.tsa_tsr carries the exact DER-encoded
TimeStampResp. A verifier MUST validate those
exact referenced bytes, MUST NOT re-encode the response
before any digest or channel check, and MUST recompute
the segment artifact digest. Validation MUST include:¶
granted (0) and required token presence;¶
id-signedData and id-ct-TSTInfo content types;¶
id-kp-timeStamping;¶
TSTInfo.genTime as the date/time input and configured trust anchors;¶
genTime within the signer certificate's validity interval; and¶
The optional artifacts.tsa_req carries the exact DER-encoded
TimeStampReq as a non-authoritative supporting artifact. A
producer sending a nonce-bearing request MUST retain
those exact bytes at least as long as the corresponding response,
MUST disclose the request with that response, and
MUST NOT treat a response that omits or changes its
nonce as successful issuance. Whenever tsa_req is disclosed
the verifier MUST validate its SHA-256 digest, require
the request message-imprint algorithm to be SHA-256 and its
hashedMessage to equal the recomputed artifact digest, and
require certReq=TRUE. If either disclosed object contains a
nonce, both MUST contain one and the values
MUST be equal. A nonce-bearing response without
tsa_req, a nonce present in only one object, unequal nonces,
or any failure of these checks is a timestamp-channel verification
failure reported with channel_failure. If the response
carries no nonce and tsa_req is not disclosed, a verifier
cannot determine whether the original request contained one and the
result makes no nonce-correlation claim. Any failure of the
validations in this section is a timestamp-channel verification
failure; the channel status MUST be
verified on success and failed on failure.¶
Verification addresses four distinct questions. The verifier result identifies the selected scope, reports chain and timestamp status, and combines applicable checks into an overall outcome.¶
These conclusions must be distinguished. An artifact that fails to decode can still carry a valid timestamp over its bytes, and a valid timestamp establishes nothing about record contents. Verification therefore begins at the primary-input boundary, selects one exercised scope, applies the validations that scope supports, and reduces the resulting conclusions to the verifier-result schema. The subsections below fix the inputs and scope selection (Section 6.5.1), the order in which validations are applied (Section 6.5.2), the structural and record-level checks themselves (Section 6.5.3 and Section 6.5.4), and how conclusions reduce to one outcome (Section 6.5.5 and Section 6.5.6).¶
The verifier-result contract applies only after the verifier has
obtained the exact producer-manifest bytes, fully parsed them as a
UTF-8 JSON producer manifest, applied every structural, lexical, and
manifest-only cross-field check that does not depend on the segment
artifact, determined every mandatory result field and the exact
artifacts.segment_cbor reference, and obtained the exact
referenced artifact bytes. Failure to obtain either primary input,
structurally invalid manifest bytes, a missing primary artifact
reference, or unsafe resolution of either primary input is a
bundle-processing error, which a verifier MUST reject
but for which it MUST NOT emit a verifier result; it
is outside the result schema and is not an
insufficient_disclosure conclusion. Structurally invalid
manifest bytes include present without tsa_tsr,
pending or unavailable with tsa_tsr, and
a missing, negative, non-integer, non-shortest, or
uint64-overflowing segment_number or
batch_number; a configured resource limit preventing the
verifier from reaching the boundary likewise produces one. After
artifact decoding, duplicate or out-of-range batch-opening numbers
yield insufficient_disclosure.¶
Verifiers MUST then decode the artifact and determine
the applicable commitment_profile_id from its in-band
field, applying the dispatch precedence in
Section 8. When that field decodes as a text
string the result's optional commitment_profile_id
MUST echo its exact value, including when
unsupported. The identifier is the semantic key for interpreting the
artifact: a structurally well-formed artifact and successful
timestamp validation are insufficient for semantic verification when
it is absent or unsupported. When the exact artifact bytes are
present but do not decode as a valid segment artifact, the result
MUST be failure and include
invalid_segment_artifact; the verifier
MUST still evaluate the TSA channel independently
over those exact bytes, and decoding failure alone
MUST NOT cause channel status failed or
channel_failure.¶
The manifest's disclosure_class is the producer-claimed
class and the result's claimed_disclosure_class
MUST repeat it. The result's
verification_scope identifies the scope selected for the
invocation: public_recompute corresponds to Class A,
disclosed_batch_recompute to Class B, and
anchor_only to Class C, ordered from strongest to weakest.
After satisfying the primary-input boundary the invocation
MUST select an intended scope; selection does not
assert support, which the verifier determines after artifact
decoding and claimed-class shape validation, and earlier fail-fast
termination still reports the selected scope without claiming it
succeeded. The claimed class determines manifest shape; the selected
scope determines which artifacts are consumed and which
recomputation claims are made. Results MUST NOT claim
assurances outside the exercised scope, and exercising a stronger
scope does not rewrite the claimed class. A verifier exercising
anchor_only MUST NOT consume
record_batches, even when valid claimed-class shape
requires those entries to be present, and MUST NOT
claim record-level or batch-opening recomputation.¶
The result's manifest_sha256 MUST equal
SHA-256 over the UTF-8 octets of the validated manifest, and
artifact_sha256 MUST equal SHA-256 over the
referenced bytes of artifacts.segment_cbor, both under the
exact-octet rule in Section 4.2, with
artifact_sha256 present even when those bytes are
malformed. These fields identify the validated inputs and do not
authenticate the result. The verifier MUST always
compare the recomputed artifact digest with
artifacts.segment_cbor.sha256, which checks manifest
self-consistency only and MUST NOT be counted as a
timestamp binding, and MUST likewise verify the
sha256 value of every other manifest reference consumed. A
verifier MAY reuse one successful digest computation
for repeated references to the same bytes; duplicate occurrences
nevertheless contribute once per listed occurrence.¶
A verifier MUST perform every applicable validation required by the sections referenced below. It SHOULD apply them in the fail-fast order shown. This list summarizes ordering and does not create requirements beyond those referenced sections.¶
prev_segment_sha256 against a disclosed predecessor (Section 4.6).¶
record_count, the power-of-two batch limit, batch-root cardinality, and composition of batch_roots into segment_root (Section 6.5.3).¶
public_recompute or disclosed_batch_recompute, including independent recomputation of segment_root under the stronger scope (Section 6.5.3, Section 6.5.4).¶
messageImprint.hashedMessage, whether or not decoding succeeded; a mismatch is a timestamp-channel verification failure, and a producer pending claim lacks the external binding and yields pending_claim.¶
missing; one 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 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 and batch_record_limit, 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 empty-root rule in Section 4.3 applies;¶
record_batches matches Section 7;¶
public_recompute, every disclosed opening is consumed, contains exactly its derived number of occurrences, and has sorted recomputed leaves that reduce to the indexed batch root, and the globally sorted multiset of every disclosed occurrence reduces independently to segment_root;¶
disclosed_batch_recompute, every opening selected for consumption is complete and reduces to its indexed root, and the selected batch numbers form a non-empty proper subset of the authoritative batches; and¶
i and i+1 are both disclosed, the greatest leaf hash in batch i is less than or equal to the least leaf hash in batch i+1.¶
The adjacent-opening comparison is required under either recompute
scope whenever the relevant pair is consumed. A successful
disclosed_batch_recompute result verifies the global sort
rule only at those consumed boundaries, and Class C verifies no
leaf-order boundary. A later public_recompute result
exposing a global ordering violation MUST be
failure with commitment_mismatch.¶
A verifier exercising public_recompute or
disclosed_batch_recompute MUST validate
every record artifact consumed under that scope as one exact
canonical-record byte string before using it as a leaf preimage. The
verifier MUST use the exact disclosed artifact bytes
and MUST NOT reconstruct a record from source
telemetry. The
bytes MUST contain exactly one CBOR data item with no
trailing data, MUST have the seven-element array
shape and field constraints in
Section 4.1, and MUST
satisfy the deterministic encoding, text-only map-key,
duplicate-key, UTF-8, tag, finite floating-point, and
permitted-value rules in Section 4.2 at every
recursive payload level, including the restricted payload shape.¶
The verifier MUST hash the exact validated octets. If
consumed bytes violate any canonical-record rule the result
MUST be failure with
invalid_canonical_record, even when those bytes hash to the
leaf value the producer committed; if a separately observed record
digest, batch root, segment root, artifact digest, or binding also
mismatches, the result MUST additionally include
commitment_mismatch.¶
A baseline verifier exercising either scope MUST
accept every conforming disclosed canonical-record artifact consumed
under that scope that lies within the shared acceptance envelope of
Section 4.1. It MAY
accept conforming records outside that envelope under identified
policy. A conforming record outside the envelope is not an
invalid_canonical_record; rejection after the primary-input
boundary is verifier_policy_rejection.¶
A result whose overall value is failure
MUST contain one or more machine-usable
failure_reasons drawn only from the categories below. A
category MUST NOT occur more than once in one result,
and failure reasons MUST be absent when
overall is success or incomplete.
Deployment-specific detail belongs in extensions; this
document creates no extension namespace for failure-reason values.
A missing primary segment artifact is a bundle-processing error, not
a failure reason.¶
unsupported_commitment_profile:invalid_canonical_record:invalid_segment_artifact:commitment_mismatch:segment_chain_mismatch:chain_status is failed.¶
channel_failure:missing and this category.¶
insufficient_disclosure:scope_not_exercised:verifier_policy_rejection:
A chain_status of predecessor_not_disclosed does
not by itself degrade the outcome; it makes no chain-adjacency
claim. The following reduction is normative and total after the
primary-input boundary has been satisfied and the exercised scope
and identified verifier policy have been fixed. A verifier
MUST evaluate every applicable failure condition and
include every distinct failure reason it reaches; multiple failure
conditions can apply to one invocation, and the two terminal
conditions apply only when no failure condition applies. Extension
data is not a channel and MUST NOT affect this
evaluation.¶
| Condition | Required effect |
|---|---|
| The in-band profile identifier is a well-formed but unsupported UUID. | Add unsupported_commitment_profile. |
| After the primary-input boundary, an otherwise valid input or required validation is rejected solely by identified verifier policy, including a configured resource limit or missing required policy input. | Add verifier_policy_rejection. |
| The verifier intentionally exercises a weaker supported scope when identified policy requires the stronger scope. | Add scope_not_exercised. |
| A required structural, commitment, chain, disclosure, artifact-reference, canonical-record, or other non-channel validation fails. | Add every applicable baseline failure category. |
The baseline TSA channel is failed or missing. |
Add channel_failure. |
| One or more failure reasons were added above. | Set overall to failure. |
No failure condition applies and the channel is pending_claim. |
Set overall to incomplete; failure_reasons absent. |
No failure condition applies and the channel is verified. |
Set overall to success; failure_reasons absent. |
Exercising a weaker supported scope has no independent outcome effect when identified policy does not require the stronger scope, though the selected scope still limits the result's claims. A bundle-processing error produces no result and does not enter this reduction.¶
Every result MUST include a non-empty
verifier_policy_id that stably identifies the complete
policy applied to the invocation within the issuing verifier's
namespace, and the same issuing verifier MUST NOT
reuse one identifier for materially different policies. That policy
includes trust anchors, accepted TSA policy identifiers, revocation
policy, algorithm constraints, and generation-time and future-skew
constraints, and baseline pending-claim reduction does not depend on
it or on out-of-band context. When exact bytes of a corresponding
policy artifact are available the result MUST include
verifier_policy_sha256 computed over those exact bytes;
otherwise that member MAY be absent, and a bare
digest is not sufficient policy identification. Verifier output
MUST NOT be represented as proving more than the
exercised scope.¶
A manifest and its verifier result cover one segment artifact. A
relying party claiming continuous chaining across a disclosed range
MUST have one validated manifest/result pair for
every segment in that range. Every pair MUST carry
the same ledger_id and commitment_profile_id under
their applicable exact-comparison rules, and segment numbers
MUST be contiguous. The first result
MUST have chain_status equal to
epoch, or to validated after validation against
its disclosed predecessor, and every later result
MUST have validated. A gap, identity
mismatch, failed link, or unavailable required predecessor prevents
a continuous-range claim. Successful composition establishes
continuity only across that range; it does not establish global
dataset completeness or the absence of undisclosed ledgers.¶
Verification claims depend on what is disclosed. This section is the single normative statement of manifest shape per claimed class. The producer claims one class in the manifest; the verifier independently reports the scope selected for its invocation (Section 6.5.1).¶
| Class | Required record_batches shape |
Supported scope |
|---|---|---|
| A (Public Recompute) | Non-empty segment: MUST open every batch exactly once, with all record artifacts needed to recompute the claimed segment root. Empty segment: MUST omit. |
public_recompute or anchor_only;
disclosed_batch_recompute only when the artifact contains
at least two batches |
| B (Partner Audit) | MUST open a non-empty proper subset of a non-empty segment's batches, each opening complete. Empty and single-batch segments cannot support Class B. |
disclosed_batch_recompute or weaker |
| C (Anchor-Only) | MUST omit. |
anchor_only
|
The disclosed_batch_recompute scope is supported only when the
authoritative artifact contains at least two batches and the verifier
consumes a non-empty proper subset of them. Selecting that scope for an
empty or single-batch segment MUST produce
failure with insufficient_disclosure; a verifier can
instead select anchor_only.¶
Every bundle MUST include the segment artifact, the
producer manifest, and the tsa member in anchoring,
with the response disclosed when the producer state is
present. An emitted empty segment MAY be
disclosed as Class A or Class C and MUST NOT be disclosed
as Class B; both omit record_batches, and the claimed class and
exercised scope distinguish an empty public recomputation from
anchor-only verification.¶
A Class A manifest claims disclosure sufficient for
public_recompute; it does not claim that a verifier exercised
that scope. A missing required opening or record artifact yields
insufficient_disclosure. A successful
public_recompute result additionally requires complete record
and root recomputation; unsupported profiles, invalid records, and
digest or commitment failures use their separately defined reasons.
Class B outputs
MUST NOT be represented as publicly recomputable; a Class
B bundle MAY include a policy artifact describing
withheld material, and a verifier exercising
disclosed_batch_recompute makes no claim about unopened batch
preimages. Every scope composes the complete authoritative
batch_roots list into segment_root. Class C does not
mean without leaf hashes: a singleton batch root equals its unsalted
leaf hash, so Class C is not a confidentiality guarantee for
low-entropy records. A Class C disclosure MUST be labeled
as such and MUST NOT claim record-level reproducibility.¶
This caveat applies to every successful result: VTL establishes internal consistency and timestamp binding for the disclosed bundle, not completeness of observed or emitted telemetry, physical truth, omitted device lifecycle state, or authorization for autonomous action.¶
A document revision does not select commitment semantics, and schema
version fields identify only individual object shapes; neither
substitutes for commitment_profile_id. This document defines
one shape each for the canonical record, segment record, closure policy,
producer manifest, and verifier result, so their CDDL rule names carry
no version suffix. The first four carry a local version value
of 1; an incompatible change to one of them
MUST increment that value and a parser
MUST reject an unsupported one, but that needs a new
commitment identifier only if commitment semantics also change. The
verifier result carries no version member: it is verifier-authored
output rather than a producer-supplied input, and the
verifier_policy_id required by
Section 6.5.6 already identifies the rules under
which it was produced. A consumer MUST interpret a
verifier result under the member semantics defined in
Appendix B and
MUST NOT infer them from a version member, a media type,
a filename, storage, or carriage; none of these are dispatch inputs.¶
This document defines one randomly generated UUIDv4
commitment_profile_id and requests no registry for it
(Section 11). Baseline producers
MUST emit exactly that UUID, and verifiers
MUST apply dispatch in this order:¶
invalid_segment_artifact;¶
unsupported_commitment_profile, and MUST NOT be classified as invalid_segment_artifact solely because it differs from this document's UUID; and¶
A verifier encountering an unsupported well-formed UUID
MUST report unsupported_commitment_profile
rather than applying fallback semantics. The identifier selects the
canonical-record syntax (Section 4.1),
deterministic byte encoding (Section 4.2), tree
calculation (Section 4.3), aligned-subtree
calculation (Section 4.4.6), artifact bytes
(Section 4.5), and chain-link calculation
(Section 4.6). It does not select minimum-size
capability, output-preserving scheduling or persistence, media
registration or carriage, manifest or result schemas, or external
evidence profiles, and implementations MUST NOT infer
commitment semantics from those properties, a filename, a manifest, an
informal name, or identifier parsing. No namespace for additional values
is defined.¶
A selected-semantic change MUST use a new identifier and
ledger epoch; an excluded, output-preserving change does not, and the
UUID MUST NOT be rebound. A verifier with policy
knowledge of conflicting semantics MUST NOT use this
profile and MUST report
verifier_policy_rejection. A future profile
MUST publish a distinct UUID and begin a new epoch with a
fresh ledger_id, serial zero, and the all-zero predecessor;
producers MUST NOT link across profiles or relabel,
re-encode, or reinterpret sealed artifacts. Rollout
SHOULD preserve historical verification support and
rollback MUST begin another new epoch. A baseline chain
claim cannot cross a profile boundary; associating epochs requires a
separately authenticated statement that is not a VTL commitment input.
Until RFC publication the identifier binding is provisional and
artifacts make only an experimental Internet-Draft conformance claim.
If the selected commitment semantics change before publication, the
changed profile MUST use a new UUID. Publication can
retain this UUID only if those selected semantics remain unchanged.¶
This section is informative. Appendix A supplies the normative commitment known-answer vectors: exact record bytes and numeric distinctions, leaf hashes, aligned batch roots, a segment root and artifact, empty lifecycle closure, and duplicate occurrences. It does not supply a complete protocol test suite or timestamp-response fixtures; a non-normative machine-readable corpus maintained with the draft source can carry the larger and malformed cases.¶
A positive suite ought to cover:¶
A negative suite ought to cover:¶
shutdown or recovery artifacts, and out-of-order
sealing;¶
pending; and¶
grantedWithMods, multiple SignerInfo values,
and the legacy SigningCertificate attribute.¶
Cross-implementation checks verify byte parity across at least two
implementations. The
normative vector's interval_ms value of 86400000 is an elapsed
duration retained for vector continuity and implies no civil-day,
time-zone, daylight-saving, or leap-second alignment.¶
This section follows [RFC5706] and is guidance; the
normative rules are in the sections referenced. Deployments
SHOULD expose admission, sealing, submission, issuance
completion, and verification as distinct states so delayed issuance is
not confused with a malformed artifact, and SHOULD
emit structured signals with stable event codes for handoff and
durability failures, ledger identity and serial conflicts,
elapsed-timer health and closure delay, artifact writes and roots, TSA
backlog and per-segment status, and storage pressure. Deployments
SHOULD document the supported record size and depth,
admission backpressure, closure policy, retention, accepted TSA
identities and policies, generation-time and skew constraints,
verifier policy, and whether reconfiguration can request application
before the current interval would otherwise close; and
SHOULD periodically verify a recent representative
bundle, alerting on failures and on issuance pending beyond policy.
Before first admission an operator SHOULD durably
provision a fresh ledger_id, configure verifier policy and
TSA trust inputs, and prequalify the TSA against
Section 6.4. Long-lived deployments can use an
external renewal profile such as [RFC4998]. This
document defines no management data model, event-code registry, or
management-protocol binding.¶
For predictable bounded operation a deployment
SHOULD configure at least one of
record_limit or size_limit_bytes, or enforce a
finite admission capacity with backpressure; capacity planning for
size_limit_bytes needs to accommodate the admitted record
that crosses the threshold. For N > 0 records and batch
limit B the artifact carries 1 + ((N - 1) / B)
roots, each costing 34 CBOR octets, so 100000 records with
B = 1024 produce 98 roots and about 3332 octets. Larger
batches reduce artifact size but enlarge the minimum Class B
disclosure unit, and when a non-null record_limit is no
greater than batch_record_limit every segment formed under
that policy has at most one batch and cannot support Class B.
Timestamp request rate is one per emitted segment, so sealing and
response acquisition SHOULD be decoupled through finite
recoverable state sized on the maximum emission rate and the expected
service outage rather than on interval_ms alone. Verifiers
SHOULD avoid resolving artifacts outside the selected
scope and MAY cache a successful record digest for
identical referenced bytes while counting every disclosed occurrence
separately.¶
This document has no IANA actions. The
application/vnd.vtl.segment+cbor and
application/vnd.vtl.manifest+json media types used by this
profile are being registered independently of this document under
Section 3.2 of [RFC6838]; the vendor
tree is used for these project-specific VTL artifact and manifest
formats, and this Independent Submission requests no standards-tree
registration. Until those registrations complete, implementations use
the registered generic application/cbor and
application/json types as specified in
Section 4.5 and
Section 5.1. No specialized media type is
requested for verifier results, which use application/json.¶
This document also requests no CBOR tag, CoAP Content-Format, or IANA
registry for commitment_profile_id: the fixed identifier is a
randomly generated UUIDv4 under [RFC9562] and needs no
central allocation. The absence of a registry provides no discovery of
conflicting third-party semantic bindings, a residual issue addressed in
Section 12.1; a future registry requires
a separate specification defining its registration policy. The explicit
statement of no IANA actions follows [RFC8126].¶
This profile introduces no new cryptographic primitives. Its security depends on a byte-preserving handoff, deterministic commitment encoding, trustworthy admission and elapsed-time handling, accurate verifier reporting, and disciplined artifact and timestamp handling, and assumes the collision and second-preimage resistance of SHA-256 and the soundness of the [RFC9162] construction as specialized here. Threats are stated in the style of [RFC3552]. Unless stated otherwise, success establishes only internal consistency with this profile and successful timestamp validation.¶
The specialization sorts leaf hashes before reduction and so commits to
a multiset rather than an ordered list. Because mapping a multiset of
leaf hashes, including occurrence counts, to its ascending sorted list
is injective, distinct multisets yield distinct ordered inputs and the
collision-resistance argument of [RFC9162] applies
unchanged. The 0x00 leaf and 0x01 parent prefixes
preserve domain separation and prevent a leaf hash from being
reinterpreted as an internal node without finding a collision.¶
The formats contain no executable content or intrinsic compression but provide no confidentiality. A manifest directs a verifier to bundle-relative paths, creating traversal, indirection, and substitution risks; verifiers MUST use the safe resolver in Section 5.3, because a pathname check followed by an independent open is unsafe. A timestamp over the artifact authenticates neither the manifest nor the result.¶
An attacker can exploit differences in CBOR validation, re-encoding,
occurrence handling, or tree composition, or present metadata that
does not match the artifact; fixed syntax and encoding, exact-octet
hashing, the in-band profile binding, and identity comparison mitigate
these. With no profile registry there is no discovery mechanism for a
third party assigning different commitment semantics to the same UUID,
so verifiers obtain mappings from identified policy and deployments
accepting unoperated producers SHOULD pin the expected
identifier. Independent artifact-digest recomputation detects mutation
or substitution, but bare leaves and roots omit ledger, serial, and
profile identity, so identical multisets produce identical roots
across ledgers: a verifier or relying party MUST NOT
substitute a bare leaf, batch root, or segment_root for
authoritative-artifact and external-binding validation.¶
An attacker or failed multi-writer deployment can reuse or roll back a
serial, emit conflicting artifacts, substitute a predecessor, or
disclose only a chain fragment. Fresh ledger identity, fencing,
contiguous serials, and predecessor-digest validation detect conflicts
when the relevant artifacts are disclosed but do not prove global
completeness. A ledger_id must remain unique while evidence
can be compared, whereas predictability of segment_number is
a required consequence of serial contiguity; operators
SHOULD audit identifier generation and alert on reuse
of a ledger or ledger/serial pair.¶
A compromised producer or upstream handoff can fabricate, suppress, substitute, replay, or misassign byte strings. VTL makes outputs tamper-evident only after commitment; upstream transport, credentials, decryption, mapping, and parsing are outside this document, and successful verification MUST NOT be reported as validating those properties. Each admission is a distinct occurrence, so a replay is indistinguishable here from a genuine repeated observation, and successful verification of a false record can make it durably and independently recomputable: a relying party MUST NOT treat a successful result as corroboration of the recorded value.¶
An attacker can alter the interval, elapsed-time source, policy, close
reason, or boundary processing. Committing the snapshot and reason
makes the producer claim tamper-evident after sealing but does not
prove honest elapsed time or the event that caused closure; a verifier
cannot distinguish an honest manual or reconfigure
claim from another asserted close reason. False upstream UTC values
remain false admitted values, and UTC changes
MUST NOT affect membership or reopen a sealed segment.
A closed-unsealed interval has no artifact or external binding, so
compromise or loss of that state before sealing can block finalization
of every later artifact in the chain; durable close state, ordered
sealing, finite recoverable queues, and backpressure limit accidental
loss but do not make that state externally tamper-evident. An operator
MUST NOT represent such state as a sealed segment or a
locally queued timestamp job as initiated issuance.¶
This profile defines no producer signing key. The TSA is the only signing authority in the baseline and signs an artifact digest rather than attesting the producer's identity or the truth of producer claims; deployments requiring producer attribution MUST obtain it outside this profile.¶
An attacker can delay or withhold responses, present pending
placeholders as final, or exploit ignored missing responses; the
status vocabulary and outcome reduction constrain these cases. An
incomplete outcome contains no validated external binding and
is the outcome an adversary can induce by suppressing a response, so a
relying party MUST NOT treat incomplete as
equivalent to success and SHOULD reverify from
a later bundle disclosing a terminal response. Successful validation
depends on the TSA key and trust anchors: a key holder can mint a
well-formed token over any digest and asserted genTime.
Validation proves only that bound bytes existed no later than the
asserted time under identified policy; it proves neither producer nor
source identity, nor when the segment opened. A nonce-free response
disclosed without tsa_req provides no nonce-correlation
assurance.¶
The identified verifier policy controls acceptable signature and key
strength; this profile sets no additional universal strength floor.
That policy MUST define later-compromise reevaluation,
acceptable historical revocation evidence, and whether a retired trust
anchor remains acceptable for a token at its TSTInfo.genTime,
and a verifier MUST NOT report verified unless
the selected path and anchor are acceptable under the policy
identified in that result. Relying parties comparing results from
different verifiers therefore need to compare policy identifiers and,
when present, policy digests.¶
SHA-256 is embedded in every commitment in this profile. Retiring it
requires a new commitment profile, and Section 8
requires that profile to begin a new epoch with a fresh
ledger_id and serial zero. No VTL commitment links the
terminated chain to its successor; association requires a separately
authenticated statement. Deployments whose retention horizon can
exceed the acceptable service life of SHA-256 MUST plan
how pre-migration evidence remains verifiable and how the epoch
boundary is authenticated. An external renewal profile such as
[RFC4998] can support long-term preservation but is not
defined here.¶
Producers need input-size limits, rate controls, finite recoverable
queues, and backpressure, with the normative rules in
Section 4.4.5. Verifiers need finite limits on
encoded lengths, collection and certificate counts, and recursion,
with baseline requirements and failure mapping in
Section 6.1 and
Section 6.5.6. Limits need checking before
unbounded allocation or recursion, and arithmetic needs to be checked.
A limit preventing acquisition or sufficient parsing before the
primary-input boundary produces no result; after that boundary,
rejection of an otherwise valid input solely for an identified policy
limit reports verifier_policy_rejection. Because this
document sets acceptance floors only for canonical-record length and
depth, two conforming verifiers can differ on inputs exceeding their
other limits. These controls reduce exposure but are not a complete
denial-of-service defense.¶
Success can be misread as completeness, physical truth, or
authorization; the caveat in Section 7
applies to every result and stronger claims require external controls.
Class B establishes sorting only within consumed openings and across
consumed adjacent boundaries and anchor-only establishes none, so a
later Class A disclosure can reveal a global-sort violation without
contradicting an earlier narrower result. Likewise a segment with a
non-conforming record can verify under anchor_only but
MUST fail with invalid_canonical_record under
any later scope consuming that record.¶
Leaf hashes are unsalted and a singleton batch root is exactly its
leaf hash, which occurs for every batch when
batch_record_limit is one and for the final batch whenever
the record count modulo the batch limit is one; even a Class C
artifact can therefore disclose individual unsalted leaf hashes, and
disclosed low-entropy material permits offline guessing, since
omitting record preimages does not omit leaf hashes. A high-entropy
application-layer nonce can help but is outside this profile. Because
batches are contiguous sorted slices, an opening bounds earlier and
later unopened hashes and openings on both sides bound the intervening
range, so deployments SHOULD minimize the number and
sorted-order spread of openings. Identical segment_root
values reveal identical multisets and MUST be
considered when roots cross privacy domains.¶
Even without payloads, manifests, artifacts, and timestamp artifacts reveal counts, layout, outage windows, delay, intervals, close reasons, and emission cadence, and repeated pseudonymous identifiers can link records and enable re-identification. Operators SHOULD minimize personal data, avoid unnecessary stable identifiers, assess Class A linkability, and apply suitable access and retention controls. Privacy-preserving disclosures MUST NOT be called publicly recomputable unless Class A conditions are met.¶
Separate specifications can define upstream admission, exported bundle
types, SCITT or COSE publication, other timestamping or attestation,
withheld-material disclosure, or other commitment profiles; opaque
extensions do not affect baseline results. A separate disclosure
specification can staple RFC 9162 paths or a reduced-tree construction
inspired by [RFC4998], whose objects are not
interchangeable with VTL batch roots. Any change to commitment bytes
or calculations requires a distinct commitment_profile_id.¶
Wrapped hexadecimal values in this appendix are presentation-only; a verifier or implementer obtains each exact value by concatenating adjacent lines without inserting whitespace.¶
The following normative canonical-record vectors differ only in the
final payload item. They demonstrate that the integer
1, floating-point 1.0, positive floating-point zero,
and negative floating-point zero retain distinct deterministic
encodings and produce distinct leaf hashes.¶
integer_1_record = 87014800000000000000010100f60001 integer_1_leaf = 61d6b5be58a51d90b833cc8f2558d3b0e30a0c41cae971a81a92596013ca4e5c float_1_record = 87014800000000000000010100f600f93c00 float_1_leaf = 793c53c545002c8b872ff84d1f479c1243f36e4ba57dbc8b387ec52c47fe62df positive_zero_record = 87014800000000000000010100f600f90000 positive_zero_leaf = 5cd7c34a8083929449188288272e339f87fe06f64738b2f8f84a2c317ee40c27 negative_zero_record = 87014800000000000000010100f600f98000 negative_zero_leaf = a45dabed6caf13fe4901f9bd596325e65e13d006dbb1ec8f1180b671537ffccb¶
The following compact known-answer vector is normative for the profile
UUID defined in Section 2. Hexadecimal values are
lowercase and unprefixed. It encodes the epoch segment: the outer
segment uses segment_number = 0, and
prev_segment_sha256 is a 32-octet all-zero byte string. The
segment uses batch_record_limit = 2 and carries two aligned
subtree roots.¶
segment_cbor is 462 octets and commits three leaves in two
aligned batch subtrees. segment_sha256 is SHA-256 over those
exact segment_cbor octets. It is the authoritative artifact
digest submitted through the baseline TSA channel and carried by a successor's
prev_segment_sha256; it is not an additional field encoded in
this artifact.¶
record_1 = 87014800000000000000010100f600f6 leaf_1 = b83bc27f2d8be3a66373af24e6af3eeffff99ff0696ae18c70e379e853796d26 record_2 = 87014800000000000000020201f600f6 leaf_2 = b5227357cf5d0619914971d8dc5218c5a99260bc5d5e1b60cb1083a911a2acaf record_3 = 87014800000000000000030302f600f6 leaf_3 = 4f82e3e7ee90a111774dd951471a31d4582e0908a0bd5fd63c0080c0231f40cc sorted_leaves = 4f82e3e7ee90a111774dd951471a31d4582e0908a0bd5fd63c0080c0231f40cc b5227357cf5d0619914971d8dc5218c5a99260bc5d5e1b60cb1083a911a2acaf b83bc27f2d8be3a66373af24e6af3eeffff99ff0696ae18c70e379e853796d26 batch_0_root = 554491e4edf28061622396b83a870db4652211557127c664c6be1c4ad66471ff batch_1_root = b83bc27f2d8be3a66373af24e6af3eeffff99ff0696ae18c70e379e853796d26 segment_root = bc6502552ed0c515f58d1c632e54db37594042609b59838eb0d5b3d5842aa054 segment_cbor = aa6776657273696f6e01696c65646765725f6964782062376131643565343063 366634333865396137356462323763393666333161616b62617463685f726f6f 7473825820554491e4edf28061622396b83a870db4652211557127c664c6be1c 4ad66471ff5820b83bc27f2d8be3a66373af24e6af3eeffff99ff0696ae18c70 e379e853796d266c636c6f73655f726561736f6e68696e74657276616c6c7265 636f72645f636f756e74036c7365676d656e745f726f6f745820bc6502552ed0 c515f58d1c632e54db37594042609b59838eb0d5b3d5842aa0546e636c6f7375 72655f706f6c696379a66776657273696f6e016a656d7074795f6d6f64656873 757070726573736b696e74657276616c5f6d731a05265c006c7265636f72645f 6c696d6974f67073697a655f6c696d69745f6279746573f67262617463685f72 65636f72645f6c696d6974026e7365676d656e745f6e756d6265720073707265 765f7365676d656e745f73686132353658200000000000000000000000000000 00000000000000000000000000000000000075636f6d6d69746d656e745f7072 6f66696c655f6964782463303861646534652d313738352d346562362d393634 382d623730303364373632383864 segment_sha256 = 2672cb72d5f06863110af1b30660c7e5ba495c0b2ce7d084b0436e010e99388d three_identical_record_1_root = 05ddc48e556d67534bf7960a70209696ca9eeb6f18d5595358e7f4804ec87701 four_identical_record_1_root = d5d26faa3f54d81d8173700a48d9286179c9d35685d464183a99b1e966f1dfd8¶
The following 394-octet artifact is the empty successor to the segment
above. It uses the same ledger, policy, and profile, sets
segment_number to 1 and prev_segment_sha256 to the
preceding segment_sha256, and exercises the lifecycle
exception in which an empty shutdown artifact retains
empty_mode equal to suppress. Its empty
batch_roots array and segment_root equal to SHA-256
over zero octets are normative.¶
empty_shutdown_suppress_segment_cbor = aa6776657273696f6e01696c65646765725f6964782062376131643565343063 366634333865396137356462323763393666333161616b62617463685f726f6f 7473806c636c6f73655f726561736f6e6873687574646f776e6c7265636f7264 5f636f756e74006c7365676d656e745f726f6f745820e3b0c44298fc1c149afb f4c8996fb92427ae41e4649b934ca495991b7852b8556e636c6f737572655f70 6f6c696379a66776657273696f6e016a656d7074795f6d6f6465687375707072 6573736b696e74657276616c5f6d731a05265c006c7265636f72645f6c696d69 74f67073697a655f6c696d69745f6279746573f67262617463685f7265636f72 645f6c696d6974026e7365676d656e745f6e756d6265720173707265765f7365 676d656e745f73686132353658202672cb72d5f06863110af1b30660c7e5ba49 5c0b2ce7d084b0436e010e99388d75636f6d6d69746d656e745f70726f66696c 655f6964782463303861646534652d313738352d346562362d393634382d6237 30303364373632383864 empty_shutdown_suppress_segment_sha256 = cde8b546adc5afa446195bb9cc4e519d4bd1c9529d1678bf6e076ef6d24e229e¶
three_identical_record_1_root and
four_identical_record_1_root are segment_root values
for segments containing respectively three and four occurrences of the
exact record_1 bytes. They are independent of
batch_record_limit; that field changes the aligned subtree
representation, not the RFC 9162 reduction of the complete sorted leaf
multiset.¶
A published machine-readable vector set can carry exact canonical
bytes, digests, expected roots, and the applicable
commitment_profile_id. The exact known-answer vectors in this
appendix are normative.¶
This appendix defines the CDDL ([RFC8610]) shape for the producer manifest and the separate verifier result. Both MUST be UTF-8 JSON texts under [RFC8259]; parsers MUST reject duplicate object member names and generators MUST NOT emit them.¶
These prose constraints apply in addition to the shape below.
json-hex32 is exactly 32 lowercase hexadecimal characters and
json-hex64 exactly 64. segment_number is a JSON string
holding the shortest unsigned base-10 form of a uint64 (0, or a
digit 1 through 9 followed by zero or more digits)
whose value MUST NOT exceed 18446744073709551615, which
avoids loss of precision where a JSON implementation's exact integer
range ends at 2^53-1. relative-path is intentionally structural
because RFC 8610 regular expressions cannot portably express
cross-platform containment checks; implementations
MUST apply the semantic path rules in
Section 5.3 to every consumed
path-bearing object, including extensions.¶
Both commitment_profile_id members are generic tstr so
that a manifest can repeat, and a result can echo, a future or
unsupported artifact value; neither relaxes the baseline producer
requirement in Section 8, and the result member is
optional because a malformed artifact might not decode far enough to
recover it. chain_status MUST be present
whenever the artifact decodes far enough to support one of the chain
conclusions defined here and MUST be absent otherwise,
absence making no chain claim; failure_reasons
MUST be present and non-empty exactly when
overall is failure. Both the anchoring and
channels maps MUST contain exactly the
tsa key, and extension containers MUST NOT add
channel keys or alter a baseline status or outcome. Violating either
direction of the state-to-reference rule in
Section 5.1 makes the manifest structurally
invalid, whereas a nonce-bearing response without tsa_req is a
timestamp-channel verification failure rather than a manifest-structure
error, because a nonce is learned only by inspecting the response.¶
producer-manifest = {
"version": 1,
"ledger_id": json-hex32,
"segment_number": uint64-decimal,
"commitment_profile_id": tstr,
"disclosure_class": disclosure-class,
"artifacts": artifacts,
"anchoring": anchoring,
? "extensions": { * tstr => json-data },
}
artifacts = {
"segment_cbor": artifact-ref,
? "predecessor_segment_cbor": artifact-ref,
? "record_batches": [+ record-batch-opening],
? "tsa_req": artifact-ref,
? "tsa_tsr": artifact-ref,
? "extensions": { * tstr => artifact-ref }
}
record-batch-opening = {
"batch_number": uint64-decimal,
"records": [+ artifact-ref]
}
artifact-ref = {
"path": relative-path,
"sha256": json-hex64
}
anchoring = {
"tsa": producer-tsa-state
}
producer-tsa-state = {
"status": "present" / "pending" / "unavailable"
}
verifier-result = {
"artifact_sha256": json-hex64,
"manifest_sha256": json-hex64,
? "commitment_profile_id": tstr,
"claimed_disclosure_class": disclosure-class,
"verification_scope":
"public_recompute" / "disclosed_batch_recompute" / "anchor_only",
? "chain_status": chain-status,
"channels": channels,
"verifier_policy_id": non-empty-tstr,
? "verifier_policy_sha256": json-hex64,
"overall": "success" / "incomplete" / "failure",
? "failure_reasons": [+ baseline-failure-reason],
? "extensions": { * tstr => json-data }
}
chain-status =
"epoch" /
"validated" /
"predecessor_not_disclosed" /
"failed"
baseline-failure-reason =
"unsupported_commitment_profile" /
"invalid_canonical_record" /
"invalid_segment_artifact" /
"commitment_mismatch" /
"segment_chain_mismatch" /
"channel_failure" /
"insufficient_disclosure" /
"scope_not_exercised" /
"verifier_policy_rejection"
channels = { "tsa": verifier-tsa-result }
verifier-tsa-result =
{
"status": "verified",
? "extensions": { * tstr => json-data }
} /
{
"status": "pending_claim",
"reason": "producer_pending_claim",
? "extensions": { * tstr => json-data }
} /
{
"status": "missing" / "failed",
"reason": non-empty-tstr,
? "extensions": { * tstr => json-data }
}
disclosure-class = "A" / "B" / "C"
relative-path = tstr
uint64-decimal = tstr
json-hex32 = (tstr .regexp "[0-9a-f]{32}") .size 32
json-hex64 = (tstr .regexp "[0-9a-f]{64}") .size 64
non-empty-tstr = tstr .size (1..18446744073709551615)
json-data =
; For JSON null; finite-float restrictions are normative prose.
nil / bool / int / float / tstr /
[* json-data] / { * tstr => json-data }
¶
This appendix gives the structural CDDL for the authoritative segment
artifact. The lexical and cross-field requirements in
Section 4.5, the encoding rules in
Section 4.2, the commitment-tree and empty-segment
rules in Section 4.3, the formation rules in
Section 4.4, and the chain rules in
Section 4.6 are normative in addition to this
shape. cbor-hex32 is distinct from the JSON rule in
Appendix B and is exactly 32 lowercase hexadecimal
characters. CDDL cannot express the power-of-two restriction or the
upper bound of 2^63 on batch_record_limit; both prose
constraints apply in addition to power-of-two-uint64.¶
segment-record = {
"version": 1,
"commitment_profile_id": tstr,
"ledger_id": cbor-hex32,
"segment_number": uint64,
"closure_policy": segment-closure-policy,
"close_reason": close-reason,
"prev_segment_sha256": sha256-value,
"record_count": uint64,
"batch_roots": [* sha256-value],
"segment_root": sha256-value
}
segment-closure-policy = {
"version": 1,
"interval_ms": positive-uint64,
"batch_record_limit": power-of-two-uint64,
"record_limit": positive-uint64 / nil,
"size_limit_bytes": positive-uint64 / nil,
"empty_mode": "emit" / "suppress"
}
close-reason =
"interval" /
"reconfigure" /
"record_limit" /
"size_limit" /
"shutdown" /
"recovery" /
"manual"
uint64 = 0..18446744073709551615
positive-uint64 = 1..18446744073709551615
power-of-two-uint64 = positive-uint64
cbor-hex32 = (tstr .regexp "[0-9a-f]{32}") .size 32
sha256-value = bstr .size 32
¶
The author thanks Joe Clarke for OPSDIR review feedback that improved the operational considerations and deployment guidance.¶