zeekayandHanzo Dev 1953a3a3d3 explorer: name the venue this chain now has
The AMM landed on 96369 on 2026-09-09 (luxfi/standard script/venue.ts,
recorded at deployments/venue/96369.json), so the four fields this file
left unset because nothing was deployed here have addresses to carry:
the two factories whose PairCreated and PoolCreated the subgraph trusts,
the quoter that prices a concentrated route, and the SwapRouter02 a swap
is addressed to.

Changing either factory changes the store's source fingerprint, so the
indexer drops its derived rows and re-reads from block 0 rather than
looking for events already behind its cursor. Applied and restarted: it
read blocks 1..59, 255 events, and values 16 pools at $39,899,893.58 with
LUX at one dollar.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-09 00:05:41 -07:00
2026-09-01 10:10:41 -07:00
2026-09-04 22:54:51 -07:00
2026-07-26 15:46:04 -07:00

universe

Lux Universe

Infrastructure monorepo for Lux Network. Compose files, Kubernetes manifests, gateway configs, and deployment scripts.

Lux Ecosystem

Repo Purpose Stack
lux/node Core blockchain node (luxd) Go
lux/wallet @luxfi/wallet (extension, mobile, web) TypeScript
lux/exchange DEX at lux.exchange (apps/web, apps/mobile) Vite + Tamagui
lux/web lux.network marketing site Next.js 14
lux/ui @luxfi/ui component library React
lux/papers 111 research papers LaTeX
lux/proofs Formal verification (Lean 4, TLA+, Halmos, Tamarin) Multi

Structure

compose*.yml        Docker Compose for local dev (mainnet/testnet/devnet)
deploy/             CD values, including the sole luxd definitions for all three networks
k8s/                Supporting Kubernetes manifests (no duplicate luxd workloads)
configs/            Node and gateway configuration
chain-configs/      Per-chain EVM configuration
scripts/            Deployment and management scripts
docker/             Dockerfiles
iam/                IAM init data for lux.id
e2e/                Playwright E2E tests (~488 cases)

Networks

The validators run as docker containers on the LAN host evo, not in a cluster. do-sfo3-lux-k8s is a DigitalOcean context and that account is gone; deploy/lux-*/luxd.yaml still addresses luxd-headless.lux-mainnet.svc.cluster.local:9630, a Service no cluster here serves. Read those files as the image pin and chain config they carry, not as a description of where anything runs.

Network Network ID C-Chain ID HTTP ports on evo luxd
Mainnet 1 96369 9730 9740 9750 9760 9770 5/5
Testnet 2 96368 9830 9840 9850 9860 9870 5/5
Devnet 3 96367 9930 9940 9950 9960 9970 5/5

Node i answers on 9730+10i, stakes on 9731+10i and keeps its ledger in /var/lib/luxd/mainnet/data-i. scripts/validators.sh is the recipe: it holds every flag the containers carry and rolls them one at a time, which is the only safe order at sample 5 / quorum 4.

app chain EVM Chain IDs

app chain EVM Coin Mainnet Testnet Devnet
Zoo ZOO 200200 200201 200202
Hanzo AI 36963 36962 36964
SPC SPC 36911 36910 36912
Pars PARS 494949 494950 494951

Quick Start

git clone --recursive [email protected]:luxfi/universe.git
cd universe

# Local development
docker compose -f compose.mainnet.yml up -d

# Production: publish through the CD-managed deploy/ values. See the note below.

kubectl apply -k k8s/lux-k8s is not the luxd deploy path. The kustomization does not build (it pulls raw files from sibling directories, which kustomize refuses), and if it did build it would regress unrelated live services. Luxd is reconciled from the three CD-managed files under deploy/. The retired LuxNetwork and handwritten StatefulSet copies were removed so an old pin cannot be applied over the live fleet.

Production (lux-k8s)

LB: 134.199.138.27. All *.lux.network services.

Namespace Services
lux-system hanzo-ingress (Traefik), lux-operator
lux-mainnet 5 luxd validators
lux-testnet 5 luxd validators
lux-devnet 5 luxd validators
lux-gateway Gateway API gateway
lux-explorer Lux Explorer (single Go binary: indexer+GraphQL+SPA, all 5 chains), exchange, indexers, faucets
lux-bridge Bridge UI + server
lux-mpc MPC dashboard + API (5 nodes, threshold 3)
lux-kms KMS API

Key Images (GHCR)

Image Purpose
ghcr.io/luxfi/node Blockchain node
ghcr.io/luxfi/explorer Lux Explorer (single Go binary: indexer+GraphQL+SPA)
ghcr.io/luxfi/operator K8s operator
ghcr.io/luxfi/gateway Gateway

RPC Endpoints

