Prometheus exporter for The Graph network subgraph — surfaces on-chain indexer economics (stake,
delegation, allocations, curation signal) that don't exist on any /metrics endpoint, for any set
of indexers you configure.
Zero-dependency Bun app. Sibling to
subgraphs-qos-exporter (gateway QoS
feed); together they give Grafana the full picture the raw graph-node metrics can't.
Supersedes the old inline
stake-exporterConfigMap ink8s-subgraphs. Metric names are unchanged (plus anindexer/indexer_namelabel on the per-deployment allocation), so existing dashboards keep working — and it now generalises to multiple indexers.
Per-indexer (labelled indexer, indexer_name) — for the INDEXERS detail set, and (with
ALL_INDEXER_STAKE=true) every registered indexer above MIN_STAKE_GRT:
| Metric | Source field |
|---|---|
indexer_self_stake_grt |
stakedTokens |
indexer_delegated_grt |
delegatedTokens |
indexer_delegation_capacity_grt |
delegatedCapacity |
indexer_allocated_grt |
allocatedTokens |
indexer_available_stake_grt |
availableStake |
indexer_query_fees_collected_grt |
queryFeesCollected |
indexer_rewards_earned_grt |
rewardsEarned |
indexer_capacity_used_ratio |
allocatedTokens / delegatedCapacity |
Per-(indexer, deployment):
| Metric | Meaning |
|---|---|
indexer_subgraph_allocated_grt |
that indexer's active allocation on a deployment |
Per-deployment (network-wide, from subgraphDeployment — deduped across all tracked indexers' allocations):
| Metric | Meaning |
|---|---|
subgraph_total_allocated_grt |
tokens allocated on the deployment by all indexers |
subgraph_signalled_grt |
curation signal on the deployment (drives the indexing-reward split) |
subgraph_name_info |
deployment → display name (value 1) |
Liveness: subgraphs_network_exporter_up, _last_refresh_seconds, _indexers_tracked,
_deployments_tracked (plus indexer_stake_exporter_up for backward-compat).
All via env (see .env.example); everything has a default.
| Var | Default | Notes |
|---|---|---|
NETWORK_SUBGRAPH_URL |
in-cluster GNArb | GraphQL endpoint; override to run locally |
INDEXERS |
our wallet | comma-separated wallets — the detail set (stake + allocations + signal + names) |
ALL_INDEXER_STAKE |
false |
also emit stake gauges for every registered indexer over the floor |
MIN_STAKE_GRT |
100000 |
stake floor for the all-indexers view and for gen-indexers.ts pruning |
REFRESH_SECONDS |
60 |
stake/allocation/signal poll |
NAME_REFRESH_SECONDS |
1800 |
display-name poll (names change rarely) |
PORT |
9400 |
HTTP listen port |
Indexer display names come from a committed indexers.json ({ "<wallet>": "<name>" }), baked into
the image and loaded at startup — so there's no risky name field on the runtime critical path.
Regenerate it out-of-band (drops anyone below the stake floor, which prunes deregistered indexers):
NETWORK_SUBGRAPH_URL="https://gateway.thegraph.com/api/<key>/subgraphs/id/<network-subgraph>" \
MAINNET_RPC_URL="https://eth.rpc.example/v1/<key>/" \
MIN_STAKE_GRT=100000 \
bun run gen:indexers > indexers.jsonName preference: ENS primary name (mainnet reverse resolution) → network-subgraph defaultDisplayName
→ indexer URL host. MAINNET_RPC_URL is optional (without it, ENS reverse is skipped).
bun install # dev deps only (types); the app itself has none
bun run start # serves /metrics and /healthz on :9400
bun test # unit tests for the pure render/format helpers
bun run typecheckGET /metrics(and/) — Prometheus text expositionGET /healthz—200 okonce the first refresh succeeds, else503 degraded
- Stdlib-only at runtime — Bun's global
fetch; the render/format helpers are pure and exported forbun test. The HTTP server + poll loop only start underimport.meta.main. - Two cadences — a fast loop (
REFRESH_SECONDS) for stake/allocations/signal, a slow one (NAME_REFRESH_SECONDS) for display names. A failed cycle keeps the last-good metrics and only flips_upto 0; fast-retries every 30s so a transient blip never leaves stale data for long. - Multiple indexers — put any wallets in
INDEXERS. Per-deploymenttotal/signalare deployment-global, so they're deduped across the union of tracked indexers' allocations.