Files
universe/k8s/STACK.md
T

16 KiB

Full Stack Standup — Lux + White-Label Exchanges

This document describes how to bring up a complete Lux-ecosystem deploy that hosts:

  • Lux Mainnet validators (Lux primary network, network ID 1)
  • app chain EVM chains: Zoo (200200), Hanzo (36963), Pars (494949), SPC (36911)
  • Per-chain backends: Gateway + indexer + GraphQL subgraph + explorer frontend
  • White-label DEX SPAs: zoo.exchange, hanzo.exchange, pars.market, lux.exchange — all running the canonical @luxfi/exchange source with their own brand, chain default, and api.<their-domain> backend
  • Cross-chain bridge: bridge.lux.network + per-brand white-label deployments via ~/work/lux/bridge

Repository map

~/work/lux/
├── exchange/          ← canonical DEX frontend (forked from Uniswap; rebranded)
│                        publishes @luxfi/exchange to npm
├── bridge/            ← cross-chain bridge (MPC 2-of-3, Warp messaging)
│                        deploys: bridge.<brand>.<tld>, multi-tenant by domain
├── explorer/          ← UNIFIED indexer + graph + explore frontend
│                        ghcr.io/luxfi/explorer:latest serves :8090
│                        ghcr.io/luxfi/indexer  ← chain data (blocks, txs, contracts)
│                        ghcr.io/luxfi/graph    ← subgraph events (DeFi, GraphQL)
│                        ghcr.io/luxfi/explore  ← Next.js frontend
├── graph/             ← GraphQL subgraph engine (Postgres-backed)
├── universe/          ← K8s manifests for everything (this dir)
├── standard/          ← Solidity contracts (V2/V3 AMM, bridge tokens, governance)
├── genesis/           ← Per-chain genesis files (alloc, chain config, precompiles)
└── cli/               ← lux-cli for chain ops (chain create/deploy, network start)

~/work/zoo/
├── exchange/          ← Zoo white-label of @luxfi/exchange (canonical example)
├── contracts/         ← Zoo bridge tokens (Z-prefix: ZETH, ZBTC, ZUSD, ZLUX, …)
├── brand/             ← @zooai/brand on npm (colors, fonts, socials, translations)
└── logo/              ← @zooai/logo on npm (SVG generators)

~/work/hanzo/
├── exchange/          ← (NEW) Hanzo white-label — see "Forking pattern" below
├── brand/             ← @hanzoai/brand
└── logo/              ← @hanzoai/logo

~/work/pars/
├── exchange/          ← (NEW) Pars white-label
├── brand/             ← @parsdao/brand
└── logo/              ← @pars/logo

Network topology

                                                         ┌─────────────┐
                            ┌──────────────────────────► │ Browser     │
                            │                            │ zoo.exchange│
                            │    HTML+JS (hanzoai/spa)   │             │
                            │                            └──────┬──────┘
                            │                                    │
                            │                            api.zoo.exchange/...
                            │                                    │
                ┌───────────┴────────────┐               ┌───────▼─────────┐
                │ hanzoai/ingress (DOKS) │               │ Cloudflare      │
                │ TLS via cert-manager   │◄──────────────┤ DNS + edge      │
                └───────────┬────────────┘               └─────────────────┘
                            │
                            │  routes by Host header to in-cluster Service
                            │
       ┌────────────────────┼────────────────────┬──────────────────────┐
       │                    │                    │                      │
       ▼                    ▼                    ▼                      ▼
┌───────────┐      ┌───────────────┐      ┌──────────────┐      ┌──────────────┐
│zoo-exchange│      │api-gateway-   │      │explorer      │      │bridge-ui     │
│(SPA on     │      │ gateway:80    │      │(unified      │      │(MPC bridge   │
│ hanzoai/   │      │(Gateway,      │      │ luxfi/explorer│      │ frontend)    │
│ spa)       │      │ all RPC routes│      │ :8090)       │      │              │
└───────────┘      │ + indexer + …)│      └──────┬───────┘      └──────┬───────┘
                   └───────┬───────┘             │                      │
                           │ proxies              │ indexer reads       │ MPC nodes
                           │                      │ graph reads         │ sign txs
                           ▼                      ▼                      ▼
                ┌──────────────────────────────────────────────────────────┐
                │ luxd validators (StatefulSet, 5 replicas)               │
                │ Primary network + all app chain EVMs (Zoo, Hanzo, Pars, SPC)   │
                │   /v1/chain/C/rpc      → Lux C-Chain (96369)              │
                │   /v1/chain/zoo/rpc    → Zoo (200200)                     │
                │   /v1/chain/hanzo/rpc  → Hanzo (36963)                    │
                │   /v1/chain/pars/rpc   → Pars (494949)                    │
                │   /v1/chain/spc/rpc    → SPC (36911)                      │
                │   /v1/chain/{Q,Z,T,A,D,B,K}/rpc → primary-network chains  │
                └──────────────────────────────────────────────────────────┘