Each network has its own host and serves a flat /v1 path. There is no /mainnet or /ext path segment — never hand-build one.

mainnet  https://api.lux.network/v1/chain/{C,P,X}/rpc         chainId 96369
testnet  https://api.lux-test.network/v1/chain/{C,P,X}/rpc    chainId 96368
devnet   https://api.lux-dev.network/v1/chain/{C,P,X}/rpc     chainId 96367

App chains serve the same shape from their own host: api.zoo.network, api.hanzo.network, api.pars.network (-test / -dev suffix per network).

E2E (Playwright)

One axis, one viewport set, one place where environments are defined.

make e2e-install                 # npm ci + chromium

make e2e-live                    # IS THE CHAIN ALIVE — ~25s, run this first
make e2e-gate                    # the go-live gate — minutes, all three frames
make e2e                         # everything, mainnet (default), all three viewports
make e2e-testnet                 # same specs, testnet endpoints
make e2e-devnet
make e2e-local                   # needs: make start network=main  +  ./local/up.sh

make e2e-surfaces                # every declared route of every product, anonymously
make e2e-console                 # all 43 lux.cloud dashboard routes, SIGNED IN
make e2e-institutional           # the 27-screen institutional score

make e2e vp=mobile               # one viewport
make e2e spec=e2e/01-explorer.spec.ts
make e2e-health                  # per-surface targets honour LUX_ENV too
make e2e-report                  # HTML report
make e2e-shots                   # open the screenshots for this env

make e2e is ~1977 cases (659 × 3 frames) and takes hours: the very large legacy broker/exchange specs are serial within their own file, so no amount of workers shortens them. make e2e-gate is the subset you run before a go-live — chain health, explorer, validator lifecycle, DeFi, bridge/MPC, DEX contracts, network independence, app chains, plus the login proof when LUX_E2E_PASSWORD is exported.

Concurrency is per-environment and measured (WORKERS in e2e/env.ts), because the environments do not have the same front door. Mainnet's api host is CDN-fronted and ran the gate at 4 workers with 342/342 green; one worker and four returned an identical result set (154 passed / 2 failed) in 5.9m vs 2.3m. Testnet and devnet resolve straight to the cluster LB with no CDN, and at 4 workers the same gate lost 66 cases to gateway throttling that all passed at 2 — it is aggregate load that tips it, so the budget belongs to the environment rather than to any one spec.

LUX_ENV=local|devnet|testnet|mainnet (default mainnet) is the only environment knob. e2e/env.ts resolves every base URL, RPC endpoint, network ID and chain ID from NETWORKS.yaml — the config, helpers.ts and the specs all derive from that one module, so changing a chain ID in the registry changes the suite. The old two-axis ENV=production|local + LUX_NETWORK=… model is retired and LUX_NETWORK now fails loudly instead of being half-honoured.

local targets the one local stack (127.0.0.1:9630 node, :8899 IAM) and reads the mainnet column, because make start network=main boots a mainnet-genesis chain. Only RPC and explorers are per-network: every app surface (cloud, id, wallet, bridge, exchange, staking, mpc, safe) is a single deployment shared by all remote envs — test.lux.cloud and dev.lux.cloud are 404.

Is the chain alive — e2e/00-liveness.spec.ts

The one question the suite did not ask. 00-health has nine tests that all reduce to eth_blockNumber > 0 — "node reports healthy", "C-Chain is bootstrapped", "all primary chains bootstrapped" — and a chain frozen since Thursday answers every one of them. Measured 2026-07-26, with the whole suite green:

network chainId tip tip age pods
mainnet 96369 1,098,191 58.9 hours 5/5 Ready
testnet 96368 1,354 3.6 min 5/5 Ready
devnet 96367 528 3.3 min 5/5 Ready

Gate on the tip, never on readiness. Readiness is a claim the node makes about itself; the tip is a fact.

A fixed "a block every N seconds" threshold is also wrong, because these chains are demand-driven — an idle one legitimately produces nothing until something asks. So the gate calibrates itself: the tip may be no older than 4× the chain's own median inter-block gap over its last 8 blocks, floored at 600s. Measured cadences are 7s on mainnet (while it was producing) and ~300s on testnet/devnet (a heartbeat CronJob), which one constant could never span. The median matters: a single 27,375-second gap — which is what a freeze looks like from inside the block list — would let a stall widen its own budget if this used a mean.

Four orthogonal tests, no overlap: fresh (tip age vs derived budget), monotonic (the tip never goes backwards and a served block keeps its hash — reorg/fork detection, which is what four competing blockIDs at height 512 looked like on devnet), consistent (net_version agrees with NETWORKS.yaml), and honest — when the P-Chain reports a full validator set, the C-Chain tip must be fresh. That last one asserts the readiness lie head-on: bootstrapped-and-frozen is the state that read 5/5 Ready across the fleet, and it now fails.

