User feedback was that my first Cloud HSM pass had three architectural
weaknesses: (1) per-provider top-level kinds vs a single cloud_hsm
discriminator, (2) SA-JSON-in-KMS auth pattern, (3) missing first-class
algorithm + structured error codes. Plus the orthogonal point that for
bridge.lux.network specifically the right topology is m-chain (native
MPC) + optional f-chain (FHE attestation) and NO external cosigners.
This commit applies all of that.
SDK (@luxfi/bridge 1.0.4 → 1.0.5):
- BridgeCloudHsmConfig — single, uniform shape:
provider: 'gcp_kms' | 'aws_kms' | 'azure_key_vault' | 'vault_transit'
keyRef: <self-describing per-provider URI form>
algorithm: 'secp256k1_ecdsa_sha256' | 'ed25519' | 'rsa_pss_sha256'
identityHint?: <non-secret workload-identity scope hint>
Replaces the three separate gcpKms / awsKms / azureKv blocks from
the first pass. Tenants layer multiple HSMs via the cloudHsm array
on BridgeMPCConfig.
- BridgeFChainConfig — Lux Network native FHE attestation:
publicUrl: <f-chain cluster URL>
scheme?: 'ckks' | 'bgv' | 'bfv'
Lives alongside m-chain on the Lux primary network. Cosigns the
same txHash that m-chain signed via FHE-secured key material so
the message never crosses a plaintext threshold quorum. PQ-safe.
- Wire shape (bridge-api.ts CosignerIntent) collapses the three
cloud_hsm variants into one canonical record with the provider
sub-discriminator. Adds the fchain wire kind.
- useTransfers iterates cfg.mpc.cloudHsm[] + emits the optional
fchain intent.
Backend (app/server):
- CloudSigner interface matches user's exact spec:
readonly provider: CloudHsmProvider
sign(input: CloudSignInput): Promise<CloudSignResult>
where CloudSignInput / CloudSignResult carry the structured metadata
(keyRef, digest, algorithm, identityHint, requestId / signature,
publicKeyRef, provider, keyVersion, signedAt).
- CloudSignerError class with stable CloudSignerErrorCode union:
permission_denied | key_not_found | key_disabled
→ maps to CosignResult.status = 'rejected'
algorithm_mismatch | invalid_digest
→ maps to 'rejected' (terminal config errors)
rate_limited | network_error | provider_unavailable
→ maps to 'failed' (retryable infra errors)
statusForErrorCode() does the mapping; the dispatcher never has to
string-match on error messages again.
- Real GCP CKMS adapter:
✅ Workload-identity ONLY (no SA-JSON-in-KMS fallback). The
backend trusts the cloud's native identity layer.
✅ identityHint is non-secret context, not credentials.
✅ Algorithm allow-list — only secp256k1_ecdsa_sha256 wired today;
ed25519 / rsa_pss_sha256 reject early with algorithm_mismatch.
✅ Error mapping covers gRPC codes 5/7/8/9/14/16 + string
fallbacks. mapGcpError centralizes it.
- AWS KMS / Azure Key Vault / Vault Transit adapters are stubs that
return CloudSignerError 'provider_unavailable' with a clear marker.
Each stub has a full implementation sketch in comments so the next
dev (or me, next session) drops in @aws-sdk/client-kms / etc. and
wires through cleanly.
- f-chain (FHE attestation) stub — waiting on @luxfi/fchain workspace.
- Persistence: cloud_hsm rows persist as 'cloud_hsm:<provider>' in
CosignerStep.kind to avoid a Prisma migration. public_id = key_ref;
api_host = identity_hint; vault_id = algorithm. rowToIntent inverts
cleanly. First schema migration round will split into dedicated
columns; the compound kind is a stable round-trippable hack.
Tests: 77/77 passing.
pkg/bridge: 58/58 (unchanged)
app/server: 16 validator (now covers all 4 cloud providers + fchain)
+ 10 Fireblocks
+ 13 Utila
+ 7 persistence
+ 19 GCP KMS + Cloud HSM stubs + fchain
+ 12 new cloud-HSM validator cases
Lux Network topology (bridge.lux.network and all Lux-branded tenants):
- Primary: m-chain (native CGGMP21 / FROST threshold MPC)
- Optional: f-chain (FHE attestation, also native to Lux)
- NO external cosigners (Utila / Fireblocks / Cloud HSM) by default
- These cosigner blocks exist on BridgeMPCConfig for white-label
tenants whose policy requires institutional / cloud-HSM cosign.
User correction record:
> 'Use a provider sub-discriminator, not separate top-level kinds' ✓
> 'Do not fetch SA JSON from KMS at signing time' ✓
> 'Keep browser SDK config declarative only' ✓
> 'Separate SDK config from backend signer runtime config' ✓
> 'Normalize algorithms explicitly' ✓
> 'Shared interface should return structured metadata' ✓
> 'Preserve provider-specific error classification' ✓
> 'rely on just b-chain and m-chain ... f-chain for FHE' ✓ (fchain
config is optional + native; external cosigners are opt-in only)
Lux Bridge
Bridge monorepo for Lux Network — a decentralized cross-chain bridge using Multi-Party Computation (MPC) for secure asset transfers.
Embed the bridge SDK
To embed bridge.lux.network in another app, install @luxfi/bridge:
pnpm add @luxfi/bridge react react-dom
import { mountBridge } from '@luxfi/bridge'
mountBridge({
config: {
apiHost: 'https://api.bridge.lux.network',
env: 'mainnet',
brand: { name: 'Lux Bridge', primaryColor: '#0066ff' },
},
})
@luxfi/bridge is the only package consumers import. All @luxbridge/*
packages are internal workspace deps and are never published.
Package scopes
| Scope | Visibility | Role |
|---|---|---|
@luxfi/bridge |
public (npm) | Canonical SDK — embed bridge UI into a host app. |
@luxfi/core |
public (npm) | Shared types (Asset, Network, Contract, ...). |
@luxfi/threshold |
public (npm) | T-Chain MPC threshold-signature SDK. |
@luxfi/utila |
public (npm) | Utila protobuf client. |
@luxbridge/lux-tenant |
private (workspace) | Slim Vite SPA for bridge.lux.network — consumes @luxfi/bridge. |
@luxbridge/settings |
private (workspace) | Network + asset registry. |
@luxbridge/explorer |
private (workspace) | On-chain explorer UI. |
@luxbridge/server |
private (workspace) | Bridge API daemon. |
@luxbridge/ui-automation |
private (workspace) | Selenium UI test harness. |
One canonical SDK name. One mount function. One config shape.
Prerequisites
- Node.js v20+
- pnpm v9.15.0+
- Go 1.22+ (for MPC tools)
- Docker (for infrastructure services)
Quick Start
1. Install Dependencies
# Install pnpm if needed
npm install -g pnpm
# Install project dependencies
pnpm install
# Install MPC tools via Go
make install-mpc
2. Start Infrastructure
# Start infrastructure services (Lux ID, KMS, databases, etc.)
make up
This starts:
- Lux ID (Casdoor) - Port 8000 - Unified authentication
- KMS (Vault) - Port 8200 - Key management
- NATS - Port 4223 - Messaging
- Consul - Port 8501 - Service discovery
- PostgreSQL - Port 5433 - Bridge database
- Lux ID DB - Port 5434 - Auth database
3. Start MPC Network
# Start 3-node MPC network
make start-mpc-nodes
This runs MPC nodes on:
- Node 0: HTTP 6000, gRPC 9090
- Node 1: HTTP 6001, gRPC 9091
- Node 2: HTTP 6002, gRPC 9092
4. Start Application Services
In separate terminals:
# Terminal 1: Bridge API Server
cd app/server && pnpm dev
# Terminal 2: Bridge UI
cd app/bridge && pnpm dev
5. Access Services
- Bridge UI: http://localhost:3001
- Bridge API: http://localhost:5000
- Lux ID Admin: http://localhost:8000
- Consul UI: http://localhost:8501
Architecture
Tenant app (Lux, Hanzo, Zoo, …)
imports @luxfi/bridge + @<org>/brand
│
▼
@luxfi/bridge (pkg/bridge)
Bridge UI inlined under src/app/
Direct deps: @hanzo/gui, @luxfi/threshold, wagmi, viem
│
┌───────┴────────┐
▼ ▼
app/server @luxfi/threshold
Express+Prisma MPC SDK (also consumed by app/server)
│
▼
m-chain (public MPC, 2-of-3 threshold)
+ optional private cluster (treasury fees)
+ optional layered cosigners (Utila / Fireblocks)
See docs/LLM.md for the full topology, MPC protocol list
(cggmp21, frost, bls, doerner, pulsar, corona, magnetar),
layered-cosigner pattern, and explorer-migration plan.
Key Features
- MPC Security: 2-of-3 threshold signatures (also PQ-safe lattice variants)
- Layered cosigners (since SDK v1.0.3): optional Utila / Fireblocks cosign on top of native MPC for institutional flows
- Multi-Chain: Support for EVM and non-EVM chains
- Unified Auth: Lux ID (Casdoor) integration
- Key Management: Secure key storage in KMS
- Service Discovery: Consul for dynamic configuration
Development
# Run tests
pnpm test
# Check MPC status
lux-mpc-cli status --url http://localhost:6000
# View logs
tail -f logs/mpc-node-0.log
# Stop services
make stop-mpc-nodes # Stop MPC
make down # Stop infrastructure
Documentation
docs/LLM.md— canonical AI/onboarding doc: architecture, package layout, MPC topology, runtime config, publishing status, layered cosigners, explorer migration plandocs/LUX-ID-INTEGRATION.md— Lux ID (Casdoor) auth integrationpkg/bridge/README.md— consumer-facing SDK README (mountBridge,BridgeConfig, all config blocks)
For the new bridge dev: issue #392 is the entry point — read order, repo map, open issues by priority.
Tips
Since "pnpm" is a finger twister, many people alias it to "pn". For example, with bash, put alias pn='pnpm' in .bashrc.