Verify

Trust, and verify.

Check a block, its transactions and the state it wrote against the chain itself. No trust in secondlayer, or in any node that served the bytes.

~/app · example output
$secondlayer verify block 9137005
✓ block 9,137,005 2e5962b0…92f57ff
✓ bitcoin headers 967,680..970,269, proof of work checked
✓ burn height 970,269
✓ cycle 144, signer set proven from cycle 143
✓ signatures 2,866 / 4,000 weight, threshold 2,800
✓ txs 2 transactions match the tx merkle root
✓ state root 8103be4b…191f0cd5 matches the witness
✓ diff 18 written, 1 carried, 5 internal
· rows not checked (pass --rows)
✓ Block 9,137,005 is proven. Trusted: only the checkpoint.
Try it

Check a block in your browser.

Runs on this page against the public proofs API. No key, no account.

The same check as secondlayer verify block.

The chain of trust

One command walks every link, from Bitcoin to your row.

A lying source can make a link fail. It can never make one pass.

CheckpointBitcoin headersSigner setStacks blockState rootYour rowtrusted as-isproof of work≥ 70% of weight signedtransaction rootwitness + MARFmatches a proven leafStacks 8,956,304example checkpoint · pass --checkpoint to use your ownCheckpointBitcoin headersSigner setStacks blockState rootYour rowStacks 8,956,304 · trusted as-isproof of work≥ 70% of weight signedtransaction rootwitness + MARFmatches a proven leaf
Below the checkpoint, the block hash-chains to it: parent links, or one MARF proof.
What you can check today

Every answer we can prove ships with its proof.

A hash, a merkle path, a signature, or a replay you run yourself. Each one names what you still trust.

Block

Any height, with its transactions and state

secondlayer verify block <height>

Still trusted: the checkpoint in the release

Transaction

It's in the block, and the signers signed it

GET /v1/index/transactions/:id/proof

Still trusted: the signer set, unless you pass your node's

State writes

Every key a block wrote, named and proven

GET /v1/proofs/writes/:height

Still trusted: the checkpoint

Archive

Your history against the signed manifest

secondlayer verify raw --against latest.json

Still trusted: the archive signing key. Checking is free; repair is paid.

Subgraph rows

Recompute a state subgraph and compare

secondlayer verify subgraph <name> --replay

Still trusted: the checkpoint. State subgraphs only.

Deliveries

Streams frames and webhooks are signed

verifyWebhookSignature(body, headers)

Still trusted: the key. Proves who sent it, not that the chain agrees.

What it does not cover yet

A check that passes leaves you trusting a smaller, named thing.

A check that can't run says so, and is never a pass.

Print eventsStacks headers don't commit to them, so no block check covers them.
Transaction resultsSame reason. Whether a call succeeded isn't in the header.
Which transaction made a writeThe transaction root binds each transaction's bytes, not which one wrote a leaf.
Bitcoin reorgsHeaders are checked for valid work, not against a competing chain.
Other subgraphsOnly state subgraphs replay. Event subgraphs don't, yet.
What you still trust
example checkpoint · Stacks 8,956,304archive signing keywebhook signing key
How far it goes

Each step trusts less than the one before.

01

Decoded

Every Stacks block, from genesis, delivered at tip.

02

Signed

Archive manifests, Streams frames and webhooks carry ed25519 signatures.

03today

Checkable

Blocks, state writes, state-subgraph rows and Bitcoin transactions prove against the chain.

04 · coming

Every write, named

Inner contract calls and writes that never print, with the transaction that made them.

In your code

The same check, as a library.

Gate a payout, a bridge release or an alert on a block you proved yourself.

  • Never throws on bad data. Returns ok: false and the first broken link.
  • Bring your own node. Blocks and MARF proofs come from its RPC; the rest is checked the same way.
  • Runs anywhere. fetch and pure-JS crypto, no Node built-ins.
  • Proofs are never billed. No key needed; add one for higher rate limits.
verify.tsts
1import {
2 SecondlayerProofSource,
3 verifyBlock,
4} from "@secondlayer/verify";
5
6// No key needed; add apiKey for higher rate limits.
7const source = new SecondlayerProofSource({
8 baseUrl: "https://api.secondlayer.tools",
9});
10
11const result = await verifyBlock(9_137_005, { source });
12
13if (!result.ok) {
14 throw new Error(result.failures[0]?.message); // first broken link
15}
16console.log(result.stateRoot, result.diff?.writes.length);

6proofs are never billed

11proven on your machine

13fails with the first broken link

Proofs API

The raw material, free.

What verify block reads. Fetch it yourself and check it however you like.

RouteReturns
/v1/proofs/block/height/:heightSigned block by height
/v1/proofs/witness/:index_block_hashState witness the header's state root must hash to
/v1/proofs/writes/:heightEvery leaf the block wrote, named
/v1/proofs/marf/:pathMARF inclusion proof for a key at a block
/v1/proofs/burn/:consensus_hashBurn block a consensus hash commits to
/v1/proofs/bitcoin-headersRaw Bitcoin headers from a height
/v1/proofs/bitcoin-tx/:txidBitcoin transaction, merkle branch and header
10 requests/s per IP (20 with a key)1 request/s for witnesses (2 with a key)self-host: needs the proof sidecar

Check a block before you trust it.