9 min read
How to Verify a Label 309 Record
Verify a Label 309 proof from the public Cardano chain, the record bytes, and optional content or recipient keys — without trusting CardanoWall or whoever published it.

You verify a Label 309 record straight from the public Cardano chain, starting with one input: a Cardano transaction reference. Nothing in the answer depends on CardanoWall, on whoever published the record, or on any server still being online.
A verifier fetches the raw transaction bytes from public Cardano infrastructure, extracts metadata label 309, reassembles the record body, checks the canonical CBOR and schema, verifies any authorship signatures, and — when the content bytes or an encrypted payload are available — recomputes the hashes. The whole check is a sequence of cryptographic comparisons, not a request to a trusted service.
The gateway that published the record is not the authority. The record is. That is the point of the standard: a proof you check once stays checkable by anyone, forever, with any conformant Label 309 tooling.
What do you need to verify?
The minimum input is a Cardano transaction hash.
With only that transaction hash, a verifier can answer:
- is there a Label 309 record in this transaction?
- is the record structurally valid?
- is the transaction settled deeply enough?
- do the record-level signatures verify, if signatures are present?
- what hashes, storage URIs, Merkle roots, and sealed envelopes does the record claim?
To prove that a specific file is the file behind the hash, the verifier also needs the file bytes or an attributable storage object referenced by the record.
To decrypt a sealed record, the verifier needs the recipient's private key. That key is used locally and is never sent back to the publisher or to CardanoWall — the whole construction is designed so verification never phones home.
What does a verifier actually check?
A good verifier should check the record in layers.
The first layer is the structural validator. This is a pure function over the record bytes. It performs no network calls, no decryption, and no signature verification. It checks the canonical CBOR, field types, closed schema, registered algorithm names, URI rules, and cross-field constraints such as "there must be at least one item or one Merkle commitment."
The second layer is the public verifier. It resolves the transaction from a Cardano data source chosen by the verifier, binds the fetched bytes to the transaction hash, extracts label 309, checks confirmation depth, and verifies record signatures. This is the layer a public audit, CI job, or explorer can run.
The third layer is the recipient verifier. It does everything the public verifier does, then tries to decrypt sealed items with the recipient's local key and recomputes plaintext hashes after decryption.
That layering matters because not every proof needs every check. A hash-only record can still be valid even when it has no encrypted payload. A public verifier can validate a signed record without being able to decrypt a sealed file.
Why verify from raw transaction bytes, not a JSON view?
Label 309 verification works from raw chain data, not from a convenient JSON projection — and the difference is not cosmetic.
The record is canonical CBOR. Signatures cover byte-exact canonical CBOR. Cardano transaction metadata has byte strings, text strings, arrays, maps, and ordering rules that JSON cannot preserve without loss.
So a verifier should fetch the raw transaction CBOR, recompute the transaction id, bind auxiliary data to the transaction body, then reassemble the label-309 chunk array into the original record body.
An explorer can be useful infrastructure. It should not become the thing you trust. The verifier should treat explorer responses as data to bind, check, and cross-check.
What is inside a label 309 record?
A Label 309 record is carried under Cardano transaction metadata label 309.
The on-chain value is not a loose JSON object. The record body is serialized once as canonical CBOR, then transported as an array of byte-string chunks because Cardano metadata byte strings and text strings are capped at 64 bytes.
After reassembly, the record body is a single CBOR map. Its top-level keys are:
v, the schema version (currently1);items, an optional array of per-content commitments — each item carries a requiredhashesmap, and optionally its ownurislist for content-addressed storage (ar://,ipfs://) and anencenvelope for sealed content;merkle, optional list commitments that bind one on-chain root to an off-chain leaf list — the way large batches are anchored;sigs, optional record-level authorship signatures;supersedes, an optional append-only pointer to an earlier record;critplus namespaced extension keys, for forward-compatible additions.
A conformant record must commit to at least one items entry or one merkle
commitment. The storage URIs and the sealed envelope live inside an item, not at
the top level — a detail worth getting right when you parse the record yourself.
The core claim is always content-first: these hashes, or this Merkle root, were anchored in this transaction no later than the block time.
What verdicts should automation expect?
Verification should not collapse every problem into "valid" or "invalid."
The Label 309 verifier model uses four machine verdicts:
valid: every required check passed.failed: the record itself failed a structural, signature, or attributable integrity check.unverifiable: a required check could not be completed for reasons not attributable to the record, such as unavailable infrastructure or content that could not be fetched.pending: the transaction is on chain but below the verifier's confirmation threshold.
That split is important in real systems.
If a storage gateway is down, the record should not be condemned. If fetched bytes are attributable to a committed URI and their hash does not match, the record should fail. If the transaction has only one confirmation, the verifier should say it is pending instead of pretending finality has already happened.
The open-source cardanowall CLI maps those states straight onto exit codes, so
the verdict survives into a shell script without parsing any JSON:
cardanowall verify <tx-hash> --json| Exit code | Verdict | Meaning |
|---|---|---|
0 | valid | every required check passed |
1 | failed | a record-attributable check failed (integrity, structure, or signature) |
2 | unverifiable | no record fault, but a required check could not run (network or policy) |
3 | pending | not enough confirmations yet — no result from a pending record is final |
4 | — | CLI input error (bad arguments or missing required input) |
That split is exactly what you want in continuous integration: the script can tell "the proof is bad" (exit 1, and never manufacturable by a misbehaving explorer) from "try again later" (exit 2). The same verifier ships in the TypeScript, Python, and Rust SDKs, so an application can read the full structured report instead of an exit code. For the patterns around wiring this into a pipeline, see using the CLI in automation.
How do you verify a file?
For a simple file proof, the workflow is:
- Take the transaction hash.
- Fetch and verify the Label 309 record.
- Identify the committed hash algorithm and digest.
- Hash the file bytes locally.
- Compare your digest to the digest in the record.
- Check confirmation depth and block time.
- Check any record signatures if they are present.
If the file differs by one byte, the hash will differ.
That is the strength of a content-hash proof. It does not prove that the file is true, legal, original, or valuable. It proves that the exact bytes matching the hash existed no later than the anchored block time, subject to the verifier's finality policy.
How do you verify a sealed record?
A sealed record adds encrypted content.
The public record still commits to the plaintext hash. The ciphertext can be stored off-chain, for example through Arweave or IPFS. The on-chain envelope contains the information needed for an intended recipient to recover the content key locally.
The recipient verifier should:
- Run the public verification path first.
- Try the supplied private key against the sealed slots.
- If a slot opens, decrypt the ciphertext.
- Recompute the plaintext hash.
- Compare it to the on-chain hash commitment.
That final hash check is the bridge between "I decrypted something" and "this is the exact content that was timestamped." Decrypting the bytes is not enough on its own; the recomputed plaintext hash has to match the commitment the record made before anything was encrypted.
The recipient key stays local. Verification needs no trusted CardanoWall account, no issuer server, and no callback to the person who published the record. A sealed record keeps its plaintext confidential to the key holders — but note its limits: it does not guarantee anonymity, and once a recipient decrypts the content they can do whatever they like with the plaintext.
How do you verify a Merkle commitment?
Merkle commitments are for batches.
Instead of publishing thousands of hashes directly in one record, a producer can publish one Merkle root and keep the ordered leaf list and inclusion proofs off-chain.
To verify one item from the batch:
- Hash the item or obtain the committed leaf digest.
- Verify the inclusion proof against the Merkle root.
- Check that the root and
leaf_countare in the Label 309 record. - Verify the Cardano transaction and confirmation depth.
The root is not enough by itself. The producer or archive must preserve the leaf list and inclusion proofs. Label 309 gives the batch a public anchor; the operational evidence around the batch still needs to be kept. This is how one record proves thousands of files at a fixed on-chain cost.
What should not be trusted?
Do not trust the publisher's website as the source of truth.
Do not trust a screenshot of a transaction.
Do not trust a JSON explorer view as the cryptographic input.
Do not trust a storage gateway merely because it returned bytes.
Do not trust "verified" badges that do not say what was checked.
The verifier should be able to produce a report: transaction hash, Cardano data sources used, confirmation depth, block time, record digest, signature results, content hash results, storage fetch results, Merkle checks, and any skipped checks.
That report is what makes the proof usable in audits and automation.
What does verification not prove?
Verification is precise. It should not be oversold.
A valid Label 309 record does not, by itself, prove:
- legal ownership of the content, or exclusive rights to it;
- that the content is true;
- that the publisher had permission to publish it;
- that a real-world person controls a signing key;
- that a court, regulator, or customer will accept the proof without supporting evidence — admissibility depends on jurisdiction, and a proof can support a case but does not replace counsel;
- that off-chain storage will remain available forever, unless storage durability is separately maintained.
It proves the claim the record actually makes: a hash or Merkle root was anchored on Cardano, any signatures over the record verified, and any content or decryption checks matched the commitments. In other words, it establishes timing and integrity — not truth, ownership, or rights.
That narrower claim is exactly what makes it useful. For the full boundary of what a timestamp can and cannot do, see what a proof does not prove.
Further reading
- The Label 309 standard, including the full verification pipeline, the verdict states, and the typed error catalogue: label309.org.
- The open-source verifier — the
cardanowallCLI plus the TypeScript, Python, and Rust SDKs that all run the same checks: github.com/cardanowall. - Label 309 is submitted to the Cardano CIP process and under review by the CIP editors as a Metadata-category proposal: the open pull request.