PERMADATA/get
X1 MAINNET

← Back to download page

Downloading a Permadata stamp

A Permadata v2 stamp is a permanent, sealed record of a file on the X1 chain. This doc is the practical guide to pulling a stamped file back out of the chain into a working local file (notld.mp4), then proving it is the exact bytes that were stamped.

1. What a stamp is, and where the numbers live

A stamp is a 512-byte account owned by the Permadata program MoViekvxc6uK6yDBf2XidPPph5ewU4XVWj2LKD4Zk3w. For files larger than ~10 KB it uses the Chunked tier: the file is split into 900-byte chunks, and each chunk is stored as one append_chunk transaction (nothing is written to account state — the transaction history is the storage). A 1.86 GB film is 2,069,456 chunks × 900 B = 2,069,456 transactions.

The fields you care about, at their byte offsets inside the account data:

fieldoffsetwidthmeaning
chunk_size143u16 LEbytes per chunk (900)
chunk_count145u32 LEnumber of chunks (2,069,456)
size_bytes153u64 LEtotal file size (1,862,510,336)
content_hash16132 BSHA-256 of the whole file — the canonical identity
chunk_root_hash19332 BMerkle root over the chunk hashes

content_hash is the one that matters most: the stamp PDA itself is derived from it, and it is what ties “these exact bytes” to the on-chain record. chunk_root_hash is a secondary integrity structure you can check if you want to prove every chunk.

For stamp ALuMA6zrW9NWGACyK7SbExwyWteAeDz9YEANFkizNUU4 (the public-domain film Night of the Living Dead):

chunk_size = 900 chunk_count = 2,069,456 size_bytes = 1,862,510,336 (≈ 1.73 GiB) content_hash = 8d5f6dd2bc41ab9b16db69b182beccddef1547da2dba6e3a3301da092da3672d merkle_root = 58fbca5656decbd3beb675000fd034862f0087e2f121e07cba75b239c9953ba9

2. The tool: permadata-get

Single-file, zero-dependency Node CLI. Run it anywhere Node ≥ 18 exists:

node tools/downloader/permadata-get.mjs <stamp> -o notld.mp4 # or, symlinked/on PATH as `permadata-get`: permadata-get <stamp> -o notld.mp4

It enumerates the stamp’s transaction history, fetches those transactions in batched JSON-RPC requests, extracts each chunk (verifying its committed hash), and writes each chunk to its byte offset — so it never holds the file in memory. Progress lives in a sidecar file; an interrupted run resumes without re-fetching.

permadata-get <stamp> -o notld.mp4 \ --rpc https://rpc.mainnet.x1.xyz \ --batch-size 80 --concurrency 8 # verify an existing file against a stamp (no network download): permadata-get --verify-only notld.mp4 <stamp> # stop early (testing): permadata-get <stamp> -o notld.mp4 --max-chunks 50000

Flags:

flagdefaultmeaning
-o, --out—destination file (required)
--rpchttps://rpc.mainnet.x1.xyzJSON-RPC endpoint
--api-key$X1_API_KEYAPI key, sent as an X-API-Key header
--batch-size80getTransaction calls per HTTP request (max ~85 on the public RPC)
--concurrency8parallel batch requests
--state.permadata-get-statesidecar progress dir
--max-chunks—stop after N chunks
--no-merkle—skip the Merkle-root check
--verify-only—verify <file> against <stamp>, exit non-zero on mismatch

On completion it prints RESULT: PASS (exit 0) or RESULT: FAIL (exit 1).

3. Downloading with the public RPC (and its real limits)

https://rpc.mainnet.x1.xyz is the free, unauthenticated RPC. It works for full downloads, with measured limits (2026-10-19, from the NOTLD run):

batch sizeresultchunks/s (1 request at a time)
10OK~194
25OK~415
50OK~443
80OK—
85OK (max)—
88 / 100 / 200HTTP 413 (payload too large)—

