Files
bridge/app/server/package.json
T
zeekay 95944ca5a5 feat(app/server): real AWS KMS + Azure KV + Vault Transit + f-chain adapters (no stubs)
'no stubs all real killer no filler' — every TODO in cosigners.ts replaced
with a real, network-touching adapter. Cloud HSM layer is now production-
grade across all four providers; f-chain attests via the Lux FHE daemon.

Dependencies added to app/server/package.json:
  @aws-sdk/client-kms       AWS SDK v3, IAM role / IRSA workload identity
  @azure/keyvault-keys      CryptographyClient with sign('ES256K'|'PS256', …)
  @azure/identity           DefaultAzureCredential (MSI / WI / Azure CLI)
  (no client lib for Vault — direct HTTP via fetch + X-Vault-Token)
  (no client lib for f-chain — direct HTTP to /v1/fhe/encrypt)

────────────────────────────────────────────────────────────────────────
 AWS KMS — real
────────────────────────────────────────────────────────────────────────
- KMSClient({ region }) — region from identityHint or parsed from key ARN
- SignCommand(KeyId, Message: digest, MessageType: DIGEST, SigningAlgorithm)
- AWS_ALGORITHM_MAP: secp256k1_ecdsa_sha256 → ECDSA_SHA_256,
                    rsa_pss_sha256          → RSASSA_PSS_SHA_256
- ed25519 NOT in map → algorithm_mismatch rejected before any RPC
- mapAwsError covers AWS exception names + HTTP status:
    AccessDeniedException / 403           → permission_denied
    NotFoundException / 404               → key_not_found
    KMSInvalidStateException / Disabled   → key_disabled
    InvalidKeyUsageException              → algorithm_mismatch
    ThrottlingException / 429             → rate_limited
    DependencyTimeout / 5xx               → provider_unavailable
    other                                  → network_error

────────────────────────────────────────────────────────────────────────
 Azure Key Vault — real
────────────────────────────────────────────────────────────────────────
- CryptographyClient(keyRef, DefaultAzureCredential())
- AZURE_ALGORITHM_MAP: secp256k1_ecdsa_sha256 → 'ES256K',
                      rsa_pss_sha256          → 'PS256'
- Returns key version from response.keyID (audit trail per CosignerStep row)
- mapAzureError covers Azure code + statusCode:
    Forbidden / AuthenticationFailed / 403  → permission_denied
    KeyNotFound / 404                       → key_not_found
    KeyDisabled                             → key_disabled
    UnsupportedKeyOperation                 → algorithm_mismatch
    TooManyRequests / 429                   → rate_limited
    ServiceUnavailable / 5xx                → provider_unavailable

────────────────────────────────────────────────────────────────────────
 Vault Transit — real
────────────────────────────────────────────────────────────────────────
- POST {VAULT_ADDR}/v1/{mount}/sign/{name} with X-Vault-Token
- Auto-splits keyRef on '/keys/' so tenants pin a non-default mount
- Body shape per algorithm:
    secp256k1_ecdsa_sha256: hash_algorithm=sha2-256, prehashed=true,
                            marshaling_algorithm=asn1 (DER, parity with AWS/GCP)
    rsa_pss_sha256:         hash_algorithm=sha2-256, signature_algorithm=pss
    ed25519:                no prehash (Vault rejects prehashed ed25519)
- Parses 'vault:v{N}:<base64>' response shape — extracts both signature
  bytes and key_version (returned as externalId='v3' etc.)
- mapVaultError maps status + body substrings:
    401/403 / 'permission denied'           → permission_denied
    404 / 'no such key'                     → key_not_found
    'key version disabled'                  → key_disabled
    'unsupported' / 'invalid input'         → algorithm_mismatch
    429 / 'rate limit'                      → rate_limited
    5xx / 'unavailable' / 'sealed'          → provider_unavailable

────────────────────────────────────────────────────────────────────────
 f-chain (Lux FHE attestation) — real
────────────────────────────────────────────────────────────────────────
- POST {publicUrl}/v1/fhe/encrypt with { plaintext_hex, scheme? }
- Endpoint contract from luxfi/fhe::cmd/fhed/main.go (the FHE daemon
  already running in the Lux ecosystem; no new server to deploy)
- 30s timeout (FCHAIN_COSIGNER_TIMEOUT_MS env override) with AbortController
- Stores the FHE ciphertext as CosignerStep.signature (0x-prefixed hex)
  — the cosign property: only f-chain (with its FHE key shares) could
  produce that ciphertext for that txHash; downstream verifier obtains
  f-chain's public key via /v1/fhe/publickey and checks equivalence
