- Terms: /terms (or NEXT_PUBLIC_TERMS_URL)
- Privacy: /privacy (or NEXT_PUBLIC_PRIVACY_URL)
- Added Regulatory Status link to lps.lux.network
- All URLs env-configurable for white-label deployments
C4 remediation: the MPC database password was stored as plaintext in
the tracked config.yaml file. Replaced with placeholder — production
reads the rotated secret via ZAPDB_PASSWORD from K8s bridge-secrets.
Add KMS_CLIENT_ID/KMS_CLIENT_SECRET Universal Auth login block
before fetching deploy credentials. Falls back to legacy
HANZO_API_KEY if Universal Auth vars are not set.
- Add mpc-signer.ts: signs EVM transactions via MPC API
- Update swaps.ts payout handler to use MPC when MPC_WALLET_ID is set
- Falls back to LUX_SIGNER private key when MPC is not configured
- MPC treasury wallets created: fee-collector + bridge-signer
- Env vars: MPC_API_URL, MPC_API_TOKEN, MPC_WALLET_ID, BRIDGE_FEE_COLLECTOR
Enable is_deposit_enabled and is_withdrawal_enabled for all tokens
across mainnet and testnet configurations to support full bridge
functionality with MPC-based deposit detection.
- 1% service fee applied when source is Lux or Zoo (exits + inter-chain)
- 0% fee when bridging IN from external chains (BTC, ETH, SOL, etc.)
- Fee deducted from payout amount, collected to BRIDGE_FEE_COLLECTOR address
- Quote and rate APIs reflect directional fee
- Fix: add @noble/ed25519 dependency for MPC signing
- Fix: string|null type compatibility in checkDepositAction
Instruments all bridge tenants (bridge.lux.network, bridge.pars.network,
bridge.hanzo.ai, bridge.zoo.ngo) with insights.hanzo.ai. Events tagged
with tenant ID and hostname for multi-tenant analytics.
The NATS subscription for MPC signing results was set up in the constructor
with a 1-second delay, but mpcService.nc was null at that point (NATS
connects during initialize()). Moved subscription to after initialize()
completes, ensuring NATS is connected before subscribing.
The compiled Docker image uses NATS-based MPC signing (not REST). The result
matching was broken because MPC nodes return tx_id (not sessionId), and the
signature is split across r, s, signature_recovery fields (not a single
signature field). This fix:
- Matches pending signatures by tx_id prefix instead of sessionId
- Reconstructs 65-byte Ethereum signature from base64 r, s, recovery values
- Removes MPC_API_KEY requirement (unused with NATS transport)
Without @tailwind base/components/utilities, Tailwind PostCSS plugin
was not generating any utility classes. Classes like bg-background,
text-foreground, flex, etc. were all missing from compiled CSS output.
Bridge UI had black text on black background because --hz-ui-* and
shadcn --foreground/--background CSS custom properties were never
defined. The Tailwind preset maps utility classes to these variables
but globals.css was missing the actual values.
Use --no-frozen-lockfile in both bridge UI and server Dockerfiles.
The workspace lockfile has overrides that don't match when building
individual packages in isolation.
- bridge Dockerfile: remove COPY contracts/package.json (excluded by
.dockerignore, causing build failure)
- server Dockerfile: use --no-frozen-lockfile to handle workspace
overrides mismatch when building server in isolation
- Remove @luxfi/ui Footer that renders a massive 1904px white SVG
triangle (LuxLogo) due to Tailwind hidden class not applying in
production build. Replace with minimal text footer.
- Set bridge fee to 1% (wallet_fee: 0.01, wallet_fee_in_usd: 1)
down from previous 10% placeholder values.
Bridge is a Node.js/Next.js web app that doesn't need native arm64
images. The arm64 build was failing with "context deadline exceeded"
on the Graviton runner, blocking the manifest creation and deploy.
Set platforms to linux/amd64 only, matching build-and-push.yml which
already builds amd64-only.
Co-authored-by: Hanzo Dev <[email protected]>
Next.js requires NEXT_PUBLIC_* env vars at build time for SSR pages.
These defaults match the production API endpoints and can be overridden
via --build-arg.
pnpm workspace creates linked node_modules for each package.
Copying only root node_modules loses the workspace package links,
causing 'next: not found' during build. Copy entire /app from deps
to preserve the full pnpm link structure.
pnpm needs git to resolve ethereumjs-abi which is referenced as a
git URL dependency. Without git installed, pnpm install fails with
ENOENT: spawn git.
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).
- Default MPC_URL to production API (mpc-api.lux-mpc.svc:8081)
- Add MPC_API_KEY support for authenticated signing
- Send X-API-Key header on all MPC requests
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..."
Remove dependency on @luxfi/ui/root-layout which calls getUserServerSide
from @hanzo/auth at prerender time. Use Inter font from next/font/google
and a minimal html/body shell directly.
- runs-on: lux-build for all jobs (native amd64, no QEMU)
- Switch cache from type=gha to type=registry (works on self-hosted)
- ui and server build in parallel, deploy auto-restarts pods on main
@luxfi/ui barrel exports re-export @hanzo/auth components. Bridge doesn't
use them (only Footer, Logo, RootLayout). Alias to false prevents webpack
from trying to parse their TypeScript source files.
Firebase webpack stub no longer needed — @hanzo/auth and @hanzo/commerce
have zero Firebase imports. Updated workspace catalog to Firebase-free versions.
@hanzo/[email protected] imports Firebase transitively during SSR page
data collection. The stub provides no-ops so the build succeeds.
Firebase is NEVER called at runtime — this is build-time only.
TODO: publish @hanzo/commerce without Firebase dep, then remove stub.
- Remove i18n from next.config.js (incompatible with App Router, caused
infinite 307 redirect loop on /)
- Make MPC node URLs configurable via MPC_URL env var (comma-separated)
- Default MPC URL changed from port 6000 to 8080 (matching actual MPC API)
Replace broken app/bridge/Dockerfile with working version that copies all
workspace deps, uses node:22, pins [email protected].0, and limits heap to 2048MB
to prevent OOM during Docker builds.
Update build-and-push.yml to inject production NEXT_PUBLIC_ env vars at
build time and support semver tags for release builds.
- Remove Utila route and webhook path rewrite from server.ts
- Make Utila gRPC client lazy via Proxy (only initializes if credentials present)
- Add NEXT_PUBLIC_BACKEND_API build-time env to bridge-ui Dockerfile
- Add postcss-import dev dependency for build compatibility
Winston file transport tried to mkdir 'logs' at module scope, causing
EACCES when running as non-root bridge user in K8s. Container logs
should go to stdout/stderr only (captured by K8s log aggregation).
With @prisma/client now in prod dependencies, prisma generate
works in the runner stage. Removes broken COPY of .prisma
directory which doesn't exist at workspace level.
@prisma/client is in devDependencies, so pnpm install --prod
doesn't include it. Copy the pre-generated client from the
builder stage instead of running prisma generate in runner.
The dist output needs to be inside app/server/ for pnpm workspace
node_modules resolution to work. Also adds prisma generate step
and changes entry point from index.js to server.js.
The userAuthApiClient.ts throws if this env var is missing. Since Next.js
validates all page modules at build time (even with force-dynamic), this
env var must be present during Docker build.
- pnpx acts as pnpm dlx, downloading latest prisma v7 from npm instead of
using locally installed v6.3.1. Switch to pnpm exec for local execution.
- Remove firebase-admin from webpack externals since externals prevents
NormalModuleReplacementPlugin from intercepting the imports. The plugin
regex now properly catches all firebase-admin/* paths and redirects to stub.
- Pin pnpm to v9 in both Dockerfiles (pnpm@latest resolves to v10 which
breaks lockfile v9.0 compatibility, causing Prisma 7 resolution)
- Replace individual webpack aliases with NormalModuleReplacementPlugin
regex to catch ALL firebase/* and @firebase/* import paths
- Add force-dynamic to /auth page to skip build-time data collection
- Rename dynamic import to avoid name collision with route segment config
firebase-admin is not installed in bridge workspace but gets imported
transitively via @hanzo/auth/server. Webpack aliases alone can't resolve
uninstalled modules - externals prevents webpack from trying to bundle them.
Previous stub only aliased during !dev builds. Now aliases ALL server
builds (dev and prod) and covers every firebase/* and @firebase/*
subpath plus firebase-admin. The stub uses a Proxy that returns noop
for any accessed property, handling all Firebase API calls safely.
Next.js evaluates all modules during build for page data collection,
including the layout which transitively imports Firebase through
@luxfi/ui and @hanzo/auth. Firebase's initializeApp() fails because
real credentials aren't available at build time.
This adds webpack aliases that redirect Firebase imports to a stub
module during server-side production builds. The real Firebase modules
still load normally in the browser at runtime.
Next.js evaluates page modules during build even with force-dynamic.
Using next/dynamic with ssr:false prevents Firebase module evaluation
during the build step, which fails because firebase-admin requires
actual credentials not available at build time.