retire graph-node: the explorer bundles graph and indexer

luxfi/explorer carries luxfi/graph and luxfi/indexer, and every network's
exchange-api already reads it at /v1/graph/{slug}/{amm,dex}/graphql. A second
Graph Protocol stack -- graph-node, its postgres, its ipfs -- served nothing.

Removes its declarations, its repo entry, and two stale exchange-api
declarations that still named it: lux-explorer runs no exchange-api at all,
and lux-ns runs from deploy/, so applying either would have pointed a live
service at a service that no longer exists.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-28 12:41:53 -07:00
co-authored by Hanzo Dev
parent 8bfa32d9aa
commit 6e013d6eb3
7 changed files with 4 additions and 802 deletions
-6
View File
@@ -68,11 +68,6 @@ repos:
tag: v2.5.1-lux
group: app
note: unified Next.js SPA + Go indexer. Replaces explorer-lux, explorer-zoo, explore-lux.
graph-node:
repo: luxfi/graph-node
tag: main
group: app
note: TODO — cut first semver release; remove main pin.
# ── Data ───────────────────────────────────────────────────────────────
state:
@@ -84,6 +79,5 @@ repos:
# explorer-lux → folded into luxfi/explore
# explorer-zoo → folded into luxfi/explore (chains.yaml multi-chain config)
# explore-lux → folded into luxfi/explore
# graph-node-lux → renamed to luxfi/graph-node
# v2-subgraph → fold target TBD
# v3-subgraph → fold target TBD
-42
View File
@@ -1,42 +0,0 @@
# Generated from the live lux-mainnet/graph-ipfs Deployment on do-sfo3-lux-k8s
# by hack/live2values.py. Live is the source: a repo of hand-applied
# manifests has unknown currency, the running fleet does not.
replicas: 1
selectorLabels:
app: graph-ipfs
containerName: ipfs
image:
repository: ipfs/kubo
tag: v0.28.0
pullPolicy: IfNotPresent
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 100m
memory: 256Mi
volumeMounts:
- mountPath: /data/ipfs
name: data
ports:
- containerPort: 5001
name: api
- containerPort: 8080
name: gateway
volumes:
- name: data
persistentVolumeClaim:
claimName: graph-ipfs-data
topologySpread:
enabled: false
lifecycle: null
terminationGracePeriodSeconds: 30
defaultReadinessProbe: false
imagePullSecrets: []
# RWO volume + one replica: Recreate, not RollingUpdate. A rolling update
# guarantees an overlap, the new pod cannot attach a ReadWriteOnce volume the
# old one holds, and the rollout parks on Multi-Attach forever while the old
# pod keeps serving — measured on status and elastic. Recreate costs one
# bounded restart and completes.
strategy: Recreate
+4 -6
View File
@@ -11,12 +11,10 @@ image:
env:
- name: PORT
value: '4000'
# One AMM source, as on testnet and devnet: the native graph inside the explorer
# image. Mainnet alone still read the legacy graph-node for v3, which carried
# LBTC at 8 decimals and LTON at 9 (both are 18 on-chain) — that priced their
# pools 1e10/1e9 off and rendered volumeUSD in the 1e25–1e31 range across the
# site. The native indexer reads decimals from the token itself, so it never had
# the defect. graph-node has no other consumer and is retired.
# One AMM source on every network: the native graph in the explorer image, which
# bundles luxfi/graph and luxfi/indexer. It reads each token's decimals from the
# token, so a pool is priced by what the chain says rather than by a figure
# carried beside it.
- name: SUBGRAPH_URL
value: http://explorer.lux-mainnet.svc.cluster.local:8090/v1/graph/cchain/amm/graphql
- name: DEX_SUBGRAPH_URL
-81
View File
@@ -1,81 +0,0 @@
# Exchange API - GraphQL proxy for lux.exchange
# Proxies Uniswap API with caching + native Lux/Zoo chain data
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: exchange-api
namespace: lux-explorer
spec:
replicas: 2
selector:
matchLabels:
app: exchange-api
template:
metadata:
labels:
app: exchange-api
spec:
imagePullSecrets:
- name: ghcr-luxfi
containers:
- name: exchange-api
image: ghcr.io/luxfi/exchange-api:v2.2.2
ports:
- containerPort: 4000
env:
- name: PORT
value: "4000"
- name: BLOCKSCOUT_API
value: "https://api-explore.lux.network"
- name: SUBGRAPH_URL
value: "http://graph-node.lux-explorer.svc:8000/subgraphs/name/luxfi/uniswap-v2"
- name: SUBGRAPH_V3_URL
value: "http://graph-node.lux-explorer.svc:8000/subgraphs/name/luxfi/uniswap-v3"
- name: UNISWAP_API
value: "https://gw.lux.exchange/gateway/v1/graphql"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 4000
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 4000
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: exchange-api
namespace: lux-explorer
spec:
selector:
app: exchange-api
ports:
- port: 4000
---
apiVersion: hanzo.ai/v1alpha1
kind: IngressRoute
metadata:
name: exchange-api
namespace: lux-explorer
spec:
entryPoints:
- websecure
routes:
- match: Host(`api-exchange.lux.network`)
kind: Rule
services:
- name: exchange-api
port: 4000
tls:
certResolver: letsencrypt
-246
View File
@@ -1,246 +0,0 @@
# Graph Node + PostgreSQL for Lux AMM subgraph indexing
# Indexes v2/v3 pool events from AMMV2Factory on Lux C-chain
---
# PostgreSQL for Graph Node
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: graph-postgres-data
namespace: lux-explorer
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: graph-postgres
namespace: lux-explorer
spec:
serviceName: graph-postgres
replicas: 1
selector:
matchLabels:
app: graph-postgres
template:
metadata:
labels:
app: graph-postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
value: graph
- name: POSTGRES_PASSWORD
value: graph-lux-2026
- name: POSTGRES_DB
value: graph_node
- name: POSTGRES_INITDB_ARGS
value: "-E UTF8 --locale=C"
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: data
persistentVolumeClaim:
claimName: graph-postgres-data
---
apiVersion: v1
kind: Service
metadata:
name: graph-postgres
namespace: lux-explorer
spec:
selector:
app: graph-postgres
ports:
- port: 5432
---
# IPFS node for subgraph deployment artifacts
apiVersion: apps/v1
kind: Deployment
metadata:
name: graph-ipfs
namespace: lux-explorer
spec:
replicas: 1
selector:
matchLabels:
app: graph-ipfs
template:
metadata:
labels:
app: graph-ipfs
spec:
containers:
- name: ipfs
image: ipfs/kubo:v0.28.0
ports:
- containerPort: 5001 # API
- containerPort: 8080 # Gateway
volumeMounts:
- name: data
mountPath: /data/ipfs
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: graph-ipfs
namespace: lux-explorer
spec:
selector:
app: graph-ipfs
ports:
- name: api
port: 5001
- name: gateway
port: 8080
---
# Graph Node ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: graph-node-config
namespace: lux-explorer
data:
config.toml: |
[store]
[store.primary]
connection = "postgresql://graph:[email protected]:5432/graph_node?sslmode=disable"
weight = 1
pool_size = 10
[deployment]
[[deployment.rule]]
shard = "primary"
indexers = ["graph-node"]
[chains]
ingestor = "graph-node"
[chains.lux]
shard = "primary"
provider = [
{ label = "lux-mainnet", url = "http://luxd-0.luxd-headless.lux-mainnet.svc:9630/v1/bc/C/rpc", features = [] }
]
---
# Graph Node StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: graph-node
namespace: lux-explorer
spec:
serviceName: graph-node
replicas: 1
selector:
matchLabels:
app: graph-node
template:
metadata:
labels:
app: graph-node
spec:
containers:
- name: graph-node
image: graphprotocol/graph-node:v0.35.1
ports:
- containerPort: 8000 # GraphQL HTTP
name: graphql
- containerPort: 8001 # GraphQL WS
name: ws
- containerPort: 8020 # JSON-RPC (admin/deploy)
name: admin
- containerPort: 8030 # Index node status
name: status
- containerPort: 8040 # Metrics
name: metrics
env:
- name: GRAPH_NODE_CONFIG
value: /etc/graph-node/config.toml
- name: node_id
value: graph-node
- name: GRAPH_LOG
value: info
- name: ipfs
value: graph-ipfs.lux-explorer.svc:5001
- name: ETHEREUM_REORG_THRESHOLD
value: "1"
- name: ETHEREUM_ANCESTOR_COUNT
value: "1"
volumeMounts:
- name: config
mountPath: /etc/graph-node
resources:
requests:
memory: "2Gi"
cpu: "500m"
limits:
memory: "4Gi"
cpu: "2000m"
volumes:
- name: config
configMap:
name: graph-node-config
---
apiVersion: v1
kind: Service
metadata:
name: graph-node
namespace: lux-explorer
spec:
selector:
app: graph-node
ports:
- name: graphql
port: 8000
- name: ws
port: 8001
- name: admin
port: 8020
- name: status
port: 8030
---
# IngressRoute for subgraph.lux.network
apiVersion: hanzo.ai/v1alpha1
kind: IngressRoute
metadata:
name: graph-node
namespace: lux-explorer
spec:
entryPoints:
- websecure
routes:
- match: Host(`subgraph.lux.network`)
kind: Rule
services:
- name: graph-node
port: 8000
tls:
certResolver: letsencrypt
-288
View File
@@ -1,288 +0,0 @@
# Graph Node + PostgreSQL for Lux AMM subgraph indexing
# Indexes v2/v3 pool events from AMMV2Factory on Lux C-chain
---
# PostgreSQL for Graph Node
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: graph-postgres-data
namespace: lux-mainnet
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 50Gi
---
# IPFS store — graph-node re-reads the subgraph manifest from IPFS on every
# restart, so this cannot be an emptyDir or a restart deletes the deployment.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: graph-ipfs-data
namespace: lux-mainnet
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: graph-postgres
namespace: lux-mainnet
spec:
serviceName: graph-postgres
replicas: 1
selector:
matchLabels:
app: graph-postgres
template:
metadata:
labels:
app: graph-postgres
spec:
containers:
- name: postgres
# MUST match the on-disk PGDATA major version. The volume was
# written by 18.4; postgres:16 dies with FATAL "unrecognized
# configuration parameter autovacuum_worker_slots" and takes
# graph-node down with DatabaseUnavailable.
image: postgres:18-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
value: graph
- name: POSTGRES_PASSWORD
value: graph-lux-2026
- name: POSTGRES_DB
value: graph_node
- name: POSTGRES_INITDB_ARGS
value: "-E UTF8 --locale=C"
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: data
persistentVolumeClaim:
claimName: graph-postgres-data
---
apiVersion: v1
kind: Service
metadata:
name: graph-postgres
namespace: lux-mainnet
spec:
selector:
app: graph-postgres
ports:
- port: 5432
---
# IPFS node for subgraph deployment artifacts
apiVersion: apps/v1
kind: Deployment
metadata:
name: graph-ipfs
namespace: lux-mainnet
spec:
replicas: 1
selector:
matchLabels:
app: graph-ipfs
template:
metadata:
labels:
app: graph-ipfs
spec:
containers:
- name: ipfs
image: ipfs/kubo:v0.28.0
ports:
- containerPort: 5001 # API
- containerPort: 8080 # Gateway
volumeMounts:
- name: data
mountPath: /data/ipfs
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: graph-ipfs-data
---
apiVersion: v1
kind: Service
metadata:
name: graph-ipfs
namespace: lux-mainnet
spec:
selector:
app: graph-ipfs
ports:
- name: api
port: 5001
- name: gateway
port: 8080
---
# Graph Node ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: graph-node-config
namespace: lux-mainnet
data:
config.toml: |
[store]
[store.primary]
connection = "postgresql://graph:[email protected]:5432/graph_node?sslmode=disable"
weight = 1
pool_size = 10
[deployment]
[[deployment.rule]]
shard = "primary"
indexers = ["graph_node"]
[chains]
ingestor = "graph_node"
[chains.lux]
shard = "primary"
# All five validators serve the same C-Chain at the same height, and the
# historical backfill is RPC-latency-bound, not CPU-bound — graph-node
# spreads requests across the providers of a chain, so listing all five is
# ~5x the backfill throughput at no cost. Labels MUST be unique ("Provider
# labels must be unique" is a hard config error, not a warning). luxd is
# archive (eth_call at block 49 returns WLUX), which the subgraph needs for
# token metadata.
provider = [
{ label = "luxd-0", url = "http://luxd-0.luxd-headless.lux-mainnet.svc:9630/v1/bc/C/rpc", features = ["archive"] },
{ label = "luxd-1", url = "http://luxd-1.luxd-headless.lux-mainnet.svc:9630/v1/bc/C/rpc", features = ["archive"] },
{ label = "luxd-2", url = "http://luxd-2.luxd-headless.lux-mainnet.svc:9630/v1/bc/C/rpc", features = ["archive"] },
{ label = "luxd-3", url = "http://luxd-3.luxd-headless.lux-mainnet.svc:9630/v1/bc/C/rpc", features = ["archive"] },
{ label = "luxd-4", url = "http://luxd-4.luxd-headless.lux-mainnet.svc:9630/v1/bc/C/rpc", features = ["archive"] }
]
---
# Graph Node StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: graph-node
namespace: lux-mainnet
spec:
serviceName: graph-node
replicas: 1
selector:
matchLabels:
app: graph-node
template:
metadata:
labels:
app: graph-node
spec:
containers:
- name: graph-node
image: graphprotocol/graph-node:v0.35.1
ports:
- containerPort: 8000 # GraphQL HTTP
name: graphql
- containerPort: 8001 # GraphQL WS
name: ws
- containerPort: 8020 # JSON-RPC (admin/deploy)
name: admin
- containerPort: 8030 # Index node status
name: status
- containerPort: 8040 # Metrics
name: metrics
env:
- name: GRAPH_NODE_CONFIG
value: /etc/graph-node/config.toml
# graph-node normalises node_id to snake_case internally; the
# deployment rule must use the SAME literal or deployments are
# assigned to a node that does not exist and never index.
- name: node_id
value: graph_node
- name: GRAPH_LOG
value: info
- name: ipfs
value: graph-ipfs.lux-mainnet.svc:5001
- name: ETHEREUM_REORG_THRESHOLD
value: "1"
- name: ETHEREUM_ANCESTOR_COUNT
value: "1"
# Backfill tuning. 96369 has ~1.1M blocks of which only the first
# ~410k carry any DEX activity, so the scan spends most of its time
# walking empty ranges one 2000-block default at a time. Larger
# ranges + a deeper block batch cut the round trips that dominate.
- name: GRAPH_ETHEREUM_TARGET_TRIGGERS_PER_BLOCK_RANGE
value: "1000"
- name: GRAPH_ETHEREUM_MAX_BLOCK_RANGE_SIZE
value: "10000"
- name: GRAPH_ETHEREUM_BLOCK_BATCH_SIZE
value: "50"
volumeMounts:
- name: config
mountPath: /etc/graph-node
resources:
requests:
memory: "2Gi"
cpu: "500m"
limits:
memory: "4Gi"
cpu: "2000m"
volumes:
- name: config
configMap:
name: graph-node-config
---
apiVersion: v1
kind: Service
metadata:
name: graph-node
namespace: lux-mainnet
spec:
selector:
app: graph-node
ports:
- name: graphql
port: 8000
- name: ws
port: 8001
- name: admin
port: 8020
- name: status
port: 8030
---
# IngressRoute for subgraph.lux.network
apiVersion: hanzo.ai/v1alpha1
kind: IngressRoute
metadata:
name: graph-node
namespace: lux-mainnet
spec:
entryPoints:
- websecure
routes:
- match: Host(`subgraph.lux.network`)
kind: Rule
services:
- name: graph-node
port: 8000
tls:
certResolver: letsencrypt
-133
View File
@@ -1,133 +0,0 @@
# Exchange API — the /v1/graphql backend every exchange SPA reads for pools,
# tokens, TVL and volume. Source: github.com/luxfi/exchange-api.
#
# Routed via:
# - api-exchange.lux.network (IngressRoute, direct)
# - api.zoo.exchange/v1/*
# - api.lux.network/v1/dex/*
#
# DATA SOURCE — read this before editing the env below.
#
# This service is a thin resolver in front of a graph. That graph used to be a
# Graph Protocol graph-node + graph-postgres + graph-ipfs stack in lux-mainnet,
# and this file still described that wiring long after the cluster stopped
# running it: the Deployments were gone, their Services had no endpoints, and
# the live pods had been repointed at the unified explorer's built-in graph
# engine (luxfi/graph) by hand. Applying the stale file would have swung a
# working exchange back onto three services that do not exist.
#
# The explorer IS the graph now — one indexer, one store, one GraphQL surface
# per chain at /v1/graph/{slug}/{amm,dex}/graphql. There is no second graph to
# bring back.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: exchange-api
namespace: lux-ns
labels:
app: exchange-api
spec:
replicas: 2
selector:
matchLabels:
app: exchange-api
template:
metadata:
labels:
app: exchange-api
spec:
imagePullSecrets:
- name: ghcr-luxfi
containers:
- name: exchange-api
image: ghcr.io/luxfi/exchange-api:2.3.28@sha256:b640cee416490deeb1fd133f4d58d6f2c4a99cf9e88d6ba9c384d27484278695
imagePullPolicy: IfNotPresent
ports:
- containerPort: 4000
env:
- name: PORT
value: "4000"
# The AMM (pools/tokens/swaps) and DEX (CLOB) views of this network's
# chain, both served by the unified explorer's graph engine.
# The FE's explore page sends its combined AMM query (bundle /
# factories / tokens / pools / *DayDatas — the v3 schema) straight
# through /v1/graphql to this endpoint and reads TVL off
# pools[].totalValueLockedUSD. The explorer shim answers that schema
# but returns "" for every USD field, which is why the TVL tiles read
# $0 on a chain with $3.0M locked. graph-node carries the real numbers.
- name: SUBGRAPH_URL
value: "http://graph-node.lux-mainnet.svc.cluster.local:8000/subgraphs/name/luxfi/amm-v3"
# The v2-shaped helpers (pairs / uniswapFactories / reserveUSD) keep
# reading the shim — graph-node serves the v3 schema only.
- name: SUBGRAPH_V2_URL
value: "http://explorer.lux-mainnet.svc.cluster.local:8090/v1/graph/cchain/amm/graphql"
- name: DEX_SUBGRAPH_URL
value: "http://explorer.lux-mainnet.svc.cluster.local:8090/v1/graph/cchain/dex/graphql"
# The V3 (concentrated-liquidity) view comes from the real graph-node
# subgraph, not the explorer shim: the shim answers the v3 schema but
# returns "" for every USD field, so TVL/volume render blank. This
# deployment carries the priced numbers (TVL, volumeUSD, derivedETH).
- name: SUBGRAPH_V3_URL
value: "http://graph-node.lux-mainnet.svc.cluster.local:8000/subgraphs/name/luxfi/amm-v3"
# Selects this deployment's network defaults (chainId, contracts).
- name: NETWORK
value: mainnet
# C-Chain RPC used to verify a market's tokens actually have code on
# THIS chain. luxd serves /v1 only — /ext 404s, and a 404 here makes
# every token look codeless, which empties the markets surface and
# reads as "no markets yet" rather than as a misconfiguration.
- name: LUX_RPC_URL
value: "http://luxd-headless.lux-mainnet.svc.cluster.local:9630/v1/bc/C/rpc"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 4000
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 4000
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: exchange-api
namespace: lux-ns
labels:
app: exchange-api
spec:
type: ClusterIP
selector:
app: exchange-api
ports:
- port: 4000
targetPort: 4000
protocol: TCP
---
apiVersion: hanzo.ai/v1alpha1
kind: IngressRoute
metadata:
name: exchange-api
namespace: lux-ns
spec:
entryPoints:
- websecure
routes:
# api.lux.exchange is live on this route too — dropping it would 404 every
# caller using that host.
- match: Host(`api-exchange.lux.network`) || Host(`api.lux.exchange`)
kind: Rule
services:
- name: exchange-api
port: 4000
tls:
certResolver: letsencrypt