Files
zeekay c78e72033c docs: chain routes are /v1/chain
luxd serves chain APIs under one segment from constants.ChainAliasPrefix
(constants v1.6.4, node v1.36.181):

  /v1/bc/<alias>/...   -> /v1/chain/<alias>/...
  /ext/bc/<alias>/...  -> /v1/chain/<alias>/...

Observations keep their original addresses: a dated note or a recorded status
code is a fact about when it was written, not an instruction to follow.
2026-08-30 17:13:55 -07:00

178 lines
5.4 KiB
Markdown

<p align="center"><img src=".github/hero.svg" alt="explorer" width="880"></p>
# Explorer
Single-binary Lux explorer: chain indexer, GraphQL engine, and SPA frontend
in one Go process. Imports [`luxfi/indexer`](https://github.com/luxfi/indexer)
and [`luxfi/graph`](https://github.com/luxfi/graph) as libraries; embeds the
[`luxfi/explore`](https://github.com/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
```bash
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:
```bash
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`](chains.example.yaml) for the full
schema.
```yaml
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/chain/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:
```bash
# 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
```bash
# 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)
```bash
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 } }"}'
```
## Related
- [`luxfi/indexer`](https://github.com/luxfi/indexer) -- chain indexer library
- [`luxfi/graph`](https://github.com/luxfi/graph) -- per-chain GraphQL library
- [`luxfi/explore`](https://github.com/luxfi/explore) -- Next.js SPA