zeekayandHanzo Dev a67f2a6644 build: graph v1.2.11 + label every graph indexer with its chain/subgraph
v1.5.1 in the cluster showed one chain's valuation pass reporting and two
others silent — and the log gave no way to tell which chain any line belonged
to, because the graph indexer was the one component here that did not prefix
its output. Config.Label (new in graph v1.2.11) fixes that: pass the same
"slug/name" this supervisor already uses everywhere else.

v1.2.11 also stops the valuation pass writing a row per swap (which is what
wedged the busy chains) and reads V3 swap amounts as the int256 they are.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 18:38:53 -07:00
2026-06-28 20:37:34 -07:00
2026-07-26 10:13:18 -07:00
2026-06-28 20:37:35 -07:00
2026-07-26 10:13:18 -07:00
2026-07-26 10:13:18 -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/mainnet/ext/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%