zooqueen f50ec8c9e5 Warm one chain at a time, the front page's first
Every chain this process serves shares one disk. Warming them together made
each finish late and made a caller arriving during startup wait behind the
contention rather than behind its own chain's scan. In turn, each finishes as
fast as the disk allows, and the chain the front page asks about is ready
first.
2026-08-13 21:42:01 -07:00
2026-08-03 21:01:59 -07:00

explorer

Explorer

Single-binary Lux explorer: chain indexer, GraphQL engine, and SPA frontend in one Go process. Imports luxfi/indexer and luxfi/graph as libraries; embeds the luxfi/explore Next.js build via go:embed.

ghcr.io/luxfi/explorer
  -> indexes one or more EVM chains  (luxfi/indexer)
  -> exposes per-chain GraphQL       (luxfi/graph)
  -> serves embedded SPA + REST API  (luxfi/explore)

Routes

Path Purpose
/ SPA (embedded; SPA-routing fallback)
/envs.js Runtime config window.ENV = {...}
/icon.svg, /logo.svg Per-host brand assets (disk override)
/health Service health
/v1/indexer/* Default chain explorer API
/v1/indexer/{slug}/* Per-chain explorer API
/v1/explorer/{slug}/* Legacy alias for /v1/indexer/{slug}
/v1/graph/{slug}/{subgraph}/graphql Per-chain, per-subgraph GraphQL
/v1/explorer/admin/chains[/{slug}] Runtime registry CRUD
/v1/explorer/realtime WebSocket realtime hub
/v1/explorer/realtime/stats Realtime stats

Quick Start

docker run -p 8090:8090 \
  -v $(pwd)/chains.example.yaml:/etc/explorer/chains.yaml:ro \
  -v explorer-data:/data \
  ghcr.io/luxfi/explorer:latest \
  --config /etc/explorer/chains.yaml

Or with compose:

docker compose up

Configuration

chains.yaml is the single source of truth. Every aspect of the deploy -- chains, branding, GraphQL subgraphs, network switcher links -- is per-chain customizable. See chains.example.yaml for the full schema.

data_dir: /data
http_addr: :8090

brand_default:
  name: Lux Explorer
  coin: LUX
  accent_color: "#5cf"

networks:
  - { label: Mainnet, domain: explorer.lux.network, chain_id: 96369 }

chains:
  - slug: cchain
    name: Lux C-Chain
    chain_id: 96369
    type: evm
    rpc: https://api.lux.network/v1/bc/C/rpc
    coin: LUX
    enabled: true
    default: true

    indexer:
      poll_interval: 30s

    graph:
      enabled: true
      subgraphs:
        - { name: amm, schema: amm, enabled: true }

    brand:
      name: Lux C-Chain
      accent_color: "#5cf"

$ENV_VAR substitution in rpc: / ws: lets one file template every network -- mount it read-only and inject secrets via env vars.

Per-Network Customization

The same image powers every deploy. To rebrand for a different network:

  1. Mount a different chains.yaml.
  2. Optionally mount icon.svg / logo.svg at the path referenced by chains.yaml -- they are read from disk per-request, not embedded.
  3. Set environment variables to fill the ${VAR} placeholders.

No image rebuild needed. The SPA reads chain list and brand at runtime from /envs.js.

Runtime Registry

Chains can be added, updated, and removed at runtime without restart:

# Add
curl -X POST http://localhost:8090/v1/explorer/admin/chains \
  -H 'Content-Type: application/json' \
  -d '{"slug":"new","name":"New","chain_id":123,"rpc":"http://node:8545","type":"evm"}'

# List
curl http://localhost:8090/v1/explorer/admin/chains

# Remove
curl -X DELETE http://localhost:8090/v1/explorer/admin/chains/new

Adding a chain spawns its indexer + (optional) graph workers; removing cancels them and frees the routes.

mDNS Discovery

Set EXPLORER_MDNS=true to auto-register chains advertised by local Lux nodes (_luxd._tcp, _zood._tcp, _hanzod._tcp, _parsd._tcp). The explorer queries info.getChains on the discovered node and registers each tracked chain. mDNS-sourced chains never override config-sourced entries.

Storage Layout

/data/
  cchain/
    query/indexer.db         (SQLite WAL, indexer)
    {zapdb}/                 (KV store)
    graph/
      amm/graph.db           (SQLite WAL, per-subgraph)
  zoo/
    ...

Each chain (and each subgraph) owns an isolated SQLite + KV store. WAL is optionally streamed to S3 with PQ encryption when REPLICATE_S3_ENDPOINT is set -- see luxfi/indexer/daemon.

Local Build

# Sibling layout: ~/work/lux/{indexer,graph,explorer}
cd ~/work/lux/explorer
go build -o explorer .

go.mod uses local replace directives pointing at ../indexer and ../graph. Production CI clones all three repos into the same layout inside the Docker build.

Endpoints (curl examples)

curl http://localhost:8090/health
curl http://localhost:8090/v1/indexer/cchain/blocks/latest
curl -X POST http://localhost:8090/v1/graph/cchain/amm/graphql \
  -H 'Content-Type: application/json' \
  -d '{"query":"{ swaps(first:5) { id amount0 amount1 } }"}'
S
Description
Lux Explorer
Readme BSD-3-Clause
1.1 MiB
Languages
Go 97.3%
Dockerfile 1.7%
Shell 0.8%
HTML 0.2%