28 Commits
Author SHA1 Message Date
zeekay f050efbaf0 Merge branch 'main' of github-zeekay:luxfi/bridge
# Conflicts:
#	cmd/bridge/admin_routes_test.go
#	cmd/bridge/api.go
#	cmd/bridge/estate_test.go
#	cmd/bridge/main.go
#	cmd/bridge/swaps_handler_test.go
#	internal/broadcast/client.go
#	internal/depositcheck/client.go
#	internal/txassembler/rpc_provider.go
#	pkg/bridge/package.json
#	pkg/bridge/src/app/styles/theme.css
2026-09-01 20:07:24 -07:00
zeekay c817afd5c0 bridge: the operator surface gets its own listener
The swap list answered anonymously with every swap id, the MPC signature
minted for it and the signed destination tx, on the port the Ingress
publishes. /metrics named the release wallets and reported which of them
could not sign. The deposit poll turned an anonymous request into a
source-chain RPC call with caller-chosen arguments.

All three move to a second listener, BRIDGE_ADMIN_ADDR, along with the
swap mutators, which still need BRIDGE_ADMIN_ROUTES on top. The edge
routes a port, so a port is the thing that can be left unrouted: k8s
publishes 8080 and gives the operator surface 8081, and the ServiceMonitor
scrapes there. A swap read by id keeps the swap's progress and drops the
signature and the raw tx; the list on the operator port carries both.

The legacy proxy exposed the same list twice. `/api/swaps/*` matched the
empty remainder and so forwarded GET /api/swaps to the backend's list;
`+` requires a sub-path. `/v1/bridge/swaps/*` forwarded arbitrary
sub-paths into /swaps/… after the prefix strip — the backend mounts its
swap router at /api/swaps, so that path space holds no routes and the
forwarder only ever produced a 404.
2026-09-01 19:39:22 -07:00
zeekay e661decabb chain routes: /v1/bc -> /v1/chain
luxd serves chain routes under /v1/chain, and the node, cli and sdk releases
already ship it. The deployed gateways still answer the old prefix, so remote
reads break until they are redeployed; the estate moves together rather than
sitting half converted.

Lines that record an observation - a probe, a status code, a past release -
keep the prefix they were written against, as do URLs on hosts that are not
ours. Avalanche endpoints move to /ext/bc, which is the path that host serves.
2026-09-01 08:20:22 -07:00
zeekay c8390b8b5c Restore the chain prefix the running nodes answer
The rename to /v1/chain is real in luxd source, but the deployed fleet has not
taken it yet: probed just now, api.lux.network, api.lux-test.network,
api.zoo.network and api.hanzo.network each answer 200 on /v1/bc/C/rpc and 404 on
/v1/chain/C/rpc. Composing /v1/chain against those hosts reaches nothing, which
is the failure the rename set out to prevent.

The move belongs with the node redeploy, not ahead of it.
2026-08-30 21:25:48 -07:00
zeekay 2a0a574b1e chain routes: /v1/bc -> /v1/chain
luxd serves chain routes under /v1/chain; the /v1/bc prefix it replaced is
gone, so anything composing it reaches nothing. Lines that record an
observation - a probe, a status code, a past release - keep the prefix they
were written against, as do URLs on hosts that are not ours.
2026-08-30 21:06:27 -07:00
zeekay 963781e962 the corporate name is Lux Industries Inc
Lux Partners Limited is not the entity behind this work. Every place the
old name stood — copyright lines, site footers, brand config, legal and
compliance text — now names the company that actually holds it.

lux.fund is the exception and is not renamed to match: that site belongs
to Lux Assets Management LLC, a different company, and it says so.

