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)
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 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
| Reader | Language | Best starting point for |
|---|---|---|
| the-reader (frozen) | Browser JS, one file, zero deps | Web readers — this exact file is minted on-chain (below) |
| read_scroll.py | Python, stdlib only, ~150 lines | Scripts, air-gapped verification |
| koios-viewer (lsview) | Python package | Full tooling: every format, registry, catalogs |
| The Library | Browser JS, one file | The 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.
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/.
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 →