Max usable batch ≈ 85. With --batch-size 80 --concurrency 8 the sustained rate is ~930 chunks/s ≈ 0.80 MB/s — the full 1.86 GB NOTLD film takes ~37 minutes. The ceiling is the public RPC’s request rate, not our code: each 80-call batch takes ~0.7 s server-side, and ~12 concurrent batch requests/s is what the free tier will sustain before it starts rate-limiting (429).

Bottom line: the free public RPC can download a film, but it is ~0.8 MB/s. For speed, use an API key (below) or your own node (below).

4. Downloading with an x1scroll API key (fast path)

A keyed endpoint raises the request budget, which is the only thing capping throughput. When it ships, point the tool at it:

export X1_API_KEY="<your key>" permadata-get <stamp> -o notld.mp4 \ --rpc https://rpc.x1scroll.io \ --api-key "$X1_API_KEY"

The key is sent as an X-API-Key header. The exact endpoint URL and the rate limit are still being finalized — this is a placeholder. Contact support@x1scroll.io (placeholder) for key issuance. The tool already implements 429/Retry-After backoff and automatic batch splitting on 413, so it degrades gracefully on any endpoint.

5. Pointing the tool at your own node

Any X1 node with transaction history enabled works:

permadata-get <stamp> -o notld.mp4 --rpc http://127.0.0.1:8899 # or over an SSH tunnel to a node that only listens locally: # ssh -L 8899:127.0.0.1:8899 user@node then use --rpc http://127.0.0.1:8899

Note: a validator running with transaction history disabled (common, to save disk) will answer Transaction history is not available from this node for getTransaction. That node can’t serve downloads — use an archival/indexer node or the public RPC.

6. Verifying a download independently (no Permadata tool)

You do not need our tool to prove a file is authentic. Two independent checks:

SHA-256 of the whole file (the canonical identity):

sha256sum notld.mp4 # must equal the stamp's content_hash: # 8d5f6dd2bc41ab9b16db69b182beccddef1547da2dba6e3a3301da092da3672d

Merkle root over the 900-byte chunks (leaf = sha256(0x00 ‖ u32le(seq) ‖ chunk), node = sha256(0x01 ‖ left ‖ right), odd nodes promoted, no duplication):

python3 - <<'PY' import hashlib, sys data = open(sys.argv[1], 'rb').read() def h(b): return hashlib.sha256(b).digest() level = [] for seq in range(0, len(data), 900): chunk = data[seq:seq+900] level.append(h(b'\x00' + seq.to_bytes(4,'little') + chunk)) while len(level) > 1: nxt = [] for i in range(0, len(level)-1, 2): nxt.append(h(b'\x01' + level[i] + level[i+1])) if len(level) % 2: nxt.append(level[-1]) level = nxt print(level[0].hex()) PY # must equal the stamp's chunk_root_hash: # 58fbca5656decbd3beb675000fd034862f0087e2f121e07cba75b239c9953ba9

(seq is the chunk index; the byte offset of chunk seq is seq * 900. The last chunk is short — 836 bytes — which the loop above handles naturally.)

The content hash is also the seed of the stamp’s PDA, so you can cross-check the address itself: findProgramAddress(["pm_stamp", content_hash], program) == stamp.

7. Troubleshooting

8. Worked example — NOTLD

# one command: download + verify bash examples/fetch-notld.sh # or by hand: permadata-get ALuMA6zrW9NWGACyK7SbExwyWteAeDz9YEANFkizNUU4 -o notld.mp4 # …prints RESULT: PASS (exit 0), sha256 = 8d5f6dd2…3672d sha256sum notld.mp4 # 8d5f6dd2bc41ab9b16db69b182beccddef1547da2dba6e3a3301da092da3672d notld.mp4

Measured on the public RPC: 2,069,456 chunks in ~37 min at ~0.80 MB/s, peak RSS ~480 MB (the signature index, not the file — the file is never held in memory), SHA-256 and Merkle root both PASS.