Per-chain gateway + explorer architecture

Each chain runs ONE backend service per concern:

chain gateway indexer graph (subgraph) explorer frontend
Lux C-Chain api.lux.network/v1/chain/c indexer-c-indexer (rolled into explorer-mainnet-v2) explore.lux.network
Zoo api.lux.network/v1/chain/zoo/rpc indexer-zoo-indexer indexer + graph + explore in ghcr.io/luxfi/explorer explore.zoo.network
Hanzo api.lux.network/v1/chain/hanzo/rpc indexer-hanzo-indexer same explore.hanzo.ai
Pars api.lux.network/v1/chain/pars/rpc indexer-pars-indexer same explore.pars.network
SPC api.lux.network/v1/chain/spc/rpc indexer-spc-indexer same explore.spc.network

Unified explorer image (ghcr.io/luxfi/explorer:latest, source ~/work/lux/explorer/):

  • Single container, port 8090
  • Combines ghcr.io/luxfi/indexer (chain data) + ghcr.io/luxfi/graph (DeFi events, GraphQL endpoint at /v1/graphql) + ghcr.io/luxfi/explore (Next.js UI)
  • chains.yaml ConfigMap declares which chains it indexes; default tracks all primary + app chain EVM chains
  • Storage: per-chain SQLite at /data/<chain>/index.db (WAL mode)
  • Optional snapshot replication to s3.lux.cloud

White-label exchanges hit:

  • api.<brand>.<tld>/v1/chain/<chain>/rpc for raw RPC (proxied via Gateway to luxd)
  • api.<brand>.<tld>/v1/graphql for swaps/pools/positions/historical (proxied to graph engine)
  • api.<brand>.<tld>/api/indexer/indexer-<chain> for block/tx/contract metadata

If a brand wants their OWN per-chain backend (sovereign indexer, custom subgraph), they can:

  1. Run ghcr.io/luxfi/explorer with their own chains.yaml (track only their chain)
  2. Point api.<brand>.<tld> at THEIR explorer instead of the shared api-gateway-gateway
  3. Resolve cross-chain queries via Warp messaging or the canonical api.lux.network

White-label DEX standup

Every WL exchange follows the same shape:

~/work/<brand>/exchange/
├── apps/web/src/main.tsx       ← thin shim, ~50 lines
│                                  imports @luxfi/exchange + @<brand>ai/brand + @<brand>ai/logo
├── apps/mobile/                ← Expo iOS+Android
├── brand-assets/               ← logo.svg, favicon.svg, wordmark.svg
├── csp.json                    ← brand-scoped Content Security Policy
├── featured-tokens.ts          ← landing-page token cloud (28 tokens)
├── Dockerfile                  ← FROM ghcr.io/luxfi/exchange + overlays
└── README.md

main.tsx contract (declarative React, one component):

import { createRoot } from 'react-dom/client'
import Exchange, { canonicalChains, zooMainnet } from '@luxfi/exchange'
import brand from '@zooai/brand'      // swap for @hanzoai/brand, @parsdao/brand, …
import Logo  from '@zooai/logo'
// import translations…

createRoot(document.getElementById('root')!).render(
  <Exchange
    {...brand} logo={Logo}
    chains={canonicalChains}                    // lux + hanzo + zoo + liquid (12 chains)
    defaultChain={zooMainnet}                   // → 200200
    dex={{ kind: 'gateway', url: '/'  }}        // SPA hits same-origin /v1/chain/<chain>/rpc
    auth={{ provider: 'iam', issuer: 'https://zoolabs.id', clientId: 'zoo-exchange', idHost: 'https://zoolabs.id' }}
    kms={{  url: 'https://kms.zoo.network' }}
    bridge={{ url: 'https://bridge.zoo.ngo' }}  // ~/work/lux/bridge — multi-tenant by domain
    i18n={{ 'en-US': en, 'es-ES': es, 'zh-CN': zh }}
    features={{ swap, pool, portfolio, limit, send, buy, bridge, explore, activity, nft }}
    featured={[ /* stocks (tenant-gated) / private / native chain tokens */ ]}
  />,
)

