Files
bridge/compose.testnet.yml
T
zeekay 90e4d49c68 ops: production network configs + operator deploy doc (mainnet + testnet)
Operator-facing configuration assets from origin/whispers/bridgev2.
No code changes — purely declarative configs and ops docs.

  cmd/bridge/networks.mainnet.yaml (NEW, 270 LOC)
  cmd/bridge/networks.testnet.yaml (refresh, +186 LOC)
    Per-network deposit + release wallet configuration for the
    cmd/bridge daemon. mainnet config is the first production-ready
    file in tree.

  compose.mainnet.yml (NEW, +78 LOC)
  compose.testnet.yml (refresh, +56 LOC)
    Compose stacks for the standalone mainnet/testnet deployments
    — Postgres + bridge-server + bridge-ui, MPC/KMS/IAM env blocks
    set per environment.

  docs/operator-deploy-phase-1-4.md (NEW, 168 LOC)
    Phase 1.4 deploy runbook: what cmd/bridge expects from
    BridgeVM, mpcd, KMS, and IAM at boot, and how to wire them in
    a real prod cluster. Pairs with REQUIREMENTS.md §13 to bridge
    spec → deploy.

Build + cmd/bridge tests (5.0s) clean.

NOT included in this PR (saved for follow-ups):
  - k8s/bridge-deployment.yaml — 199 LOC delta; review separately
    so deployment edits aren't bundled with new config files
  - REQUIREMENTS.md R6 spec refresh — track in its own doc PR
  - app/bridge/src/bridge.config.ts Lux mainnet/testnet wagmi
    additions — small but a tenant-frontend change, separate slice
2026-05-28 20:20:00 -07:00

351 lines
9.8 KiB
YAML