The whole fleet is read in one pass too, so any single run says which of the three networks is moving.

On mainnet today this is RED and it should stay red until the chain moves. Do not widen the budget to get a green run.

Every page of every product — e2e/routes.ts

Coverage used to be a function of which spec somebody happened to write. Safe's 31 routes, MPC's 15 and the entire admin surface had zero tests between them, while api.lux.financial — whose API is dead — carried 293.

So the routes are data: one list per surface in e2e/routes.ts, iterated by 60-surfaces (anonymous) and 61-console (signed in). Adding a page to a product is one line there and it is covered at all three viewports on every environment. No spec hard-codes a path.

HTTP status is not evidence. Seven of the nine origins answer 200 for a path that does not exist (lux.cloud, exchange, bridge, lux.id, wallet, mpc, superadmin — measured with a /zzz-bogus control against each); only safe.lux.network and explore.lux.network 404 honestly, and those two get a live control test each run so the distinction stays true rather than remembered. Every route is therefore opened in a real browser and has to have painted: real text into a non-empty root, and not the app's own error boundary. Two separate assertions, because a crash screen sails through a length check — exchange /pool serves HTTP 200 with 553 characters, all of which say "Something went wrong".

That is what a route sweep is for, and it found on its first run:

  • exchange /positions, /pool, /pool/v2, /bridge, /teleport — crash screen at HTTP 200
  • exchange /vote — white screen, 0 characters, 0 root children
  • explorer /stats, /advanced-filter — crash screen at HTTP 200

Protected routes are asserted as protected: the MPC console's 13 signed-in screens, /bridge/fees among them, must bounce an anonymous browser to a sign-in screen. Serving them would be the finding, so the redirect is the pass.

61-console opens all 43 /dashboard routes with a real session, and holds the nine screens the owner named — treasury, safes, mpc, bridge, wallets, validators, dex, chains, deploy — to a higher bar: each must name itself and show which chain it is talking about (a network name, a chainId, an address or a height). Five of the nine currently show none of those.

61-console fails rather than skips when LUX_E2E_PASSWORD is unset. A skipped authenticated sweep is the same colour as a passing one, and the whole point of the file is that nobody had checked.

Viewports are projects

mobile 390x844, tablet 834x1194, laptop 1440x900 — so "mobile and desktop" means the same three frames on every run, and every screenshot is comparable across runs and environments.

Screenshots

~/Desktop/lux-e2e/<env>/<surface>-<viewport>.png, written on pass and fail — a failure you cannot look at is a failure you cannot fix. Deliberate frames come from snap(page, surface); a failing test's dying state is copied out of Playwright's run directory by e2e/shot-reporter.ts as fail-<spec>-<test>-<viewport>.png.

Proving sign-in

export LUX_E2E_PASSWORD='…'      # never committed; LUX_E2E_USER defaults to [email protected]
make e2e-login

e2e/50-login.spec.ts drives the real flow — lux.cloud → "Continue with Lux ID" → lux.id → /dashboard — and refuses the cheap answers, because every cheap answer lies here:

  • HTTP 200 on /dashboard proves nothing: it is an SPA, so an anonymous visit is also 200 and also stays on the /dashboard URL.
  • "the password field vanished" proves nothing: the OIDC callback parks on an interstitial that has no form either, and /auth/callback with no code renders "Sign-in failed · Missing authorization code in callback URL" at HTTP 200.

So the proof is the access token: the run ends with a JWT in sessionStorage whose iss is the IdP and whose email is the account that was typed, and GET lux.id/v1/iam/oauth/userinfo accepts it (that endpoint returns 401 for no token and 401 for a junk token, so a 200 is only reachable with a token the IdP really minted). That is the exact leg that breaks in an invalid_client regression — the IdP still issues a code and the browser still lands on /dashboard, but the code→token exchange 401s and no token is ever stored. The spec fails on Sign-in failed, invalid_client, PKCE is required, a missing authorization code, a parked interstitial, or still being on the IdP.

It also asserts the handoff is a public PKCE client (client_id=lux-cloud, code_challenge_method=S256, redirect https://lux.cloud/auth/callback, no secret), and it carries its own negative control: a test that proves an anonymous /dashboard holds no token and offers no sign-out, so the positive assertions cannot go vacuous.

License

Proprietary - Lux Industries Inc. All rights reserved.

S
Description
Declared state for the lux fleet: one values file per service, one ApplicationSet
Readme BSD-3-Clause
6.7 MiB
Languages
TypeScript 66.4%
Shell 14.9%
Python 7.9%
Go 4.8%
Makefile 4.3%
Other 1.7%