Two kinds of line were left for a person rather than rewritten. A dated
news item recording that Lux Partners Limited was established in the Isle
of Man is a historical fact, and renaming it would falsify the record. And
a sentence asserting that the company is domiciled in the Isle of Man is a
claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle
of Man company, so swapping the name there would assert something untrue.
2026-08-28 10:59:57 -07:00
venuswhispersandzeekay 49bcf61703 feat(bridge): release-wallet signability monitoring + alert rules
Port of the wallet-health work from preserve/lux-bridge-golive onto
main's architecture: canary-sign poller (10m default) against every
minted release wallet, cached snapshot surfaced on /metrics as
bridge_release_wallet_signable / _sign_latency_ms / _last_check_age,
PrometheusRule + ServiceMonitor for the o11y stack, and the operator
runbook section (re-mint, don't retry). Catches the June key-share
desync failure mode before a real payout does.
2026-08-06 21:27:25 -07:00
zeekayandHanzo Dev e7d3548f74 fix(mpc): point the keygen path at :9800 — the only port mpcd opens
Every bridge swap creation has failed since the MPC StatefulSet was cut over,
with `dial tcp <mpc-api-svc>:8081: i/o timeout`, so no deposit address was ever
minted and /health showed 229,398 broadcast ticks with 0 attempts and 0
successes. Two independent misconfigurations, each fatal on its own:

1. mpc-api-svc mapped 8081 -> targetPort 8080, but the StatefulSet launches
   `mpcd start --listen :9651 --api :9800`. `netstat -ltn` inside mpc-node-0
   lists 7301/9800/9653/9651 — nothing has ever bound 8080. The StatefulSet
   declares no readinessProbe, so all three pods were published as ready
   endpoints on the dead port and the Service read as healthy.

2. mpc-node-ingress admitted only app=bridge-server on 8080/8081. The Go
   backend that serves bridge.lux.network is app=lux-bridge, and the real API
   port 9800 was on no allow-list at all, so the packets were dropped rather
   than rejected — hence a hang instead of a connection error.

Verified against the live cluster: mpc-api-svc:8081/keygen now answers instead
of hanging, and POST /api/swaps advances from 502 `i/o timeout` to 401
`unauthorized`. Auth is a separate, still-open blocker: mpcd's validateJWT
requires iss=mpc.lux.network and aud containing mpc-api, and derives its HS256
key from --jwt-secret / MPC_JWT_SECRET (neither is set on the StatefulSet),
while bridge-mpc-token/MPC_API_TOKEN carries only user_id/org_id/role/iat/exp.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 19:45:34 -07:00
zeekay 88af06da11 rpc: move every Lux/Zoo caller off the dead /ext/ prefix onto /v1/
luxd serves exactly one HTTP prefix — /v1 (node server/http/server.go
baseURL). /ext/ was removed some releases ago, so every Lux and Zoo URL
in this repo was pointing at a 404.

Measured 2026-07-27:
  api.lux.network/v1/bc/C/rpc       200  eth_chainId 0x17871 (96369)
  api.lux.network/ext/bc/C/rpc      404
  api.lux-test.network/v1/bc/C/rpc  200  eth_chainId 0x17870 (96368)
  api.zoo.network/v1/bc/C/rpc       200  eth_chainId 0x30e08 (200200)
  api.zoo-test.network/v1/bc/C/rpc  200  eth_chainId 0x30e09 (200201)
  control api.lux.network/v1/bc/ZZZNOPE/rpc  404

ZOO_MAINNET/ZOO_TESTNET were doubly wrong: legacy prefix AND a "Z" chain
alias that 404s on both prefixes. Zoo's EVM answers under bc/C.

Avalanche endpoints deliberately keep /ext/ — that is their API, not
ours (measured: api.avax.network/ext/bc/C/rpc 200 0xa86a, /v1 404).

B-chain/T-chain URLs move to /v1 as well; BridgeVM is still undeployed
(re-verified today: /v1/bc/B/rpc 404 while /v1/bc/C/rpc 200, so the 404
is "no such chain", not "no such prefix").
2026-07-27 17:56:13 -07:00
fd3bc75a29 bridge: sync latest — docs trim, tenant config, internal broadcast/substrate/txassembler fixes (#406)
Squash of the local main lineage onto the canonical origin/main. Net tree only
(62 files): stale docs pages removed, tenant config + tests updated, minor Go
fixes, refreshed opnet-watcher build. No third-party white-label branding in
code or history.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-07 12:39:25 -07:00
venuswhispers b1241c9fda feat: added mpc sign option 2026-05-27 14:49:55 +02:00
zeekay 5ff4b6a86f k8s: draft luxfi-explorer manifest for app/explorer/ replacement (issue #387)
Lands a side-by-side draft of the new single-binary Go explorer at
`ghcr.io/luxfi/explorer` — the canonical replacement for the legacy
Next.js `app/explorer/` (`@luxbridge/explorer`).

Includes:
  - ConfigMap `luxfi-explorer-chains` — initial chain registry (lux +
    ethereum), hot-reloadable via the binary's /v1/explorer/admin/chains
    API once running.
  - Deployment — replicas=1, strategy: Recreate (single PVC writer per
    the SQLite+ZapDB embedded index docs), readiness + liveness probes
    against /healthz, resources sized small (100m/256Mi req → 2c/2Gi limit).
  - PVC 20Gi for the embedded chain index.
  - Service ClusterIP :80 → :3000.
  - Ingress on `bridge-explorer-v2.lux.network` for the soak window —
    legacy keeps `bridge-explorer.lux.network`. Cut over after 1-week QA.

Marked DRAFT in the file header. New bridge dev confirms the image tag
(pin off `:latest` before the legacy delete step) and the RPC config
before `kubectl apply`.

Status update on #387 will go in the issue once this lands on main.
2026-05-22 00:09:01 -07:00
zeekay e1be5e27ca manifests: rename VITE_BRIDGE_* env keys → BRIDGE_*
The runtime shim (window.__ENV / /__ENV.js) reads BRIDGE_* names from
the container env at boot. The previous VITE_BRIDGE_* prefix only made
sense when the build was Vite-time inlining the values, which is no
longer the case (and was the bug behind manifest+runtime drift). One
canonical name across compose+k8s+entrypoint.
2026-05-21 16:52:35 -07:00
zeekay 840e5323e4 manifests: align bridge-ui with Vite SPA (ghcr.io/hanzoai/spa, :80)
bridge-ui ships as a prebuilt Vite SPA inside ghcr.io/hanzoai/spa
(see app/bridge/Dockerfile final stage), not a Next.js dev container.
The compose + k8s manifests were still describing the old Next shape:
containerPort 3000, `npm run dev`, /app/.next volume mount, and
NEXT_PUBLIC_* env vars. None of that exists in the image anymore.

- compose.testnet.yml / compose.mainnet.yml: port 3001:80 (was 3001:3000);
  drop command + .next mount; swap NEXT_PUBLIC_* for VITE_BRIDGE_* env
  (the names app/bridge/src/bridge.config.ts reads).
- k8s/mpc-deployment.yaml: containerPort 80 (was 3000); Service
  targetPort 80 (was 3000 — broken since the static-SPA cutover); swap
  NEXT_PUBLIC_* for VITE_BRIDGE_* on the Deployment env.
- compose.yml: keep local hot-reload UX by mounting the repo root and
  running `pnpm -C app/bridge dev --host 0.0.0.0 --port 3001` against
  node:22-alpine. Dev port moves 3000 -> 3001 to match the published
  SPA convention across envs.
- scripts/start-local.sh: update printed UI URL to :3001.

compose.infra.yml and compose.local.yml don't define bridge-ui — left
untouched. scripts/launch-mpc-bridge.sh already prints :3001 and has no
Next-specific config; check-ci-status.sh only references bridge-ui as a
GHCR package name (image preserved per spec). Both verified clean.

Validated:
- docker compose -f compose.{yml,testnet,mainnet,infra,local}.yml config -q
- kubectl --dry-run=client apply -f k8s/mpc-deployment.yaml
2026-05-21 16:34:08 -07:00
zeekay 118e977946 feat: universal Teleport bridge with 35+ chain plugins
- opnet-watcher: universal binary with plugin architecture
  - 3 functional plugins (OP_NET, Solana, TON)
  - Generic EVMPlugin handles all EVM chains (Ethereum, Base, Arbitrum, etc.)
  - 17 non-EVM chain stubs (Sui, NEAR, Cosmos, XRPL, ICP, etc.)
  - Threshold signing via luxfi/crypto (CGGMP21/FROST)
  - Separate txSigner hot wallet for on-chain submission
  - Multi-tenant API with hostname-based routing
  - Confirmation depth, backing sanity checks, EIP-155 local signing
- manifests: 32 chain configs + 5 tenant configs (lux, zoo, hanzo, pars, spc)
- k8s: fix chain IDs 1330663759→4294967299, ClusterIP services, KMS secrets
- CI: conformance tests gate production deployment
2026-04-06 23:03:19 -07:00
zeekay fd545f7b09 fix: replace nginx with hanzoai/static for MPC landing pages
Replace nginx:alpine image with ghcr.io/hanzoai/static:latest,
update config paths, fix ingressClassName to hanzo.
2026-03-25 12:16:40 -07:00
zeekay 4199614654 fix: update bridge server for MPC integration and k8s deployment
Updated MPC wallet integration, network configuration, swap handling,
and added k8s MPC deployment manifest.
2026-03-12 16:13:53 -07:00
zeekay 5c00959daa feat: enable LUX<>ZOO bridging, 0% in / 1% out fees, pod hardening
- Enable deposits for native LUX and ZOO currencies on both chains
- Fee model: 0% deposit, 1% withdrawal (MPC wallet accumulates fees)
- Add securityContext to bridge MPC nodes: non-root, read-only rootfs,
  drop ALL capabilities, seccomp RuntimeDefault
- Add /tmp emptyDir for read-only rootfs compatibility
2026-03-05 16:03:27 -08:00
zeekay 307e4485cc unify all sql images to :latest (PG18)
Single version policy: ghcr.io/hanzoai/sql:latest everywhere.
No version pinning, no backwards compat, always latest stable.
2026-03-01 21:08:42 -08:00
zeekay 5a036d779b unify on hanzoai/sql:17-alpine, IngressClass ingress
All postgres images → ghcr.io/hanzoai/sql:17-alpine
All ingress classes → ingress (was nginx/hanzo)
2026-03-01 20:53:34 -08:00
zeekay 178a58ee56 fix(k8s): add mpc-api-svc for bridge-server MPC connectivity
bridge-server uses MPC_URL=http://mpc-api-svc.lux-bridge.svc:8081
mpcd API listens on :8080, so targetPort must be 8080.
Also correct headless mpc-node service ports (zap:9651, api:8080).
2026-03-01 19:46:57 -08:00
zeekay d4f7a4222b fix: MPC peer UUIDs + ZAPDB_PASSWORD env var + 5-node init container
- peers.json: use UUID node_ids matching identity files (not human names)
- Add ZAPDB_PASSWORD from bridge-secrets to MPC container env
- Add mpc-db-password key to bridge-secrets
- Init container now copies node identities 0-4 (was 0-2)
- Remove duplicate MPC_TOTAL_NODES env var on bridge-server
- LOG_LEVEL: debug → info (less noise)
2026-03-01 16:49:37 -08:00
zeekay e50be977e2 feat: 3-of-5 MPC cluster + CNAME white-label resolution
- Scale MPC from 2-of-3 to 3-of-5 threshold (5 nodes, 3 required to sign)
- Add node3 and node4 identities + keys to k8s secret and configmap
- MPC_THRESHOLD=3, MPC_TOTAL_NODES=5 env vars on statefulset
- config/mpc/config.yaml: threshold=3, total_nodes=5, ports 6003/9093 + 6004/9094
- Landing pages updated: "2-of-3" → "3-of-5" throughout
- tenant.ts: CNAME-first resolution — any hostname CNAMEd to bridge.lux.network
  gets auto-resolved; BRIDGE_TENANT_CONFIG env supports wildcard (*.domain)
  for Bootnode / self-hosted deployments without image rebuilds
2026-03-01 16:43:11 -08:00
zeekay 11d201da98 feat: white-label multi-tenant bridge UI
- Add tenant config system (lib/tenant.ts) — hostname → branding + MPC mode
- Tenants: bridge.lux.network, bridge.pars.network, bridge.hanzo.ai, bridge.zoo.ngo
- Middleware passes x-tenant-hostname header for SSR + API routes
- /api/tenant endpoint resolves tenant config by hostname
- TenantProvider React context applies CSS vars (--brand-primary, --brand-accent)
- Layout reads tenant config server-side, injects CSS vars + favicon + metadata
- Header supports logoSrc prop for custom logo override per tenant
- next.config.js allows remote logo images from all tenant domains
- k8s ingress: added rules for bridge.pars.network, bridge.hanzo.ai, bridge.zoo.ngo
- MPC backend pluggable: lux-mpc (3-node cluster) or t-chain (ThresholdVM)
- Self-hosted deployments supported via BRIDGE_TENANT_CONFIG env var

Anyone can point a domain at the bridge + add config → instant white-label bridge.
All tenants share the same lux-mpc backend (no per-network SPOF).
2026-03-01 16:35:30 -08:00
zeekay 98ea565c71 fix: use ghcr.io/luxfi/mpc (not lux-mpc) for MPC image name 2026-02-27 19:29:26 -08:00
zeekay 7bb8fb31c6 feat: deploy MPC 3-node cluster with proper config and identities
- Fix image refs: ghcr.io/luxfi/lux-mpc, hashicorp/consul, ghcr.io/luxfi/bridge-server
- Add ConfigMap with config.yaml, peers.json, node identities
- Add Secret with Ed25519 private keys for all 3 nodes
- Init container copies config/identities to ephemeral /app dir
- Override CMD to use legacy mode: mpcd start -n <node-name>
- Peers registered in Consul KV (mpc_peers/node{0,1,2})
- All 3 nodes running, peer discovery working, signing consumer subscribed
2026-02-27 19:16:20 -08:00
zeekay 8a83b67013 fix: disable RainbowKit SSR and correct bridge-ui K8s env vars
RainbowKit with ssr:true crashes server render (WalletConnect tries
localStorage on Node.js). K8s deployment had wrong env var name
(NEXT_PUBLIC_API_URL) instead of NEXT_PUBLIC_BRIDGE_API, causing
getBridgeSettings() to fail and UI to show "Bridge is loading..."
2026-02-27 17:26:05 -08:00
zeekay a1b78ae8cb Integrate new KMS + MPC services 2025-08-03 15:52:32 -05:00