services:
# Infrastructure Services
nats:
image: nats:2.10-alpine
container_name: bridge-nats
restart: always
ports:
- "4222:4222"
- "8222:8222"
command: "-js -m 8222 --store_dir /data"
volumes:
- nats-data:/data
networks:
- bridge-network
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "4222"]
interval: 5s
timeout: 3s
retries: 5
consul:
image: hashicorp/consul:1.17
container_name: bridge-consul
restart: always
ports:
- "8500:8500"
- "8600:8600/udp"
command: "agent -server -bootstrap-expect=1 -ui -client=0.0.0.0 -datacenter=testnet"
volumes:
- consul-data:/consul/data
networks:
- bridge-network
environment:
- CONSUL_BIND_INTERFACE=eth0
- CONSUL_DATACENTER=testnet
healthcheck:
test: ["CMD", "consul", "members"]
interval: 5s
timeout: 3s
retries: 5
# Database
postgres:
image: ghcr.io/hanzoai/sql:latest
container_name: bridge-postgres
restart: always
environment:
- POSTGRES_USER=bridge
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-testnet123}
- POSTGRES_DB=bridge
volumes:
- postgres-data:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
networks:
- bridge-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U bridge"]
interval: 5s
timeout: 3s
retries: 5
# MPC Nodes - Testnet configuration
mpc-node-0:
image: ghcr.io/luxfi/lux-mpc:latest
container_name: bridge-mpc-0
restart: always
environment:
- NODE_ID=mpc-node-0
- NODE_PORT=6000
- NATS_URL=nats://nats:4222
- CONSUL_URL=http://consul:8500
- DATA_DIR=/data/mpc
- LOG_LEVEL=debug
- MPC_THRESHOLD=2
- MPC_TOTAL_NODES=3
- MPC_KEY_PREFIX=bridge/mpc/testnet
- NETWORK=testnet
- ENABLE_TEST_MODE=true
volumes:
- mpc-data-0:/data/mpc
ports:
- "6000:6000"
- "9090:9090"
networks:
- bridge-network
depends_on:
nats:
condition: service_healthy
consul:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6000/health"]
interval: 10s
timeout: 5s
retries: 3
mpc-node-1:
image: ghcr.io/luxfi/lux-mpc:latest
container_name: bridge-mpc-1
restart: always
environment:
- NODE_ID=mpc-node-1
- NODE_PORT=6000
- NATS_URL=nats://nats:4222
- CONSUL_URL=http://consul:8500
- DATA_DIR=/data/mpc
- LOG_LEVEL=debug
- MPC_THRESHOLD=2
- MPC_TOTAL_NODES=3
- MPC_KEY_PREFIX=bridge/mpc/testnet
- NETWORK=testnet
- ENABLE_TEST_MODE=true
volumes:
- mpc-data-1:/data/mpc
ports:
- "6001:6000"
- "9091:9090"
networks:
- bridge-network
depends_on:
nats:
condition: service_healthy
consul:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6000/health"]
interval: 10s
timeout: 5s
retries: 3
mpc-node-2:
image: ghcr.io/luxfi/lux-mpc:latest
container_name: bridge-mpc-2
restart: always
environment:
- NODE_ID=mpc-node-2
- NODE_PORT=6000
- NATS_URL=nats://nats:4222
- CONSUL_URL=http://consul:8500
- DATA_DIR=/data/mpc
- LOG_LEVEL=debug
- MPC_THRESHOLD=2
- MPC_TOTAL_NODES=3
- MPC_KEY_PREFIX=bridge/mpc/testnet
- NETWORK=testnet
- ENABLE_TEST_MODE=true
volumes:
- mpc-data-2:/data/mpc
ports:
- "6002:6000"
- "9092:9090"
networks:
- bridge-network
depends_on:
nats:
condition: service_healthy
consul:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6000/health"]
interval: 10s
timeout: 5s
retries: 3
# Bridge Server - Testnet
bridge-server:
image: ghcr.io/luxfi/bridge-server:latest
container_name: bridge-server
restart: always
environment:
- NODE_ENV=staging
- PORT=3000
- DATABASE_URL=postgresql://bridge:${POSTGRES_PASSWORD:-testnet123}@postgres:5432/bridge
- NATS_URL=nats://nats:4222
- CONSUL_URL=http://consul:8500
- NODE_ID=bridge-server-0
- MPC_THRESHOLD=2
- MPC_TOTAL_NODES=3
- MPC_KEY_PREFIX=bridge/mpc/testnet
- NETWORK=testnet
- ENABLE_METRICS=true
- LOG_LEVEL=debug
- ENABLE_TEST_ENDPOINTS=true
- FAUCET_ENABLED=true
ports:
- "3000:3000"
- "9093:9090"
networks:
- bridge-network
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_healthy
consul:
condition: service_healthy
mpc-node-0:
condition: service_healthy
mpc-node-1:
condition: service_healthy
mpc-node-2:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
# Bridge UI - Testnet — Vite SPA (ghcr.io/hanzoai/spa serves /dist on :80).
# BRIDGE_* env documents what the image was built with; the values
# become real only when baked at build time (app/bridge/Dockerfile).
bridge-ui:
image: ghcr.io/luxfi/bridge-ui:latest
container_name: bridge-ui
restart: always
environment:
- BRIDGE_API_HOST=https://bridge-api-testnet.lux.network
- BRIDGE_ENV=testnet
- BRIDGE_CLIENT_ID=lux-bridge
- BRIDGE_IAM_ORG=lux-network
ports:
- "3001:80"
networks:
- bridge-network
depends_on:
- bridge-server
# Bridge unified Go binary (cmd/bridge). Local-dev / testnet equivalent
# of k8s/bridge-deployment.yaml — same image, same env contract.
#
# The image bundles the SPA via go:embed and serves both / and
# /v1/bridge/* natively. Runs alongside bridge-server + bridge-ui
# during the §13.6 cutover soak. After DNS cutover, those two
# services and the bridge UI/server images can be retired.
#
# Build locally before `docker compose up bridge-go`:
# docker build -f cmd/bridge/Dockerfile -t ghcr.io/luxfi/bridge:dev .
# Then export BRIDGE_TAG=dev (or pin :latest from CI).
bridge-go:
image: ghcr.io/luxfi/bridge:${BRIDGE_TAG:-latest}
container_name: bridge-go
restart: always
ports:
- "8080:8080"
environment:
- BRIDGE_ADDR=:8080
- BRIDGE_CONFIG=/etc/bridge/networks.yaml
- BRIDGE_DATA_DIR=/var/lib/lux-bridge
# Empty disables the legacy backend proxy — all /v1/bridge/*
# routes resolve through native handlers.
- BRIDGE_BACKEND_URL=
# BridgeVM (b-chain) JSON-RPC. Leave unset until BridgeVM is
# available; native handlers still serve quote + swap state.
- BRIDGE_BCHAIN_URL=
# MPC daemon. The lux-mpc image exposes its HTTP API on NODE_PORT
# (default 6000 per the in-compose mpc-node-* env). Adjust if a
# different mpcd build / port is in use.
- BRIDGE_MPC_URL=${BRIDGE_MPC_URL:-http://mpc-node-0:6000}
- BRIDGE_MPC_TOKEN=${BRIDGE_MPC_TOKEN:-}
- BRIDGE_MPC_ORG_ID=bridge
# Source-chain RPC overrides. Holesky's publicnode default is
# geo-blocked from many hosts; drpc is reliable. Override per
# chain if you hit rate limits.
- BRIDGE_SOURCE_RPC_OVERRIDES=ETHEREUM_SEPOLIA=https://ethereum-sepolia-rpc.publicnode.com,LUX_TESTNET=https://api.lux-test.network/ext/bc/C/rpc,BASE_SEPOLIA=https://sepolia.base.org,HOLESKY_TESTNET=https://holesky.drpc.org
volumes:
# YAML-driven SPA picker config (networks + tokens + limits).
# Mounted read-only — the k8s deployment uses a ConfigMap for
# the same purpose.
- ./cmd/bridge/networks.testnet.yaml:/etc/bridge/networks.yaml:ro
# zapdb-backed swap store + release-wallets.json. Persists
# across container restarts so in-flight swaps survive.
- bridge-go-data:/var/lib/lux-bridge
networks:
- bridge-network
depends_on:
mpc-node-0:
condition: service_healthy
mpc-node-1:
condition: service_healthy
mpc-node-2:
condition: service_healthy
# Monitoring - Lighter weight for testnet
prometheus:
image: prom/prometheus:latest
container_name: bridge-prometheus
restart: always
volumes:
- ./monitoring/prometheus/testnet.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
ports:
- "9094:9090"
networks:
- bridge-network
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=7d'
grafana:
image: grafana/grafana:latest
container_name: bridge-grafana
restart: always
ports:
- "3002:3000"
networks:
- bridge-network
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
volumes:
- grafana-data:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources
# Test Explorer
explorer:
image: ghcr.io/luxfi/bridge-explorer:latest
container_name: bridge-explorer
restart: always
environment:
- API_URL=http://bridge-server:3000
- NETWORK=testnet
ports:
- "3003:3000"
networks:
- bridge-network
depends_on:
- bridge-server
volumes:
nats-data:
consul-data:
mpc-data-0:
mpc-data-1:
mpc-data-2:
postgres-data:
prometheus-data:
grafana-data:
bridge-go-data:
networks:
bridge-network:
driver: bridge
ipam:
config:
- subnet: 172.21.0.0/16