AArkiv SQLunknown
all entities

Read only SQL index for Arkiv

Ask live Arkiv data anything, in SQL.

Every live entity is loaded into DuckDB in this tab. Join, group and window them in milliseconds, with no 200 row pages.

Live entities
…
…
Distinct creators
…
…
Expiring within 1 hour
…
…
Created in the last hour
…
…

… per query, measured in this tab

How a row reaches this tab
  1. 1Arkiv chainTiramisu testnet
  2. 2Public logsReplayed with no API key
  3. 3IndexerSelf hosted, read only
  4. 4Parquet snapshot + deltaVersioned files
  5. 5DuckDB in your browser… per query above

Explore

Start from ready made views, or write your own query.

Playground

Your own SQL, run in this tab

Query entities and attributes with autocomplete, history, CSV export and a one click check of any row on chain.

SELECT creator, count(*) AS n
FROM entities
GROUP BY creator
ORDER BY n DESC
Open playground

Latest changes

The most recent creates, updates and deletions seen by this tab.

Loading…

Filters you can forge, queries you cannot write

Arkiv attributes are written by whoever creates the entity, and arkiv_query only filters and pages.

Who this protects

Anyone can create an entity with project = arkiv-sql-indexer-demo, so a dashboard that trusts that attribute counts forged rows as its own. $creator is set by the chain. Scope the index with $creator = addr(…) and look alikes never get in.

arkiv_query returns at most 200 results per page, with no JOIN, GROUP BY or ORDER BY on attributes. Here the same data sits in DuckDB in your tab, so those queries just work.

README.md, SPECS.md, packages/core/src/query.ts

From the chain to your tab

A self hosted indexer prepares files. Your browser runs the SQL and talks to Arkiv directly.

How it works

Architecture of Arkiv SQL IndexerThe Arkiv RPC on Tiramisu feeds a Bun indexer, which writes a DuckDB checkpoint, Parquet snapshots and a JSON delta. A Next.js proxy serves those files to the browser, which runs DuckDB WASM. The browser also talks to the Arkiv RPC directly, without an API key, for the live head, log tail, payloads and verify. An optional Bun demo writer sends SDK writes to the RPC.Demo writerBun and SDK, optionalArkiv RPC on Tiramisupublic endpoint, chain 7738577Docker ComposeBun indexerlog replay, attribute fill, sweepDuckDB checkpointindex.duckdbParquetversioned, keep 2JSON deltaper sync batchNext.js proxy/data with Range requestsYour browser: DuckDB WASMentities and attributes views, SQL, live taildirect, no API keylive headeth_getLogspayloadverify

A Bun indexer replays public eth_getLogs, fills attributes in pinned batches of at most 50 keys, and publishes versioned Parquet snapshots plus a JSON delta.

A Next.js proxy serves those files. DuckDB WASM loads them, and the tab polls the public RPC for new logs and goes to it directly for payloads and verify, without an API key.

apps/indexer/src/fill.ts, apps/indexer/src/snapshot.ts, apps/web/lib/proxy.ts, apps/web/lib/live-tail.ts

Why it has to be Arkiv

The index leans on properties the chain guarantees, and keeps the rest off chain on purpose.

Why Arkiv

  • $creator and $owner are system fields. The chain sets them, so the creator scope cannot be forged with an attribute.
  • Native expiry. Every entity has an expiresAt block. The index enforces it instead of inventing its own retention.
  • Typed, queryable attributes at a pinned block. Lookups combine $creator, $key and atBlock with cursor pagination.
  • A public RPC anyone can read. Your browser checks rows and follows logs without the indexer in the path.

Off Arkiv on purpose: the Parquet snapshots, the DuckDB checkpoint and SQL execution. Payloads stay on chain and load on demand.

SPECS.md, arkiv/schema.md, apps/indexer/src/store.ts

Arkiv features you can see

Each one is in the code, and each one shows up somewhere in this app.

Feature details or open the showcase

  • Typed attributes, payload on demandA Payload button on each result row reads the payload from the RPC when you click it, shown up to 128 KiB.apps/demo-writer/src/writer.ts, packages/core/src/decode.ts, apps/web/components/RowActions.tsx
  • Compound queries scoped by $creatorThe status bar shows the indexed expression. With a creator expression, rows from other creators never reach your tab.packages/core/src/query.ts, apps/web/components/dash/StatusBar.tsx
  • Pinned reads with atBlockConsistent rows, and a status bar that tells you how far the index is behind the chain head.apps/indexer/src/backfill.ts, apps/indexer/src/fill.ts
  • Expiry per entity typeThe Expiry view in the showcase, and heartbeats leaving SQL ten minutes after creation.apps/demo-writer/src/plan.ts
  • Leases renewed by activityA ticket's expires_at moves forward. Verify reports extended when the chain holds a newer update with a later expiry than your row.apps/demo-writer/src/writer.ts, apps/demo-writer/src/plan.ts, apps/web/lib/verify.ts
  • One batch for a ticket and its first commentThe featured playground query joins open tickets to their comment counts.apps/demo-writer/src/writer.ts, apps/web/lib/views.ts
  • Ownership moves, creator staysThe Creator vs current owner table in the showcase Creators view.apps/demo-writer/src/writer.ts, apps/web/lib/views.ts
  • Keyless sync from native logsThe live block, pending attribute fetches and RPC budget state in the status bar.apps/indexer/src/sync.ts, apps/web/lib/live-tail.ts, apps/indexer/test/keyless.test.ts

Check it yourself

Nothing here asks for trust you cannot test from a browser.

Step by step or open the playground

  • Verify any row. In the playground, Verify page re-reads each row from the public RPC and labels it ok, stale, extended, expired or deleted. It is a consistency check, not a cryptographic proof.apps/web/lib/verify.ts, apps/web/components/dash/ResultsGrid.tsx
  • Expiry at the live head. SQL views hide every entity whose expiry block is at or below the head your tab read from the RPC.packages/core/src/views.ts
  • The demo wallets are public. Look them up on the Tiramisu explorer.README.md, apps/web/lib/explorer.ts
Demo wallet (creator)
0x1f61c3cac1612C020f64739B128C5f198939C8Cd
Agent one
0xb0B87EdDB0bc777Eb663c7a11703ba5ED4849A28
Agent two
0xF1BC753cF42d0157Ac34E28DBf06108F66aDB3B3