Queryable
Filter and paginate records through indexed attributes with a Web2-like query language. No external indexer.
Arkiv is a Web3 database. It stores your data as entities that you can query by indexed attributes, that your wallet owns, and that expire when you say so. You talk to it with the same tools you use for Ethereum: a wallet, a transaction, and a JSON-RPC endpoint.
For years, builders had to choose. Web2 is fast and convenient, until it locks you in, goes down, or shuts you out. Web3 hands you ownership, then makes you pay for it in speed, simplicity, and cost.
| Web2 | Web3 | Arkiv | |
|---|---|---|---|
| UX | Battle tested | Bad | Great |
| Integrity | Centralized | Tamper-proof | Tamper-proof |
| Ownership | False ownership | Wallet-owned | Wallet-owned |
| Queries | Queryable | Not easily queryable | Queryable |
| Lifetime | Time-scoped | Permanent | Time-scoped |
Arkiv is the layer for owned, queryable data, with Web3 guarantees.
Queryable
Filter and paginate records through indexed attributes with a Web2-like query language. No external indexer.
Time-scoped
Every entity has an expiration block. When it arrives, the entity leaves the query surface. You pay for the lifetime you choose.
Tamper-proof
Every write is a signed Ethereum transaction. State is committed with a keccak Merkle-Patricia trie, so any change is evident.
Wallet-owned
Only the owner wallet can update, extend, transfer, or delete an entity.
Arkiv is an Ethereum-aligned DB-Chain. It looks and feels like a database: CRUD and indexed queries over RPC, with programmable expiration.
┌──────────────────────────────────────────────────────────┐│ YOUR APP ││ TypeScript SDK · JSON-RPC · any Ethereum wallet │└───────────────┬──────────────────────────┬───────────────┘ │ eth_sendRawTransaction │ arkiv_query ▼ ▼┌──────────────────────────────────────────────────────────┐│ ARKIV DB-CHAIN ││ Sequencer produces blocks (2 s block time) ││ Arkiv DB engine replaces the EVM: entities, attributes, ││ indexes, expiry, ownership ││ State root: keccak-MPT, readable with eth_getProof ││ Watchers replicate the chain and serve reads │└───────────────┬──────────────────────────────────────────┘ │ GLM deposits (relayer) ▼┌──────────────────────────────────────────────────────────┐│ ETHEREUM ││ Source of GLM. Arkiv commits its records here for ││ durable archival, so data stays recoverable and ││ tamper-proof even if every Arkiv node disappears. │└──────────────────────────────────────────────────────────┘A DB-Chain runs on an Ethereum execution client with the EVM interpreter removed. In its place sits the Arkiv DB engine. The engine validates, decodes, and applies database operations, and it prices them by the storage work they cause.
to address is a known Arkiv operation address. Every other transaction reverts. No bytecode is deployed, not even at genesis.eth_* for transactions, receipts, balances, and proofs. arkiv_* for queries and entity views.You pay for data operations in GLM. On testnets, the faucet gives you test GLM.
An entity is a data record on Arkiv. It has:
The protocol also keeps system attributes on every entity: $owner, $creator, $createdAt, $updatedAt, and $expiresAt. You can query them. You cannot set them.
| SDK method | Who | What |
|---|---|---|
createEntity() | any funded wallet | Create an entity with attributes and a lifetime |
patchEntity() | owner | Set or unset attributes, or replace the payload |
extendEntity() | owner | Move the expiration later. Never earlier |
changeOwnership() | owner | Hand the entity to another wallet |
deleteEntity() | owner | Remove the entity at once |
One transaction can carry many operations. They apply as one atomic batch. If one fails, none apply.
Entity keys are derived by the protocol from your wallet, a nonce, and a random salt. Nobody can guess your next key. You can compute it yourself before you send the transaction with predictEntityKey().
The type you give a value decides which query operators work on it. Typed helpers come from @arkiv-network/sdk/attr. A plain string needs no helper:
import { i32 } from "@arkiv-network/sdk/attr";
{ type: "note" } // str: eq() and startsWith(){ priority: i32(5) } // i32, u64, u256, dec: eq() and range (gt, lt, gte, lte){ active: true } // bool, addr, key, bytes32: eq() onlyArkiv measures time in blocks. One block is 2 seconds. Build a lifetime with ExpirationTime and pass it as expires:
import { ExpirationTime } from "@arkiv-network/sdk";
ExpirationTime.fromHours(12)ExpirationTime.fromDays(7)ExpirationTime.atBlock(1_200_000n)From the expiration block on, the entity is gone from every query and rejects every operation. There is no event for this. If you mirror Arkiv data off-chain, track $expiresAt yourself.
Start with a short lifetime to save on fees. Extend it later if you still need the data.
A query is a boolean expression. No SELECT, no WHERE:
type = str('note') AND priority > i32(3)Operators: AND, OR, NOT, =, <, >, <=, >=, STARTSWITH. Tag every value with its type, except bare booleans and bare i32 numbers. Use * to match every entity. There is no != yet. Write NOT (attr = value).
Full syntax reference: Query syntax.
The query builder of the SDK does more than basic filtering. Before you write a manual workaround, see what it already provides:
select({ key: true, payload: true }). The result type narrows to exactly those fields.hasNextPage() and next(). Each page holds up to 200 results.getEntityCount()..ownedBy() and .createdBy() to filter by wallet.Full reference: Querying Data
No advanced blockchain knowledge required. Use the SDK to create and query your data like you would with a Web2 database.