- 401/403 → rejected (policy denial)
- 5xx → failed (retryable infra)
- network/abort → failed

────────────────────────────────────────────────────────────────────────
 Other touches
────────────────────────────────────────────────────────────────────────
- fetchCosignerSecret narrows kind 'fireblocks' explicitly so the union
  with cloud_hsm + fchain typechecks
- cosigners-gcp-kms.test.ts: removes the four 'not yet implemented'
  marker assertions (those now live as positive assertions in the new
  cosigners-aws-azure-vault-fchain.test.ts)

Test counts:
  SDK  pkg/bridge:                   58/58 unchanged
  Server app/server:                 77 → 95 (+18)
    16 wire-shape validator
    10 Fireblocks
     7 persistence + dispatch-for-swap
    13 Utila
    13 GCP KMS + cloudHsmDigest + algorithm allow-list
    18 NEW: 6 AWS + 3 Azure + 3 Vault Transit + 5 f-chain + 1 ed25519 reject
  Typecheck:                          clean on both packages

No STUBs remain. Every adapter is a real network call with a real error
mapping. Workload-identity-only by design (no SA-JSON-key fallback).
2026-05-23 16:34:29 -07:00

220 lines
8.4 KiB
JSON

{
"name": "@luxbridge/server",
"version": "1.0.0",
"private": true,
"description": "",
"main": "src/server.ts",
"_moduleAliases": {
"@": "dist"
},
"scripts": {
"dev": "nodemon",
"db": "node dist/db.js",
"rm": "node dist/delete.js",
"nodes": "node dist/nodes.js",
"tc": "tsc",
"typecheck": "tsc",
"test": "vitest run",
"clean": "rm -rf ./node_modules",
"prisma:gen": "pnpm exec prisma generate",
"build": "pnpm exec prisma generate && tsc && tsc-alias",
"start": "node dist/server.js",
"lint": "eslint src/**/*.ts",
"format": "eslint src/**/*.ts --fix",
"update-db": "kubectl exec -it $(kubectl get pod -l app=bridge-server -o jsonpath='{.items[0].metadata.name}') -- pnpm run db",
"colima:start": "colima start --arch aarch64,x86_64",
"docker:build": "cd ../.. && docker buildx build --load --platform linux/amd64 -t ghcr.io/luxfi/bridge-server:latest -f app/server/docker/Dockerfile .",
"docker:login": "docker login --username $GH_USER --password $GH_TOKEN ghcr.io",
"docker:push": "docker push ghcr.io/luxfi/bridge/server:latest",
"k8s:apply": "kubectl apply -f k8s/bridge-deployment.yaml && kubectl apply -f k8s/bridge-service.yaml",
"k8s:logs": "kubectl logs -l app=bridge-server -f",
"k8s:scale": "kubectl scale deployment bridge-server --replicas=$REPLICAS",
"k8s:create-secret": "kubectl create secret generic bridge-server-secret --from-literal=POSTGRESQL_URL=\"$POSTGRESQL_URL\" --dry-run=client -o yaml | kubectl apply -f -",
"k8s:restart": "kubectl rollout restart deployment bridge-server",
"k8s:status": "kubectl get deployments,service,pods -l app=bridge-server -o wide",
"k8s:stop": "kubectl delete deployment bridge-server || true && kubectl delete service bridge-service || true"
},
"prisma": {
"schema": "prisma/schema.prisma"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.7",
"@types/morgan": "^1.9.9",
"@types/multicoin-address-validator": "^0.5.3",
"@types/node": "^20.17.1",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.2.0",
"@typescript-eslint/parser": "^8.2.0",
"nodemon": "^3.1.4",
"prettier": "^3.3.3",
"ts-node": "^10.9.2",
"tsc-alias": "^1.8.10",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.5.4",
"vitest": "^2.1.0"
},
"dependencies": {
"@aws-sdk/client-kms": "^3.1053.0",
"@azure/identity": "^4.13.1",
"@azure/keyvault-keys": "^4.10.0",
"@google-cloud/kms": "^5.5.0",
"@luxfi/core": "workspace:*",
"@luxfi/utila": "^3.0.0",
"@noble/ed25519": "^2.3.0",
"@prisma/client": "^6.3.1",
"@solana/addresses": "2.0.0",
"axios": "^1.7.4",
"consul": "^2.0.1",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"ethers": "^6.13.4",
"express": "^4.21.1",
"express-rate-limit": "^8.3.1",
"express-validator": "^7.2.0",
"fireblocks-sdk": "^5.39.0",
"helmet": "^8.0.0",
"jsonwebtoken": "^9.0.2",
"module-alias": "^2.2.3",
"morgan": "^1.10.0",
"multicoin-address-validator": "^0.5.22",
"nats": "^2.29.3",
"prisma": "^6.3.1",
"uuid": "^9.0.0",
"web3": "^4.16.0",
"winston": "^3.17.0"
},
"pnpm": {
"overrides": {
"color-string@<1.5.5": ">=1.5.5",
"tar@<6.2.1": ">=6.2.1",
"axios@>=0.8.1 <0.28.0": ">=0.28.0",
"tough-cookie@<4.1.3": ">=4.1.3",
"ws@>=8.0.0 <8.17.1": ">=8.17.1",
"ws@>=7.0.0 <7.5.10": ">=7.5.10",
"ws@>=2.1.0 <5.2.4": ">=5.2.4",
"zod@<=3.22.2": ">=3.22.3",
"next@>=14.0.0 <14.2.21": ">=14.2.21",
"undici@>=6.0.0 <6.21.1": ">=6.21.1",
"undici@>=4.5.0 <5.28.5": ">=5.28.5",
"esbuild@<=0.24.2": ">=0.25.0",
"vite@>=5.0.0 <=5.4.11": ">=5.4.12",
"elliptic@<=6.6.0": ">=6.6.1",
"vite@>=5.0.0 <5.4.15": ">=5.4.15",
"@remix-run/express@>=2.11.1 <2.16.3": ">=2.16.3",
"estree-util-value-to-estree@<3.3.3": ">=3.3.3",
"vite@>=5.0.0 <5.4.18": ">=5.4.18",
"cookie@<0.7.0": ">=0.7.0",
"@babel/runtime@<7.26.10": ">=7.26.10",
"@babel/helpers@<7.26.10": ">=7.26.10",
"vite@>=5.0.0 <=5.4.18": ">=5.4.19",
"base-x@<=3.0.10": ">=3.0.11",
"base-x@=4.0.0": ">=4.0.1",
"vite@>=5.0.0 <5.4.17": ">=5.4.17",
"image-size@>=1.1.0 <1.2.1": ">=1.2.1",
"brace-expansion@>=1.0.0 <=1.1.11": ">=1.1.12",
"brace-expansion@>=2.0.0 <=2.0.1": ">=2.0.2",
"next@>=13.0 <14.2.30": ">=14.2.30",
"pbkdf2@>=3.0.10 <=3.1.2": ">=3.1.3",
"@eslint/plugin-kit@<0.3.4": ">=0.3.4",
"on-headers@<1.1.0": ">=1.1.0",
"next@>=0.9.9 <14.2.31": ">=14.2.31",
"vite@<=5.4.19": ">=5.4.20",
"next@>=0.9.9 <14.2.32": ">=14.2.32",
"@metamask/sdk-communication-layer@>=0.16.0 <=0.33.0": ">=0.33.1",
"@metamask/sdk@>=0.16.0 <=0.33.0": ">=0.33.1",
"next@>=0.9.9 <14.2.24": ">=14.2.24",
"vite@>=5.2.6 <=5.4.20": ">=5.4.21",
"validator@<13.15.20": ">=13.15.20",
"tar-fs@>=2.0.0 <2.1.4": ">=2.1.4",
"tar-fs@>=3.0.0 <3.1.1": ">=3.1.1",
"sha.js@<=2.4.11": ">=2.4.12",
"tmp@<=0.2.3": ">=0.2.4",
"form-data@>=4.0.0 <4.0.4": ">=4.0.4",
"form-data@<2.5.4": ">=2.5.4",
"tar-fs@>=3.0.0 <3.0.9": ">=3.0.9",
"tar-fs@>=2.0.0 <2.1.3": ">=2.1.3",
"tar-fs@>=3.0.0 <3.0.7": ">=3.0.7",
"tar-fs@>=2.0.0 <2.1.2": ">=2.1.2",
"elliptic@>=4.0.0 <=6.5.6": ">=6.5.7",
"elliptic@>=2.0.0 <=6.5.6": ">=6.5.7",
"elliptic@>=5.2.1 <=6.5.6": ">=6.5.7",
"min-document@<=2.19.0": ">=2.19.1",
"glob@>=10.2.0 <10.5.0": ">=10.5.0",
"valibot@>=0.31.0 <1.2.0": ">=1.2.0",
"node-forge@<1.3.2": ">=1.3.2",
"axios@<0.30.0": ">=0.30.0",
"axios@>=1.0.0 <1.8.2": ">=1.8.2",
"elliptic@<6.6.0": ">=6.6.0",
"elliptic@<6.5.6": ">=6.5.6",
"jws@<3.2.3": ">=3.2.3",
"next@>=13.3.0 <14.2.34": ">=14.2.34",
"@remix-run/server-runtime@<=2.17.2": ">=2.17.3",
"@remix-run/router@<=1.23.1": ">=1.23.2",
"@remix-run/react@<2.17.3": ">=2.17.3",
"react-router@>=6.0.0 <6.30.2": ">=6.30.2",
"@remix-run/node@<=2.17.1": ">=2.17.2",
"@remix-run/react@>=1.15.0 <=2.17.0": ">=2.17.1",
"next@>=13.3.1-canary.0 <14.2.35": ">=14.2.35",
"h3@<=1.15.4": ">=1.15.5",
"axios@>=1.0.0 <1.12.0": ">=1.12.0",
"@coinbase/wallet-sdk@>=4.0.0-beta.0 <4.3.0": ">=4.3.0",
"lodash-es@>=4.0.0 <=4.17.22": ">=4.17.23",
"lodash@>=4.0.0 <=4.17.22": ">=4.17.23",
"undici@<6.23.0": ">=6.23.0",
"vite@>=5.0.0 <5.4.16": ">=5.4.16",
"next@>=10.0.0 <15.5.10": ">=15.5.10",
"next@>=13.0.0 <15.0.8": ">=15.0.8",
"tar@<7.5.7": ">=7.5.7",
"diff@>=4.0.0 <4.0.4": ">=4.0.4",
"diff@>=5.0.0 <5.2.2": ">=5.2.2",
"validator@<13.15.22": ">=13.15.22",
"js-yaml@<3.14.2": ">=3.14.2",
"js-yaml@>=4.0.0 <4.1.1": ">=4.1.1",
"semver@>=2.0.0-alpha <5.7.2": ">=5.7.2",
"webpack@>=5.49.0 <=5.104.0": ">=5.104.1",
"webpack@>=5.49.0 <5.104.0": ">=5.104.0",
"mdast-util-to-hast@>=13.0.0 <13.2.1": ">=13.2.1",
"undici@>=6.0.0 <6.21.2": ">=6.21.2",
"undici@<5.29.0": ">=5.29.0",
"qs@>=6.7.0 <=6.14.1": ">=6.14.2",
"pbkdf2@>=1.0.0 <=3.1.2": ">=3.1.3",
"axios@<=0.30.2": ">=0.30.3",
"axios@>=1.0.0 <=1.13.4": ">=1.13.5",
"tar@<=7.5.2": ">=7.5.3",
"tar@<7.5.8": ">=7.5.8",
"bn.js@>=5.0.0 <5.2.3": ">=5.2.3",
"bn.js@<4.12.3": ">=4.12.3",
"minimatch@<3.1.3": ">=3.1.3",
"minimatch@>=5.0.0 <5.1.7": ">=5.1.7",
"minimatch@>=9.0.0 <9.0.6": ">=9.0.6",
"rollup@>=4.0.0 <4.59.0": ">=4.59.0",
"rollup@<2.80.0": ">=2.80.0",
"minimatch@>=5.0.0 <5.1.8": ">=5.1.8",
"minimatch@>=9.0.0 <9.0.7": ">=9.0.7",
"minimatch@<3.1.4": ">=3.1.4",
"serialize-javascript@<=7.0.2": ">=7.0.3",
"next@>=14.0.0 <14.2.25": ">=14.2.25",
"ajv@<6.14.0": ">=6.14.0",
"ajv@>=7.0.0-alpha.0 <8.18.0": ">=8.18.0",
"qs@<6.14.1": ">=6.14.1",
"svgo@>=3.0.0 <3.3.3": ">=3.3.3",
"svgo@>=2.1.0 <2.8.1": ">=2.8.1",
"tar@<=7.5.9": ">=7.5.10",
"tar@<=7.5.10": ">=7.5.11",
"flatted@<3.4.0": ">=3.4.0",
"undici@>=6.0.0 <6.24.0": ">=6.24.0",
"undici@<6.24.0": ">=6.24.0",
"tar@<=7.5.3": ">=7.5.4",
"next@>=9.5.0 <15.5.13": ">=15.5.13",
"next@>=10.0.0 <16.1.7": ">=16.1.7",
"h3@<1.15.6": ">=1.15.6",
"socket.io-parser@>=4.0.0 <4.2.6": ">=4.2.6"
}
}
}