Three pages on Arweave that read the ar.io gateways' signed index publications in the browser, verifying every byte before it is used, and the library under them.
ArDrive is the worked example, not the point. An L2 index is a selection
of ANS-104 data items by one tag (for ardrive-l2, App-Name starting with
ArDrive); any gateway operator can publish one for any app, and everything
here but the ArFS layer and the drive browser is generic. The recipe from a
selection to a verified index and a page is
docs/new-l2-index.md.
| package | what | name |
|---|---|---|
packages/index-client |
the verifiable index client: publication verification, range reads checked against BitTorrent v2 hash trees, the pinned DuckDB-WASM engine, the parquet-l1, parquet-l2 and cdb64 layouts, the cost model |
@ar-io/index-client |
packages/ui |
design tokens (ArDrive and ar.io brands), primitives, icons | @ar-io/explorer-ui |
apps/ardrive-explorer |
every drive, folder and file on ArDrive, over ardrive-l2 (the example selection) |
explore_ardrive.ar.io |
apps/arweave-l1-explorer |
SQL and lookups over Arweave's base layer, over parquet-l1 |
arweave-l1-search.ar.io |
apps/network-index |
every index a gateway publishes, verified, with a CDB64 lookup | cdb64.ar.io |
The index formats are specified in
ar-io/ar-io-node docs/
(index-publication.md, cdb64-format.md, index-swarm.md). The
performance budget and the lessons the code keeps are in
CONTRIBUTING.md; how to report a vulnerability is in
SECURITY.md.
- Using the indexes from your own script or page:
packages/index-client/README.md(the four steps every use takes, and what each layout lets you query) and the runnableexamples/, from Node:pnpm example:lookup <data-item-id>resolves a data item to its root transaction through the CDB64 index;pnpm example:query l1runs SQL over aparquet-l1band;pnpm example:query ardrivefinds the L2 index that selects ArDrive's data items and runs SQL over a band of it.
- Pages built on it:
apps/network-index(the smallest: verification and a CDB64 lookup),apps/arweave-l1-explorer(SQL overparquet-l1in the browser) andapps/ardrive-explorer(an L2 selection with its own layer). Each keeps its whole flow insrc/session.ts. - Indexing another app the way ArDrive is indexed:
docs/new-l2-index.md(and thenew-l2-explorerskill in.claude/skills/). - Working on the repo:
CONTRIBUTING.md; agents:CLAUDE.md.
- The publication (
/ar-io/indexes) is Ed25519-signed by the gateway; the key is checked against the ar.io registry through a different gateway (packages/index-client/src/publication.ts). - Band files are read in byte ranges through a service worker that checks
every piece against the BitTorrent v2 hash tree whose root the signed
publication pins (
src/range/). Nothing unverified reaches the engine. A script that reads a file whole checks it against the SHA-256 the publication signs for it instead (examples/query-band.ts). - DuckDB-WASM and its worker are pinned by SHA-256 in
engine.lock.json; the Parquet extension by DuckDB's own signature.pnpm pins:checkproves the pins against npm and two gateways. - Names (ArDrive record JSON) are fetched from a gateway, are not covered by the signature, and are labelled so; each is size-checked against the index.
pnpm install
pnpm test # Vitest: the client (with native DuckDB over a recorded band) and the apps' models
node scripts/fetch-engine.mjs # caches the pinned Parquet extension for the fixture gateway
pnpm --filter ./apps/ardrive-explorer run build:e2e
pnpm e2e # Playwright against the local fixture gateway (no network)
pnpm e2e:live # the same suites against turbo-gateway.com
pnpm --filter ./apps/ardrive-explorer run devfixtures/live holds a recorded publication (turbo-gateway.com, seq 159),
its registry, one small ardrive-l2 band and its torrent. fixtures/synthetic
is generated by pnpm fixtures:build: a publication signed with a test key
over two ardrive-l2 bands (a 20,000-file drive, a wide drive, a private
drive, versions, a hidden file), an L1 band and a root-tx band, with every
record's JSON. tools/fixture-gateway serves either, with Range, torrents,
/ar-io/peers, /raw and /graphql.
Never run the suites on a gateway: a gateway blocks its own public IP on
/raw, so names fail there and nothing else is representative.
An app ships only as the exact bytes that were fully verified on a developer machine, and only from a tag.
-
Verify locally. On a clean checkout of the commit to ship:
node scripts/release.mjs verify network-index --version 1.0.0
It installs, checks the engine pins against two gateways, lints, runs every unit test, the app's E2E specs on the fixture gateway, builds for production, and runs the app's specs live against turbo-gateway.com. Every step must pass. It then writes
releases/<app>.json: the git tree id of every build input and the SHA-256 of every built file. -
Commit the record, merge it, and tag that commit on main:
network-index-v1.0.0,arweave-l1-explorer-v1.0.0orardrive-explorer-v1.0.0. -
The Deploy workflow builds again and refuses to go on unless the build inputs and every built byte match the record. It runs the app's live specs again, then ario-deploy uploads
dist/with Turbo, gzip-compressed and deduplicated against earlier uploads, and sets the ArNS record (cdb64,arweave-l1-search, orexploreunderardrive). It then fetches every file back through the new manifest and compares it with the record, waits for the name to resolve to it, and runs the app's smoke spec against the deployed page. The manifest id, the name's previous target (the rollback) and every file's SHA-256 go intoDEPLOYS.md.
Each name's controller key lives in its own GitHub environment (arweave
for cdb64 and arweave-l1-search, ardrive for ardrive), with a Solana
upload key beside it; the addresses are in .github/workflows/deploy.yml.
Nothing deploys from a developer machine.
AGPL-3.0-or-later (see LICENSE), matching ar-io/ar-io-node, whose index formats this implements.