Onboarding a new white-label app (e.g. ~/work/hanzo/exchange)

  1. Fork from canonical Zoo template:

    cp -r ~/work/zoo/exchange ~/work/hanzo/exchange
    cd ~/work/hanzo/exchange
    git remote set-url origin [email protected]:hanzoai/exchange.git
    
  2. Swap brand imports in apps/web/src/main.tsx:

    - import brand from '@zooai/brand'
    - import Logo  from '@zooai/logo'
    + import brand from '@hanzoai/brand'
    + import Logo  from '@hanzoai/logo'
    - defaultChain={zooMainnet}
    + defaultChain={hanzoMainnet}
    
  3. Replace brand-assets/ SVGs with the brand's mono+color logos.

  4. Update Dockerfile:

    - tar -xzf "$(npm pack @zooai/brand@latest)" --strip-components=1
    + tar -xzf "$(npm pack @hanzoai/brand@latest)" --strip-components=1
    
  5. Stand up api.hanzo.exchange ingress (see k8s/lux-mainnet/WHITE-LABEL-API.md):

    # k8s/<brand>-exchange/api-ingress.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata: { name: api-hanzo-exchange, namespace: lux-mainnet }
    spec:
      ingressClassName: ingress
      tls: [{ hosts: [api.hanzo.exchange], secretName: api-hanzo-exchange-tls }]
      rules:
      - host: api.hanzo.exchange
        http: { paths: [{ path: /, pathType: Prefix, backend: { service: { name: api-gateway-gateway, port: { number: 80 } } } }] }
    
  6. Stand up the SPA pod (re-use k8s/exchange/overlays/zoo-mainnet/deployment.yaml, change namespace + ingress hosts + brand env vars).

  7. DNS:

    • hanzo.exchange → 134.199.138.27 (hanzo-ingress LB), Cloudflare proxied
    • api.hanzo.exchange → same LB

That's it. The new brand inherits all chain support, all swap/pool/limit/bridge functionality, all canonical token lists, all canonical RPC infra. Per-app cost is ~50 lines of main.tsx + Dockerfile overlay.

Bridge integration

~/work/lux/bridge is multi-tenant by Host header. The bridge UI at bridge.lux.network, bridge.zoo.ngo, bridge.hanzo.ai, bridge.pars.network all serve from the SAME deployment in lux-bridge namespace; the frontend detects domain and renders matching brand. To add a brand's bridge:

  1. Add the host to the bridge-ingress ingress
  2. Configure brand in app/bridge/src/lib/tenant.ts (or set BRIDGE_TENANT_CONFIG env)
  3. Point DNS at 134.199.138.27

The MPC mode (lux-mpc 3-node cluster vs t-chain ThresholdVM) is set per-tenant in the same tenant config. All Lux ecosystem brands share the same MPC custody network unless explicitly opted out.

Local dev — full stack on one machine

# 1. Lux validator (anvil-style dev mode, chain id 31337)
cd ~/work/lux/cli && go build -o bin/lux ./main.go
./bin/lux network start --local

# 2. Deploy contracts (V2 + V3 + bridge tokens + governance)
cd ~/work/lux/standard
LUX_MNEMONIC="<your-mnemonic>" forge script script/DeployFullStack.s.sol --rpc-url http://127.0.0.1:8545 --broadcast

# 3. Unified explorer + graph (with chains.example.yaml: only local chain)
cd ~/work/lux/explorer
docker compose up -d   # explorer:8090 with indexer + graph + frontend

# 4. Bridge MPC nodes
cd ~/work/lux/bridge && make up && make start-mpc-nodes

# 5. Pick ONE white-label exchange to run
cd ~/work/zoo/exchange && pnpm install && pnpm --dir apps/web dev   # vite on :5173

# Open http://localhost:5173 — talks to local luxd via api.lux.network override

Cluster deploy — production

# All manifests live under ~/work/lux/universe/k8s/
kubectl --context=lux-k8s apply -f k8s/lux-mainnet/         # validators + gateway + indexers
kubectl --context=lux-k8s apply -f k8s/lux-explorer/        # unified luxfi/explorer pod
kubectl --context=lux-k8s apply -f k8s/lux-bridge/          # MPC bridge
kubectl --context=lux-k8s apply -k k8s/exchange/overlays/zoo-mainnet      # Zoo SPA + ingress
# Lux Uniswap fork (lux.exchange + markets.lux.network + exchange.lux.network):
kubectl --context=lux-k8s apply -k k8s/exchange/overlays/lux-mainnet
# Per-env Lux exchanges (testnet/devnet) and pars-mainnet share the same base.
kubectl --context=lux-k8s apply -f k8s/lux-mainnet/api-zoo-exchange-ingress.yaml
# (replicate api ingresses for each brand)

DNS (managed in Cloudflare):

host type target proxied brand
lux.exchange A 134.199.138.27 ✓ Lux
api.lux.network A 134.199.138.27 ✓ Lux
zoo.exchange A 134.199.138.27 ✓ Zoo
api.zoo.exchange A 134.199.138.27 ✓ Zoo
hanzo.exchange A 134.199.138.27 ✓ Hanzo (TBD)
api.hanzo.exchange A 134.199.138.27 ✓ Hanzo (TBD)
pars.market A 134.199.138.27 ✓ Pars (TBD)
api.pars.market A 134.199.138.27 ✓ Pars (TBD)
<dropped — tenant-managed> A 134.199.138.27 ✓ white-label tenant
<dropped — tenant-managed> A 134.199.138.27 ✓ white-label tenant

Reference