Skip to main content

Registry quickstart

Outcome. Your own listing Registry: a discovery index with your chosen read/write access policy and filter vocabulary, wired to sellers and buyers.

Public terminology on this site is Registry. The synchronized source file is docs/indexer-quickstart.md — "indexer" survives at the pinned revision only in that filename and in internal identifiers; the document itself, reproduced below, says "listing registry" throughout.

Before you begin

  • Work from a clean SCM checkout at the revision shown below.
  • Install Docker Engine and Docker Compose v2 on the Registry host.
  • Decide whether reads and writes are public or bearer-token gated before distributing participant configuration.
  • Generate new operator secrets; never reuse the placeholders in the guide.

Clone SCM and select the documentation pin before running the first build command:

git clone https://github.com/arkhai-io/simple-compute-market.git
cd simple-compute-market
git checkout --detach 648682c67a26caf9283492e999923ab6ca1206ee

Environment. Every command below runs on the host where you operate the Registry (Docker + Compose v2). A Registry is a discovery role only: it never negotiates, settles, or fulfills. Where the synchronized body says "vetted sellers," read that as sellers holding write authorization. An API key does not establish real-world identity, capacity, availability, or performance.


How to stand up your own listing registry. Reasons to run one:

  • Curate which sellers can publish and which buyers can query (bearer-token auth, §6).
  • No third-party rate limits.
  • Custom filter-spec.yaml — the vocabulary for gpu_model, region, etc. is per registry.
  • Solo testing — no fanout.

compose/seller.yml is registry-agnostic, so a listing registry can run on the same host as a seller or anywhere else. Co-location is only deployment convenience: the registry remains a discovery role, while the storefront remains the seller's negotiation, settlement, and fulfillment role. See roles.md for the role model.

1. Build the image

make build-registry

Produces arkhai:registry.

2. Configure

cp config.registry.env.example config.registry.env
$EDITOR config.registry.env

Fill in:

  • REGISTRY_ADMIN_API_KEY — operator-only secret used to mint/revoke per-user keys at /admin/api-keys. Generate with openssl rand -hex 32.
  • REGISTRY_BOOTSTRAP_API_KEY — the bearer token sellers and buyers will use until per-user keys are minted. Same openssl rand -hex 32 pattern. This is the shared secret you give out.

The example file gates reads and writes independently and ships with both on (REGISTRY_REQUIRE_READ_API_KEY=true, REGISTRY_REQUIRE_WRITE_API_KEY=true) — a private listing registry where buyers hold read keys and sellers hold write keys.

For a fully public listing registry (anyone can publish and query) set both to false and drop the two key vars. For an open market — public discovery, publishing limited to vetted sellers — set only REGISTRY_REQUIRE_WRITE_API_KEY=true and hand write keys to sellers.

3. Bring it up

docker compose -f compose/registry.yml up -d

# or, sharing a docker network with a seller stack:
docker compose -f compose/seller.yml -f compose/registry.yml up -d

The compose file mounts a named volume at /app/data so the sqlite state persists across restarts.

4. Wire sellers and buyers

In each storefront / buyer TOML:

[registry]
urls = ["http://<REGISTRY_HOST>:8080"]

When the listing registry and seller share a docker network, use the service name: urls = ["http://registry:8080"].

5. Checks

curl -sf http://<REGISTRY_HOST>:8080/health

docker compose logs registry | grep -i "JIT.*Indexed agent"

curl -s http://<REGISTRY_HOST>:8080/filter-spec | jq

# Listings — note the full canonical agent ID, URL-encoded:
curl -s "http://<REGISTRY_HOST>:8080/agents/eip155%3A84532%3A0x8004A818BFB912233c491871b3d84c89A494BD9e%3A<N>/listings" \
| jq

Verify an attributed publisher's listings directly — omit the header only when reads are intentionally public:

curl -s "http://<REGISTRY_HOST>:8080/listings?publisher=<SIGNING_WALLET_ADDRESS>" \
-H "Authorization: Bearer <read_api_key>" | jq '.items'

6. Bearer-token auth

config.registry.env.example ships with both gates on. To disable auth (fully public listing registry) set REGISTRY_REQUIRE_READ_API_KEY and REGISTRY_REQUIRE_WRITE_API_KEY to false and drop the two key vars.

Flow:

  • Admin mints/revokes per-user keys: POST /admin/api-keys with Authorization: Bearer <REGISTRY_ADMIN_API_KEY>. The request body takes scope (read for buyers, write for sellers; defaults to read). A write key also satisfies read routes.
  • Sellers and buyers send Authorization: Bearer <api_key> on every request, configured via each side's [registry.auth] block. Keys must match the URL in [registry] urls exactly (scheme, host, port, no trailing slash).

In the seller / buyer TOML:

[registry.auth]
"http://<REGISTRY_HOST>:8080" = "<api_key>"

What success looks like

/health returns success, /filter-spec returns the vocabulary this Registry accepts, the container retains its SQLite state across a restart, and an authorized participant can read or publish according to the two gates you configured. An empty listing response is valid; it is not a failed Registry.

If a check fails

  • A 401 from an intended public read means the read gate is still enabled.
  • A 401 or 403 with a bearer token usually means the token scope is wrong or the participant's [registry.auth] key does not exactly match the configured URL.
  • A healthy process with no persisted rows usually means the expected named volume is not mounted.

Next steps

Continue to Registry authentication and topology to mint per-participant credentials and verify revocation.