Guides

Guides · Part 3 of 8

Verify a record

Verification is the half of Label 309 that asks nobody's permission. Given a Cardano transaction reference, you can confirm a record yourself — against the public chain, through an explorer you choose. There is no login, and nothing about the result depends on whoever published it.

With the CLI

Install the cardanowall binary, then point it at a transaction:

cardanowall verify 3b9f…c1a2

It resolves the transaction through a public Cardano explorer, structurally validates the record, checks any authorship signatures, confirms the record is settled, and prints a verdict. The verdict is one of four states, and the exit code carries it through verbatim — so it drops straight into CI:

Exit codeVerdictMeaning
0validevery required check passed
1faileda record-attributable check failed (integrity, structure, or signature)
2unverifiableno record fault, but a required check could not run (network or policy)
3pendingnot enough confirmations yet — no result from a pending record is final
4CLI input error (bad arguments or missing required input)

The split between failed and unverifiable is deliberate: a failed verdict is always attributable to the record itself, so no misbehaving explorer can manufacture one; unverifiable means the verifier could not complete a check for reasons outside the record — a gateway it couldn't reach, a deny-listed host, or a fetch ceiling.

Add --json for a machine-readable report, and keep every network hop under your control by pointing at your own infrastructure:

cardanowall verify 3b9f…c1a2 \
  --cardano-gateway https://your-koios-instance/api/v1 \
  --threshold 20 \
  --json

If the record is sealed to you, hand the verifier your key and it will decrypt the payload and recompute the plaintext hash:

cardanowall verify 3b9f…c1a2 --secret-key-stdin

Prefer --secret-key-stdin, --secret-key-file, or the CARDANOWALL_RECIPIENT_KEY environment variable over a bare --secret-key flag, so the key never lands in your shell history.

Verify without the network

You can also check a record you hold as a file, with no transaction and no explorer, by pointing --record at its bytes (raw CBOR or its hex encoding):

cardanowall verify --record ./record.cbor

This runs the same structural, signature, and content checks over the supplied bytes: a producer's pre-submission check ("is the record I am about to publish well-formed and correctly signed?") or an archival re-validation. With no transaction there is nothing to say about the chain, so the report omits block time and confirmations unless you assert them yourself with --block-time, --slot, and --confirmations.

For a record anchored on the preprod test network rather than mainnet, add --network preprod to any of the on-chain commands above.

With the TypeScript SDK

The verifier the CLI uses ships in the SDK. It needs no client, no base URL, and no key — verification is gateway-agnostic:

import { verifyTx } from '@cardanowall/sdk-ts/verifier';

const report = await verifyTx({ txHash: '3b9f…c1a2' });

console.log(report.verdict); // 'valid' | 'pending' | 'unverifiable' | 'failed'

if (report.verdict !== 'valid') {
  console.error(report.issues, report.signatures);
}

verifyTx returns a full VerifyReport: the verdict and exitCode, the resolved block_time and confirmationDepth, the structural-and-verifier issues list, the per-item items and merkle check results, the optional signatures, and an auditTrail log of every outbound request it made — so you can prove to yourself it never contacted the publisher.

To verify a record sealed to you, hand the verifier your secret key. The keyring is global to the run — every key you pass is tried against every sealed item, so there is no per-item index:

const report = await verifyTx({
  txHash: '3b9f…c1a2',
  decryption: [{ recipientSecretKey: mySecretKey }],
});

With the Python SDK

cardanowall-sdk is a byte-for-byte twin of the TypeScript verifier — the same checks, the same verdict:

import asyncio
import cardanowall

report = asyncio.run(cardanowall.verify_tx(cardanowall.VerifyTxInput(tx_hash="3b9f…c1a2")))
print(report.verdict)

With the Rust SDK

The cardanowall crate carries the same verifier. verify_tx is synchronous — it owns a blocking transport — and returns the same VerifyReport:

use cardanowall::verifier::{verify_tx, Verdict, VerifyTxInput};

let report = verify_tx(&VerifyTxInput::new("3b9f…c1a2"));

println!("{}", report.verdict.as_str()); // "valid" | "pending" | "unverifiable" | "failed"

if report.verdict != Verdict::Valid {
    eprintln!("{:?}", report.issues);
    eprintln!("{:?}", report.record_signatures);
}

To verify a record sealed to you, attach your secret key. The keyring is global to the run — every key is tried against every sealed item, so a credential carries no item index. The input fields are public, so build on top of VerifyTxInput::new:

use cardanowall::verifier::{verify_tx, Decryption, VerifyTxInput};

let mut input = VerifyTxInput::new("3b9f…c1a2");
input.decryption = Some(vec![Decryption::Recipient {
    recipient_secret_key: my_secret_key, // Vec<u8>
}]);

let report = verify_tx(&input);

Why this needs no trust

Every check runs from public data and an explorer you choose. The people who published the record cannot influence the outcome — and if they disappeared tomorrow, your verification would still work exactly the same.