Proof Bundle Wire Format
The self-describing JSON and CBOR wire format of a Truestamp proof bundle, the named type registry, the subject with its claims, metadata and witnesses, block maps that carry their own metadata, the block path, external commitments, the signing key event, and why nothing opaque travels.
Overview
A proof bundle is the receipt you download when you timestamp something with Truestamp. It is a single small file that anyone can check for themselves, without asking Truestamp whether it is real. That is the whole point: you do not have to trust us, because the file carries its own evidence. Everything after this section is the technical, field-by-field reference for developers who build software that reads or writes these files.
What a bundle proves is a submission window. Its subject is a submitted item, an entropy observation, or a Truestamp block. An item bundle shows the item was submitted after the public records its fingerprint commits to, and every bundle shows its subject sits in a Truestamp block that was later committed to a transaction on a public blockchain. It does NOT prove when the underlying content was originally created, and it does not prove who authored it. For the exact timing rules see verify a proof.
Under the hood, a bundle is a compact, self-contained artifact that lets a
verifier reconstruct the cryptographic chain from the subject out to the
public-blockchain transaction, without ever contacting a Truestamp server. Every
bundle shares one top-level schema discriminated by a type name, carries a
single Ed25519 signature over a fixed-width binary payload, and serializes to two
interchangeable encodings: a human-readable JSON form and a compact binary
CBOR form (CBOR is a standardized binary encoding for the
same data JSON holds). This concept is the field reference for that wire format.
It does not walk through the verification steps (see
verify a proof) or the proof lifecycle (see
the proof lifecycle).
Two properties shape everything below. First, nothing opaque travels: every hash a verifier meets is recomputed from bytes the bundle carries, so the bundle holds the metadata maps themselves and carries no metadata hash and no block hash anywhere. Second, the keys are long and readable, and nothing cryptographic depends on a key name: the signature covers a fixed binary payload, and every hash is computed over decoded values.
The bundle is designed to be verifiable offline. The external calls a verifier can make are to the public blockchains (Stellar Horizon, a Bitcoin node) when confirming the commitment transactions exist, and to the original public sources (the NIST beacon, Stellar, Bitcoin) when confirming a witness or an entropy subject. None of them is a Truestamp server. Trusting the signing key does need one out-of-band fetch of Truestamp’s published keyring, or a carried signing key event, but neither is fetched per verification. A bundle carries everything else it needs inline.
Top-level structure
Every bundle is a single map (JSON object or CBOR map) with these top-level keys.
The type name selects the subject type and determines which of the optional
keys are present.
| Key | Type | Presence | Meaning |
|---|---|---|---|
version |
integer | always | Format version. Currently always 1. |
type |
string | always | Subject type name (see the type registry below). |
generated_at |
string | always | ISO 8601 generation timestamp, truncated to whole seconds. |
public_key |
string | always | Base64 Ed25519 public key of the proof signer, 32 bytes, standard base64 with padding. |
signature |
string | always | Base64 Ed25519 signature over the proof payload, 64 bytes, standard base64 with padding. |
subject |
map | absent for block and beacon |
The subject being attested. |
inclusion_proof |
string | absent for block and beacon |
Base64url (no padding) Merkle inclusion proof of the subject hash into block.merkle_root. |
block |
map | always | The block that contains the subject. |
block_path |
array of block maps | optional | The blocks between the head block and the containing block, oldest first. |
commitments |
array | always, non-empty | External commitments to public blockchains, at least one entry. |
signing_key_event |
map | optional | The ledger block that introduced the key that signed this bundle. |
Key order carries no meaning. Canonicalization is applied only to the individual maps that are hashed, never to the bundle as a whole, so a bundle may present its top-level keys in any order and still verify.
The non-empty commitment invariant
commitments is required and MUST contain at least one entry. A bundle with no
external commitment would resolve only to Truestamp’s own signature chain, which
contradicts the promise of offline verifiability without trusting Truestamp. A
subject whose block has not yet been committed to a public blockchain cannot
produce a bundle: generation fails until the next epoch commit lands.
The subject
subject is present for item and entropy subjects and absent for the two
block-like types, where the block is its own subject.
| Key | Presence | Meaning |
|---|---|---|
id |
always | Subject id: a 26-character ULID for an item, a UUIDv7 for an entropy observation. |
claims |
item subjects | The claims map exactly as submitted, hashed under prefix 0x11. |
entropy |
entropy subjects | The captured source payload, hashed under prefix 0x21. |
metadata |
always | The subject’s metadata map, hashed under 0x12 (item) or 0x22 (entropy, always the empty map). |
signing_key_id |
always | 8 lowercase hex characters identifying the key that signed the subject, bound into the composite preimage as its four raw bytes. |
witnesses |
optional, item subjects only | Witness name to witness detail. |
The composite subject hash binds the id, the data hash, the metadata hash, and
the signing key id, under prefix 0x13 for an item and 0x23 for an entropy
observation. The metadata hash in that preimage is derived from the carried
metadata map rather than read from the bundle.
An item’s metadata map holds exactly one key, witnesses, whose entries name
the public records the item’s fingerprint committed to at submission:
"metadata": {
"witnesses": {
"block": "<hex hash of the head block at submission>",
"entropy_stellar": "<hex hash of the newest captured Stellar observation>",
"entropy_nist": "<hex hash of the newest captured NIST observation>",
"entropy_bitcoin": "<hex hash of the newest captured Bitcoin observation>"
}
}
block is always present. Each entropy_* key is present only when that source
had captured at least one observation by the time the item was submitted, and is
absent otherwise, never null. An entropy subject’s metadata is always the empty
map: the captured payload is itself the subject, so it witnesses nothing.
Block maps
The same five-key shape appears everywhere a block appears: the top-level
block, each block_path entry, signing_key_event.block, and the block
witness inside subject.witnesses.
| Key | Meaning |
|---|---|
id |
Block id (UUIDv7). |
previous_block_hash |
Hex SHA-256 hash of the preceding block. |
merkle_root |
Hex SHA-256 Merkle root of the block. |
metadata |
The block’s metadata map, hashed under prefix 0x33. The empty map for an ordinary block; a key-event block carries {"key_event": {...}}. |
signing_key_id |
8 lowercase hex characters identifying the key that signed the block. |
A block hash is derived under prefix 0x32 from the id, previous block hash,
Merkle root, metadata hash, and signing key id. The block hash itself is never
carried anywhere in a bundle, so a bundle cannot lie about it: a verifier
recomputes it, and the metadata hash it needs is recomputed from the carried
metadata map.
The block path
block_path appears only when the item’s head block (the block whose hash
subject.metadata.witnesses.block names) is not the containing block’s direct
predecessor, and only when the block witness was asked for. It is ordered
oldest first: block_path[0].previous_block_hash equals the head block hash,
each following entry’s previous_block_hash equals the hash of the entry before
it, and block.previous_block_hash equals the hash of the last entry. Each entry
is an ordinary block map. The path carries the ledger-position claim of the block
witness and nothing else; no witness hash depends on it.
External commitments
Each entry in commitments records one public-blockchain commitment of the block
hash into an epoch Merkle root, and carries the epoch Merkle proof
(epoch_proof, base64url, no padding) that walks the block hash up to that root.
An epoch Merkle root is the single hash that summarizes a batch of Truestamp
block hashes; that one root value is what actually gets written to the public
blockchain, and the epoch proof shows this block belongs under it.
The chain is named in chain, and the value written on chain is in
epoch_merkle_root on both chains. There is one root key, not one per chain, so
nothing dispatches on which key happens to be present.
| Key | Presence | Chains | Meaning |
|---|---|---|---|
chain |
required | both | "stellar" or "bitcoin". |
epoch_merkle_root |
required | both | Hex 32-byte epoch Merkle root: the Stellar transaction memo, or the Bitcoin OP_RETURN payload. |
epoch_proof |
required | both | Base64url Merkle proof: block hash to the epoch Merkle root. |
transaction_hash |
confirmation | both | Hex 32-byte transaction hash or txid. |
network |
optional | both | public or testnet (Stellar); mainnet, testnet, or regtest (Bitcoin). |
timestamp |
optional | both | ISO 8601: the Stellar ledger close time, or the Bitcoin block header time. |
ledger |
optional | Stellar | Ledger sequence number. |
block_height |
confirmation | Bitcoin | Block height. |
txoutproof |
optional | Bitcoin | Hex txoutproof (partial Merkle tree). |
raw_transaction |
optional | Bitcoin | Hex raw transaction. |
block_merkle_root |
optional | Bitcoin | Hex 32-byte Bitcoin block Merkle root. |
Presence is graded by consequence, in three tiers, and only the first is a rejection. Required means the whole bundle is rejected without it: lacking the chain name, the epoch proof, or the epoch root, the entry cannot enter the epoch-proof walk or the signed epoch-root list at all. Confirmation means the field is needed only to look the transaction up on the public chain: without it that chain’s confirmation step has nothing to query and is reported skipped, exactly as a network failure is. Optional means its absence narrows what the confirmation step can establish and is never a failure.
What an absent optional field does
Truestamp legitimately emits entries that omit optional fields, so a verifier
that treats them as required rejects real bundles. The Bitcoin offline payload
txoutproof, raw_transaction, and block_merkle_root is the case that matters
in practice, and each field’s absence skips exactly the checks that consume it:
- an absent
txoutproofskips the txoutproof parse, the partial-Merkle-tree walk, the check that the txid is in the matched transaction set, and the cross-check ofblock_merkle_rootagainst the txoutproof header; - an absent
raw_transactionskips the OP_RETURN extraction and the txid recomputation; - an absent
block_merkle_rootskips the cross-check of the recorded block Merkle root against the txoutproof header; - an entry carrying none of the three carries no offline Bitcoin evidence at all, and its Bitcoin commitment is reported skipped.
An absent network on its own never skips a confirmation step: a Stellar entry
with no network still resolves to the non-public Horizon instance, while a
verifier must not guess a Bitcoin network. An absent transaction_hash (either
chain) or block_height (Bitcoin) leaves the confirmation step with nothing to
query, so it is likewise skipped, never failed.
Witnesses
A witness is a public record that existed before the submission and that the subject’s composite fingerprint commits to. Witnesses open the submitted-after edge of the submission window; commitments, their mirror image, close the submitted-before edge.
subject.metadata.witnesses names each witness by its hash, and is what the
fingerprint commits to. subject.witnesses carries the underlying detail so a
verifier can rehash it and confirm the match. The two are independent: the
metadata map is fixed at submission and always travels, while which details ride
along is chosen when the bundle is generated.
| Name | Committed in item metadata | Detail carried | How it is checked | What a confirmed witness establishes |
|---|---|---|---|---|
block |
yes | the head block map | recompute the block hash (0x32) and compare it with the committed value |
the head block existed, and the containing block’s ledger position follows from it |
entropy_stellar |
yes | the observation’s payload map | hash the payload under 0x21 and compare |
submitted after that ledger’s close time |
entropy_nist |
yes | the observation’s payload map | hash the payload under 0x21 and compare |
submitted after that pulse’s publication time |
entropy_bitcoin |
yes | the observation’s payload map | hash the payload under 0x21 and compare |
submitted after that block header’s time, read conservatively |
signing_key_event |
no, carried at the top level | the key-event block and its own commitments | recompute the block hash, then walk each epoch proof | the signing key was introduced by a publicly committed key event |
No witness introduces a new byte prefix: the block witness reuses the block-hash prefix and every entropy witness reuses the entropy prefix. See the byte-prefix registry.
Witnesses are selectable, and the choice never touches a hash
Which witness details ride in a bundle is a generation argument. A download
offers a complete file, carrying every witness so the file stands alone, and
a compact file carrying none. The API’s witnesses argument takes the same
choice, plus any subset.
Selecting witnesses cannot change a hash or the signature. The signed payload
covers the subject hash, the block hash, and the epoch roots; the subject’s
metadata hash covers subject.metadata (which holds the witness hashes the
fingerprint commits to), not subject.witnesses (which holds the details). A
compact bundle therefore verifies under exactly the same signature as the
complete one, and a verifier reports each committed witness as committed but not
carried. Because the metadata map always travels, even a compact bundle names
every witness by hash.
Unknown witness names never break a bundle
A verifier hashes subject.metadata exactly as carried, so a witness name it
does not recognize cannot change any digest it computes. An unrecognized name,
whether it appears in subject.metadata.witnesses or in subject.witnesses, is
reported as a visible skip and never fails a proof. Names are permanent: a name
is never renamed and never reused, and a new kind of witness claims a new name.
Those two rules together are what let the witness registry grow without a format
version bump.
The signing key event
signing_key_event is optional and carried at the top level, because it
witnesses the proof’s signature rather than the submission. It holds the ledger
block whose metadata records the key event that introduced the signing key, plus
that block’s own public-chain commitments:
"signing_key_event": {
"block": {
"id": "...",
"previous_block_hash": "...",
"merkle_root": "...",
"metadata": {
"key_event": {
"type": "genesis",
"key_id": "f2c39df9",
"sequence": 0,
"public_key": "<base64>",
"prerotation_commitment": null
}
},
"signing_key_id": "..."
},
"commitments": [ "...this block's own commitments, same shape as above..." ]
}
type is genesis, rotation, or emergency_rotation. A verifier confirms
that key_event.public_key equals the bundle’s public_key and that
key_event.key_id equals the key id derived from it, recomputes the block hash
from the carried map, and walks each commitment’s epoch proof to its recorded
root. The event rides along only when that key-event block has at least one
external commitment, so a bundle either carries a chain-checkable key event or
carries none at all. See Ed25519
signatures for how this sits alongside the
pinned keyring.
The type registry
One unified registry names subject types and commitment chains, namespaced by decade. Names travel on the wire because a name is readable and a stray integer is not; the frozen integer code travels inside the signed payload. Both halves are permanent: a name is never renamed or reused, and a code is never renumbered or reassigned.
| Name | Code | Where it appears |
|---|---|---|
block |
10 | type; the code is emitted in the signed payload |
beacon |
11 | type; the code is emitted in the signed payload |
item |
20 | type; the code is emitted in the signed payload |
entropy_nist |
30 | type; the code is emitted in the signed payload |
entropy_stellar |
31 | type; the code is emitted in the signed payload |
entropy_bitcoin |
32 | type; the code is emitted in the signed payload |
stellar |
40 | commitments[].chain; the code is reserved and never on the wire |
bitcoin |
41 | commitments[].chain; the code is reserved and never on the wire |
The decades are meaningful: 10-19 block-like subjects, 20-29 item subjects,
30-39 entropy sources, 40-49 external commitment chains, 50 and above
reserved.
Block-like subjects
Both block and beacon share the same structural shape: no subject, no
inclusion_proof, block present, commitments non-empty. For these the block
is the subject, so there is no intra-block Merkle inclusion step and no witness
map. The two are cryptographically distinct only through the type code inside the
signed payload, so a beacon signature does not verify as a plain block signature
and vice versa, even for the same block. That gives a verifier a self-describing
discriminator without relying on a filename or out-of-band metadata.
The signature payload
Every bundle carries exactly one Ed25519 signature computed over
SHA256(0x61 || payload), where payload is a fixed-width, big-endian binary
layout. The leading 0x61 is a domain prefix: a fixed one-byte tag prepended
before hashing so that this hash can never collide with a hash computed for a
different purpose in the system. Each kind of hash uses its own prefix (see
domain-separated hashing).
offset size field
------ ---- -----
0 1 v version, uint8, currently always 1
1 2 t type code, uint16 big-endian (the registry integer for `type`)
3 4 kid signing-key id of the proof signer, derived from public_key
7 8 ts_ms generated_at in milliseconds since the Unix epoch, uint64 big-endian
15 32 subject_hash raw bytes
47 32 block_hash raw bytes
79 2 N count of epoch roots, uint16 big-endian
81 32*N epoch_roots concatenated raw epoch roots, in commitments array order
Three properties make this payload the tamper-evidence root for the whole bundle:
- The
tvalue in the payload is the registry integer for the bundle’stypename. Changingtypechanges the pre-image and fails the signature check. This is what makes block and beacon cryptographically distinct. - The
kidin the payload identifies the key that signed the proof and is derived at verification time from the suppliedpublic_key(SHA256(0x51 || public_key)truncated to 4 bytes), not read from anysigning_key_idin the bundle. Under steady state all three match; under legitimate key rotation the signer’s key id may differ from the block or subject keys, and that is allowed. - The epoch roots are the
epoch_merkle_rootof each commitment entry, concatenated in array order. Reordering or alteringcommitmentschanges the payload.
For block-like subjects, subject_hash == block_hash: the same 32 bytes fill
both slots. That degenerate case is intentional and harmless.
JSON and CBOR encodings
A bundle serializes to one of two interchangeable encodings that encode the same logical bundle and pass through the same verification.
The JSON form is human-readable. Hash-type fields are lowercase hex strings;
public_key and signature are standard base64; the Merkle proofs
inclusion_proof and epoch_proof are base64url with no padding.
The CBOR form (RFC 8949) is the compact binary form. The whole bundle is wrapped
in the CBOR self-describing tag 55799 (the three bytes 0xd9 0xd9 0xf7), so a
stream parser can detect the format from its prefix.
One rule decides which fields become CBOR byte strings: a field is a byte string when it is a member of a length-prefixed hash preimage or is raw cryptographic material. Everything else keeps its natural CBOR type, and every map that is canonicalized before hashing stays in the JSON value space, hex strings included, so that its bytes canonicalize identically in both encodings.
| Field | JSON | CBOR |
|---|---|---|
public_key |
base64 string | byte string (32 bytes) |
signature |
base64 string | byte string (64 bytes) |
every signing_key_id |
hex string (8 chars) | byte string (4 bytes) |
every previous_block_hash, merkle_root |
hex string (64 chars) | byte string (32 bytes) |
epoch_merkle_root, transaction_hash, block_merkle_root |
hex string (64 chars) | byte string (32 bytes) |
subject.claims, subject.entropy |
JSON object | CBOR map, JSON value space |
subject.metadata (witness hashes included) |
JSON object | CBOR map, JSON value space |
every block metadata, including in block_path and the key event |
JSON object | CBOR map, JSON value space |
every subject.witnesses.entropy_* payload |
JSON object | CBOR map, JSON value space |
inclusion_proof, epoch_proof |
base64url string | text string, unchanged |
txoutproof, raw_transaction |
hex string | text string, unchanged |
version, ledger, block_height |
numbers | numbers |
ids, type, chain, network, timestamps |
text strings | text strings |
The block witness inside subject.witnesses is an ordinary block map, so its
hash fields follow the block rule and become byte strings, while its metadata
stays in the JSON value space like every other hashed map.
The Merkle proofs and the Bitcoin hex fields stay text strings in CBOR, and none
of the four is ever decoded from a byte string. A byte string in
inclusion_proof or epoch_proof rejects the bundle; one in txoutproof or
raw_transaction leaves the checks that consume it with nothing to read, so they
report skipped.
Offline verifiability
The bundle is self-contained by design. A verifier reconstructs the full chain using only the bundle contents:
- The subject data, the metadata map, and the signing key id regenerate the subject hash for non-block-like subjects.
- The inclusion proof walks the subject hash up to
block.merkle_root. - The block fields, with the metadata hash derived from the carried metadata map, regenerate the block hash.
- Each
epoch_proofwalks the block hash up to an epoch Merkle root that equals the on-chain value inepoch_merkle_root. - Each carried witness rehashes to the value
subject.metadata.witnessescommits to, which is what binds the witness to the fingerprint. - The signature, the public key, and the reconstructed payload confirm Truestamp signed exactly this bundle.
Confirming that the commitment transactions exist on Stellar or Bitcoin, and re-fetching a witness or an entropy subject from its own public source, are the steps that reach the network, and all of them are optional: a verifier can run fully offline and simply mark those checks as skipped. Tampering with any signed input breaks verification.
Nothing opaque travels
A bundle carries no hash it does not also carry the preimage for. Carrying a metadata hash on its own would leave a verifier holding a 32-byte value it could not independently recompute, so the bundle carries the maps and every hash a verifier meets is derived from bytes in the file:
subject.metadatais the preimage of the subject’s metadata hash, so the witnesses that open the submitted-after edge are readable rather than hidden behind a digest;- every block map carries its own
metadata, so the block metadata hash and then the block hash are both derived rather than trusted; - no block hash appears anywhere in the bundle, so there is nothing for a bundle to assert about a value a verifier must compute.
The practical consequence is that both edges of the submission window are
reachable from the file. The submitted-before edge rests on the commitment
transaction carrying an epoch root that covers this block. The submitted-after
edge rests on the witnesses: their hashes always travel inside
subject.metadata, and a complete bundle also carries the underlying records, so
a verifier can rehash each one and then re-fetch it from NIST, Stellar, or
Bitcoin to pin the published time the edge rests on.
Integers in subject data must stay inside the safe range
Subject data is canonicalized with JCS before it is hashed, and RFC 8785 defines every JSON number by parsing it into an IEEE-754 double. An integer outside the range a double represents exactly therefore canonicalizes differently depending on the verifier: Truestamp emits it verbatim, while a JavaScript, Go, or Python verifier reads a rounded value back and derives a different digest. Such a proof is cryptographically sound but not portably verifiable, and a verifier that does not notice will report a valid proof as failed.
RFC 8785 states the bound in Appendix B as a SHOULD: integers should lie within
-9007199254740991 to 9007199254740991, that is plus or minus 2^53 - 1,
which is JavaScript’s Number.MAX_SAFE_INTEGER. Truestamp enforces it at
submission: an Item whose claims carry an integer outside that range is rejected,
with an error naming the offending key. Send a larger value as a string instead.
Floats are unaffected and are not restricted. A float is already a double, so every implementation reads back the same value, and the canonical form is determined by ECMA-262’s number-to-string rules that RFC 8785 adopts.
The rule applies to every map a verifier canonicalizes, which includes the metadata maps and the carried witness payloads. Entropy observation payloads are captured verbatim from NIST, Stellar, and Bitcoin, so Truestamp does not control their contents; a client reading one should be prepared for the same limit.
Nesting depth is limited to 32 levels
Truestamp’s verifier refuses a bundle nested more than 32 levels deep, the bundle
object itself counting as the first level and every map and list inside it as one
more, whichever encoding it arrives in. The check runs before anything in the
bundle is canonicalized, because the cost of canonicalizing grows quickly with
depth, and it stops at the first level past the limit. The refusal is a rejection
of the whole bundle as malformed, not a failed step. A bundle Truestamp issues
stays well inside the limit: its deepest part, an item’s claims metadata, is
itself limited to 10 levels of nesting at submission.
Versioning
version is the format version, and it is 1. Version 1 is the first
published format, and the layout described in this concept is what it means.
A bundle carrying a top-level v or t key, where this layout carries version
and type, is a short-key pre-publication draft and is not supported. A verifier
that receives one rejects the whole bundle as unsupported_layout and should
tell the holder to regenerate the proof; no verifier, page, or document keeps a
second code path for that shape. Regenerating a proof for a subject that is still
committed produces the published layout.
version is a hard compatibility boundary that governs how the format may
evolve:
- A breaking change to the bundle schema or to the signature payload bumps
versionto a new integer. A bundle carrying a differentversionis a different format, and a verifier written for1should not attempt to parse or verify it. - Non-breaking additions do NOT bump
version. New type names, new witness names, and new optional fields can all appear within version 1, because the registries only ever claim unused names and never renumber, reuse, or rename an existing entry.
Unfamiliar input comes in three kinds, handled three different ways, and a verifier must not merge them:
- An unrecognized
typeis a hard rejection (invalid_subject_type).typeselects the subject’s shape (whethersubjectandinclusion_proofare present) and the byte-prefix family used to derive the subject hash. A verifier that has never seen the name cannot derive the subject hash at all, so “a bundle whose signed inputs still verify” is not a state it can reach. It must reject before producing any step results, must not infer a shape from whichever keys happen to be present, and in particular must NOT treat an unknown type as block-like. - An unrecognized witness name is a visible skip. The fingerprint is derived by hashing the metadata map exactly as carried, so a name the verifier does not know changes none of its arithmetic and can never fail a proof.
- An unrecognized optional field inside a known structure is ignored. A field a verifier never reads cannot change a digest it does compute; a verifier that surfaces it should do so as a skip or an informational result, never as a failure.
See verify a proof for how each outcome is reported.
Example (JSON, item subject)
An item bundle committed to both Stellar and Bitcoin, carrying all four witnesses and a signing key event. Hash, key, and proof values are truncated with an ellipsis for readability; on the wire they are full-length hex or base64 strings.
{
"version": 1,
"type": "item",
"generated_at": "2026-07-24T12:00:00Z",
"public_key": "IVL40Zt5HSRFMkLhXy6rbLfP…",
"signature": "BfZua6JEboOJUVHvC/IcEfzBQazK…",
"subject": {
"id": "01KY9ZWEX0248J48HK6D248NAN",
"claims": {
"name": "Appendix D worked example",
"hash": "b47cc0f104b62d4c7c30bcd6…",
"hash_type": "sha256"
},
"metadata": {
"witnesses": {
"block": "f6912793a49f289affc65a95…",
"entropy_bitcoin": "e358c850958e1f1e8ad4c60c…",
"entropy_nist": "0a5eb1a1721a463924404929…",
"entropy_stellar": "8a7e002361b83a4e05e09150…"
}
},
"signing_key_id": "f2c39df9",
"witnesses": {
"block": {
"id": "019f93fd-c670-7001-8000-000000000001",
"previous_block_hash": "a3b8c9d0e1f2a3b4c5d6e7f8…",
"merkle_root": "dcbf10256899ea68cbe0c112…",
"metadata": {},
"signing_key_id": "f2c39df9"
},
"entropy_stellar": {
"closed_at": "2026-07-24T11:58:10Z",
"hash": "9c9c9c9c9c9c9c9c9c9c9c9c…",
"sequence": 51234501
},
"entropy_nist": {
"pulse": {
"chainIndex": 1,
"pulseIndex": 2847559,
"outputValue": "abababababababababababab…",
"timeStamp": "2026-07-24T11:58:00.000Z",
"version": "2.0"
}
},
"entropy_bitcoin": {
"hash": "cdcdcdcdcdcdcdcdcdcdcdcd…",
"height": 870455,
"time": 1784894100
}
}
},
"inclusion_proof": "AgEBpL3mm9F5EfSMUULiCpl0Zlv-…",
"block": {
"id": "019f93ff-2600-7c30-8000-000000000c30",
"previous_block_hash": "f6912793a49f289affc65a95…",
"merkle_root": "7c40e8809aafdbc946bdc514…",
"metadata": {},
"signing_key_id": "f2c39df9"
},
"commitments": [
{
"chain": "stellar",
"epoch_merkle_root": "f5c2df8d5fdc24b0aef14c7a…",
"epoch_proof": "AgIjXul3dz0rfPIe4Aocilj…",
"transaction_hash": "5e5e5e5e5e5e5e5e5e5e5e5e…",
"network": "public",
"timestamp": "2026-07-24T12:00:12Z",
"ledger": 51234567
},
{
"chain": "bitcoin",
"epoch_merkle_root": "d1526bd295f222761a81bef1…",
"epoch_proof": "AQFI0ilW7XcA0GN7vG-R5V7…",
"transaction_hash": "7a7a7a7a7a7a7a7a7a7a7a7a…",
"network": "mainnet",
"timestamp": "2026-07-24T12:40:00Z",
"block_height": 870456
}
],
"signing_key_event": {
"block": {
"id": "019f93c8-3780-70a0-8000-0000000000a0",
"previous_block_hash": "96118892a96ced7df9875cca…",
"merkle_root": "e3b0c44298fc1c149afbf4c8…",
"metadata": {
"key_event": {
"type": "genesis",
"key_id": "f2c39df9",
"sequence": 0,
"public_key": "IVL40Zt5HSRFMkLhXy6rbLfP…",
"prerotation_commitment": null
}
},
"signing_key_id": "f2c39df9"
},
"commitments": [
{
"chain": "stellar",
"epoch_merkle_root": "5ee58a111980722fb7bf2cb4…",
"epoch_proof": "AQH-9oSs8vQTJbZXE4TSRmj…",
"transaction_hash": "3f3f3f3f3f3f3f3f3f3f3f3f…",
"network": "public",
"timestamp": "2026-07-24T11:00:14Z",
"ledger": 51230004
}
]
}
}
The compact variant of the same bundle drops subject.witnesses,
signing_key_event, and block_path, and keeps everything else, including
subject.metadata. A block-like bundle drops subject and inclusion_proof
entirely and keeps version, type, generated_at, public_key, signature,
block, and commitments.
Citations
- RFC 8949: Concise Binary Object Representation (CBOR). Defines the CBOR encoding and the self-describing tag
55799used by the compact binary bundle. - RFC 8785: JSON Canonicalization Scheme (JCS). Canonical byte form of every hashed map: subject data, metadata, and witness payloads.
- RFC 6962: Certificate Transparency. Source of the Merkle leaf and internal-node hashing the inclusion and epoch proofs walk.
- RFC 8610: Concise Data Definition Language (CDDL). The schema language Truestamp uses to specify and validate the CBOR wire form.
- Beacon API
- Bitcoin
- Block hash
- Byte-Prefix Registry
- CBOR
- Claims
- Compact Merkle Proof Encoding
- Compact proof encoding
- Ed25519 signature
- Ed25519 Signatures
- Entropy source
- Epoch root
- External Commitment
- JCS (JSON Canonicalization Scheme)
- JSON:API HTTP Surface
- MCP Server for LLM Agents
- Metadata hash
- Offline verification
- OpenTimestamps
- Proof bundle
- Proof Claims by Availability
- Public Entropy Beacons
- RFC 8785: JSON Canonicalization Scheme (JCS)
- Signing-key ID (kid)
- Stellar
- Submission Window
- t type code
- The Proof Lifecycle
- Truestamp CLI
- Verify a Proof
- Witness