Ledger Scrolls
Build Your Own Reader

The most important reader is the one you write.

A format only deserves the word immutable if anyone can rebuild the media without asking permission — without this website, without BEACN, without anyone. The whole protocol is two chain queries and a hash. This page is everything you need to build a conforming reader in an afternoon, and to prove it's correct.

The algorithm (all of it)

pointer (txHash#ix) → fetch that output's inline datum [1 query] → decode 8 CBOR fields: contentType, codec, sizes, two SHA-256s, the ordered page-tx list → fetch metadata label 22025 for those txs [1 query] → concatenate the byte segments, in order → gunzip if codec says gzip → sha256(result) == the manifest's fingerprint, or you show NOTHING

Tiny files skip the pages: if the datum's top-level CBOR value is a byte string (or a constructor holding one) rather than a manifest, those bytes are the media — a Standard Scroll.

The golden rule of every conforming reader: never render bytes you could not verify.

The two queries, concretely

Free, keyless, any language. This is the entire chain interface:

# 1. the manifest (or Standard Scroll) datum
curl -X POST https://api.koios.rest/api/v1/utxo_info \
  -H 'Content-Type: application/json' \
  -d '{"_utxo_refs":["<txHash>#0"], "_extended":true}'
# → .inline_datum.bytes is CBOR: [2, contentType, codec, sizeDecoded,
#    sha256Decoded, sha256Encoded, [pageTxHashes…], next]

# 2. the pages it names (chain scrolls only)
curl -X POST https://api.koios.rest/api/v1/tx_metadata \
  -H 'Content-Type: application/json' \
  -d '{"_tx_hashes":["<page-tx-1>","<page-tx-2>"]}'
# → label "22025" of each: { v, i, n, sha, p: ["0x<64-byte segment>", …] }

# concatenate → gunzip → sha256sum — done.

Segment encodings vary by indexer — accept 0x-prefixed hex, bare hex, and {"bytes":"…"} objects. Byte strings over 64 bytes arrive as CBOR indefinite-length chunks; join them. Full normative rules: Protocol v1 · chain-format spec.

Reference readers to copy from

ReaderLanguageBest starting point for
the-reader (frozen) Browser JS, one file, zero depsWeb readers — this exact file is minted on-chain (below)
read_scroll.py Python, stdlib only, ~150 linesScripts, air-gapped verification
koios-viewer (lsview) Python packageFull tooling: every format, registry, catalogs
The Library Browser JS, one fileThe full experience: shelves, channels, trust log

The complete written guide — CBOR decoding, datum shapes, failure semantics, endpoint independence: docs/BUILD_A_READER.md.

Prove it — the conformance suite

The conformance suite is the contract: test vectors for hashing, deterministic gzip, both CBOR byte-string forms, segment variants, and pointer validity. Your thirty lines and ours can prove they agree:

git clone https://github.com/BEACNpool/ledger-scrolls
python3 ledger-scrolls/conformance/run_conformance.py   # or run_conformance.mjs

Then read something real — every live scroll carries its expected hash. If your reader reconstructs the Eternal Scroll to 65824f62…47dd2, it works.

Stay independent: a reader is only as free as its data source. Best: your own node + your own Koios instance. Good: any Koios-compatible provider, or a CORS mirror you deploy yourself in two minutes. Let the user pick the endpoint — an endpoint you don't control is a landlord.

The reader that reads itself

A complete, working reader is minted on Cardano as a scroll. Hand any conforming reader this pointer and you get back reader.html, byte for byte — a reader you can extract from the chain using nothing but the spec, which then reads every other scroll, including itself:

9a564165ebdc4e0c4a2e1163b5cf9355604ecb8e163b425d834570e5b9007de2#0

That loop is the whole point. If this repository, this website, and everyone who ever worked on this protocol disappear, the spec is two queries and a hash — and a working implementation is carved into the ledger it reads. The library holds its own pair of glasses. Receipts and the frozen source: examples/the-reader/.

Why we retired the hosted reader page: hosting a second viewer taught people to depend on our URL. The Library remains for browsing — but the reader's real home is the chain, and the real mission is a thousand independent readers. Build yours, and tell us about it.

Got a reader? Write something worth reading.

Price any file and mint small ones straight from your browser wallet — no node, no command line.

📜 Mint a scroll →