zeekay d86ead4f93 feat: Cloud HSM (GCP real, AWS/Azure/Vault stubs) + f-chain native cosigner (#386)
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)
2026-05-22 20:27:02 -07:00
2024-01-08 22:05:19 +01:00
2025-12-24 14:19:52 -08:00
2025-12-24 14:19:52 -08:00
2026-03-27 19:44:20 -07:00
2025-12-31 22:21:30 -08:00
2025-08-03 15:52:32 -05:00

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

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 plan
  • docs/LUX-ID-INTEGRATION.md — Lux ID (Casdoor) auth integration
  • pkg/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.

S
Description
▼ Bridge, powered by trustless MPC network and privacy focused Teleport protocol.
Readme BSD-3-Clause
260 MiB
Languages
TypeScript 52.2%
Go 40.9%
Solidity 2.8%
JavaScript 1.8%
Shell 1.4%
Other 0.8%