abx.
Using ABX

Self-hosting

A live project runs two reference services: a resolver and an effects runner. You run both on infrastructure you own — or point at a remote provider that runs them for you; the interface is the same either way. The resolver holds no signing key.

The resolver

The resolver re-indexes the chain's event logs into a local projection and serves the token API. It is read-only. Its endpoints include:

  • GET /t/{chainId}/{address}/{tokenId}: token metadata — ERC-721 tokenURI on a 1/1, Series, or code project; ERC-1155 uri on an edition, same route — and .../image for the image.
  • GET /c/{chainId}/{address}: ERC-7572 collection metadata.
  • GET /api/project/{address}: the full reconstructed project state, including abx_provenance.
  • GET /d/{chainId}/{address}: a per-contract dashboard.
  • GET /health.
  • GET /.well-known/abx-service: the public service descriptor.
  • POST /v1/projects and the rest of the bearer-authed control plane: register, list, deregister, reindex, status. Enabled by setting ABX_RESOLVER_ADMIN_TOKEN on the node; the token authorizes index control only, never signing.

Don't hand-build these URLs. A contract commits its own metadata base on-chain (tokenURIBase / contractURIBase), so the chain — not this list — is the authoritative answer to where a given project's metadata lives. abx tokenuri <address> and abx contracturi <address> read it from the contract and follow it. That matters because the routes look similar enough to guess wrong: dropping the {tokenId} off /t/… does not give you collection metadata (that's /c/…).

When a read does miss, the response says which of three things went wrong, in a machine code — see the error taxonomy. A 400 invalid_request means the path shape was wrong and names the right template; 404 unknown_route means no such route here; 404 not_registered means the path was fine but this node doesn't index that contract. Only the last one is about the project, and none of them mean the service is down.

Run it locally with abx serve. This also runs a chain watcher that polls for new logs, about every 12 seconds, and re-indexes each registered project. Deploy it to a host with abx deploy-resolver --provider <fly | render | vps>, which writes a self-contained deployment artifact for that target. The production image installs @artblocks/abx-cli from npm and runs without a browser.

Is it caught up?

abx status answers that. Bare, it lists every project this node tracks with its indexing state; with an address, it shows one project in detail — the scan floor, blocks indexed against chain head, and the cause if something went wrong:

abx status                       # this node: chain, storage, and every project's state
abx status <address>             # one project: lifecycle, floor, blocks indexed vs head
abx status <address> --remote <name> --watch   # ask a hosted node or a provider, and follow along

The five states are queuedbackfillinglive, plus stale (was live, now lagging — still serving) and failed (carries a cause; retried with backoff). They are the same five words a managed provider reports, so the question doesn't change shape when the answer comes from someone else's infrastructure.

Two behaviors worth knowing about your own node: a registration is durable before catch-up starts, so a flaky RPC makes for a slower backfill rather than a lost project; and a backfill interrupted by a restart is re-queued and picked up by the watcher rather than sitting there registered-but-empty. The chain watcher also marks projects stale when it falls far behind head, which is how a failing RPC becomes visible instead of silently serving old state.

Two optional knobs: ABX_REGISTER_DEADLINE_MS (default 8000) is how long a registration may hold the HTTP request open before it answers "accepted, still catching up" and finishes in the background — the registration is durable either way, so this only decides who waits. ABX_STALE_LAG_BLOCKS (default 5000) is how far behind head the watcher may fall before it reports stale.

abx status is about serving: who has the data and how fresh it is. For what the chain itself says about a contract — owner, supply, royalty, locks — use abx state <address>.

Because a resolver re-indexes from the chain, moving to a new host is a configuration change. See migrating.

The effects runner

A code project's still image is rendered off-chain, so the resolver image stays browser-free and a separate runner does the rendering. The runner uses Playwright and Chromium. It watches the spine, renders new mints and parameter changes, and publishes each render to the resolver.

  • Run it locally with abx effects. Add --once to sweep every project once and exit.
  • Host it with abx deploy-effects --resolver-url <https://your-resolver>.
  • Render one project's missing stills with abx render <address>.

Set ABX_EFFECTS_URL on the resolver so its chain watcher notifies the runner as soon as something changes. A periodic sweep is the fallback.

Storage

The resolver and runner read and serve the source bytes from a storage backend. For a hosted pair, use a shared, addressable backend such as IPFS, Arweave, or S3-compatible cloud, not local disk. See Storage.

Migrating

abx migrate <address> --from <source-resolver-url> --to <dest-resolver-url> moves a project's off-chain state to a new resolver. It reconstructs the on-chain truth, copies the source resolver's off-chain values (description, external URL, off-chain traits, image locators), re-pins images to a durable backend after verifying them against the on-chain hash, and runs a parity check between source and destination. It does not cut over: you re-point DNS, or the on-chain base URI with set-token-uri and set-contract-uri, when you are ready. The two resolvers never talk to each other.

On this page