Set Up Advanced Wallets Infrastructure

Overview

Advanced wallets are a type of self-custody cold wallet that enable you to have the greatest control over your user and backup keys. These wallets leverage two instances of BitGo Express, enabling you to manage your user and backup keys within your region or jurisdiction (also known as on-prem). Like other wallet types, BitGo manages the BitGo key using an air-gapped hardware security module (HSM).

You can deploy the application as a standard Node.js process. BitGo recommends using the included Docker Compose file, since it's configured to run both services with proper network isolation for enhanced security.

Architecture

Advanced wallets use the following two BitGo Express servers:

  • Advanced Wallet Manager (AWM) - A lightweight, dedicated server isolated within a secure internal subnet with no internet access that you use for key generation, signing, and wallet recovery.
  • Master BitGo Express (MBE) - A normal Express server residing outside your secure subnet that's a full-featured API gateway. The MBE orchestrates operations between BitGo APIs on the public network and the AWM on your internal network.

The AWM uses Secure Socket Layer (SSL) to restrict access, only accepting incoming communication from MBE and outgoing communication to your key management service (KMS). These communication limitations protect your private keys from exposure and unauthorized access.

The AWM integrates with your external KMS API. This API is an abstraction layer for HSM and KMS providers, handling both encryption and decryption of your private keys, while securely storing the encryptedPrv values in its own dedicated database. BitGo provides the API specification and this integration guide, so you can flexibly manage encryption and implementation with your own interfaces and integrating with your preferred HSM and KMS.

The AWM requires a compatible KMS API for all secure key operations. You must provide your own implementation (such as AWS CloudHSM and Dinamo HSM), using the BitGo provided KMS API interface specification.

Note: BitGo doesn't provide the KMS integration. You must implement this separately. You can select any KMS you prefer, so long as it's compatible with the BitGo provided KMS API interface specification.

You can reference the following example implementations:

Key Features

  • Complete Infrastructure Control - Host and manage all components in your own secure environment.
  • KMS and HSM Integration - Bring your own KMS and HSM.
  • Network Isolation - The AWM operates in a completely isolated network segment with no external internet access.
  • mTLS Security - Mutual TLS with client certificate validation for secure inter-service communications.
  • Flexible Configuration - Environment-based setup with file or variable-based certificates.

Prerequisites

Ensure your system has the following:

Install Project

1. Clone Repository

# Clones the repo using SSH
git clone [email protected]:BitGo/advanced-wallets.git

# Navigates to your `advanced-wallets` directory
cd advanced-wallets

2. Install Dependencies

npm install

3. Build Project

npm run build

Test Locally

Before configuring your production setup, BitGo recommends running the application locally for development and testing. When you run the full application locally, you can start both the AWM and the MBE from the same machine. For local development, you can test with or without mTLS authentication or with self-signed demo certificates. If you're testing locally with mTLS, then you must create certificates.

The following code samples use Node.js without mTLS.

1. Start AWM

TLS_MODE=disabled \
BITGO_ENV=test \
APP_MODE=advanced-wallet-manager \
ADVANCED_WALLET_MANAGER_PORT=3080 \
KMS_URL=http://localhost:3000 \
npm start

Step Result

> @bitgo/[email protected] start
> nodemon

[nodemon] 3.1.10
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): src/**/*.ts
[nodemon] watching extensions: ts
[nodemon] starting `npm run build && node ./bin/advanced-wallet-manager`

> @bitgo/[email protected] build
> npm run tsc -- --build --incremental --verbose . && cp package.json dist/


> @bitgo/[email protected] tsc
> tsc --build --incremental --verbose .

[4:54:36 PM] Projects in this build:
    * tsconfig.json

[4:54:36 PM] Project 'tsconfig.json' is up to date because newest input 'src/masterBitgoExpress/routers/sendManyRoute.ts' is older than output 'dist/tsconfig.tsbuildinfo'

2025-12-05 16:54:38:5438 info: Starting in Advanced Wallet Manager mode...
2025-12-05 16:54:38:5438 info: === Certificate Loading ===
2025-12-05 16:54:38:5438 info: ==========================
2025-12-05 16:54:38:5438 info: App is initializing
2025-12-05 16:54:38:5438 info: Log location: /Users/vivianmccarty/advanced-wallets/logs/http-access.log
2025-12-05 16:54:38:5438 info: Advanced Wallet Manager starting...
2025-12-05 16:54:38:5438 info: Base URI: http://localhost:3080
2025-12-05 16:54:38:5438 info: Port: 3080
2025-12-05 16:54:38:5438 info: Bind: localhost
2025-12-05 16:54:38:5438 info: KMS URL: http://localhost:3000
2025-12-05 16:54:38:5438 info: Recovery Mode: false
2025-12-05 16:54:38:5438 info: === mTLS Configuration ===
2025-12-05 16:54:38:5438 info: TLS Mode: disabled
2025-12-05 16:54:38:5438 info: ========================
2025-12-05 16:54:38:5438 info: Advanced Wallet Manager started successfully

2. Start MBE

Open a new terminal, navigate to the advanced-wallets directory, and run the following:

TLS_MODE=disabled \
BITGO_ENV=test \
APP_MODE=master-express \
MASTER_EXPRESS_PORT=3081 \
ADVANCED_WALLET_MANAGER_URL=http://localhost:3080 \
npm start

Step Result

> @bitgo/[email protected] start
> nodemon

[nodemon] 3.1.10
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): src/**/*.ts
[nodemon] watching extensions: ts
[nodemon] starting `npm run build && node ./bin/advanced-wallet-manager`

> @bitgo/[email protected] build
> npm run tsc -- --build --incremental --verbose . && cp package.json dist/


> @bitgo/[email protected] tsc
> tsc --build --incremental --verbose .

[4:55:27 PM] Projects in this build:
    * tsconfig.json

[4:55:27 PM] Project 'tsconfig.json' is up to date because newest input 'src/masterBitgoExpress/routers/sendManyRoute.ts' is older than output 'dist/tsconfig.tsbuildinfo'

2025-12-05 16:55:28:5528 info: Starting in Master Express mode...
2025-12-05 16:55:28:5528 info: === Certificate Loading ===
2025-12-05 16:55:28:5528 info: ==========================
2025-12-05 16:55:28:5528 info: Master express app is initializing
2025-12-05 16:55:28:5528 info: Log location: /Users/vivianmccarty/advanced-wallets/logs/http-access.log
2025-12-05 16:55:28:5528 info: ✓ AWM Client initialized with URL: http://localhost:3080
2025-12-05 16:55:28:5528 info: Master Express server starting...
2025-12-05 16:55:28:5528 info: Base URI: http://localhost:3081
2025-12-05 16:55:28:5528 info: Port: 3081
2025-12-05 16:55:28:5528 info: Bind: localhost
2025-12-05 16:55:28:5528 info: Recovery Mode: false
2025-12-05 16:55:28:5528 info: Advanced Wallet Manager URL: http://localhost:3080
2025-12-05 16:55:28:5528 info: === mTLS Configuration ===
2025-12-05 16:55:28:5528 info: TLS Mode: disabled
2025-12-05 16:55:28:5528 info: ========================
2025-12-05 16:55:28:5528 info: Master Express server started successfully

3. Test Connections

You can test your local setup using the following health check endpoints. The first endpoint tests MBE directly on port 3081. The second endpoint tests the connection between MBE and AWM to ensure both services can communicate properly.

Endpoints:

# Ping MBE
curl -X POST http://localhost:3081/advancedwallet/ping

# Test connection between services
curl -X POST http://localhost:3081/ping/advancedWalletManager

Step Result

# Test MBE
{"status":"master express server is ok!","timestamp":"2025-12-05T22:59:54.738Z"}

# Test connection between services
{"status":"Successfully pinged advanced wallet manager","awmResponse":{"status":"advanced wallet manager server is ok!","timestamp":"2025-12-05T23:00:57.970Z"}}

Next Steps

To continue local testing on your machine, see the following guides:

Once you're satisfied with your local development, follow the steps bellow to set up advanced wallets in production with mTLS security.

Configure Production Security

Production deployments use mTLS security. Each service (AWM, MBE, and KMS) must use separate certificates. Client certificates for outbound connections must be different from server certificates. Ensure proper security protocols by regularly rotating certificates according to the security policy of your organization.

1. Generate mTLS Key

openssl genrsa -out private.key 2048

Step Result

You generated a 2048-bit RSA private key and saved it to a file named private.key. Store this in a secure location with restricted file permissions.

Generating RSA private key, 2048 bit long modulus
.......+++++
.......................+++++

3. Generate CSR

Generate a certificate signing request (CSR) by running the following command and answering the questions that display in your terminal:

openssl req -new -key private.key -out request.csr

Step Result

You generated a CSR and saved it to a file named request.csr.

4. Submit CSR to CA

Submit your CSR file, request.csr, to a trusted certificate authority (CA) for signing. Follow the instructions outlined by your preferred CA and Public Key Infrastructure (PKI) of your organization.

Step Result

Once the CA processes your CSR and completes any necessary verification, you're issued a signed certificate for your domain. Save the signed certificate to a file, with a .crt extension.

5. Get Certificate Fingerprints

Obtain the client certificate fingerprints for the MTLS_ALLOWED_CLIENT_FINGERPRINTS environment variable. This adds an extra layer of security by restricting which specific client certificates can connect to your servers, even if they're otherwise valid and trusted certificates.

openssl x509 -in /path/to/client-cert.crt -noout -fingerprint -sha256 | cut -d'=' -f2

Step Result

You receive a SHA-256 fingerprint. Add your fingerprints to the MTLS_ALLOWED_CLIENT_FINGERPRINTS environment variable in the docker-compose.yml file of your project. You can specify multiple fingerprints as a comma-separated list.

sha256:D1:E9:5A:F2:8C:3B:67:0A:47:15:F9:6D:E2:3F:8B:12:9E:5C:7A:D4:8F:1E:6B:0D:C3:A9:2F:5B:E8:4D:7C:10

Run in Production

Ensure Docker, Docker Compose, and your KMS API implementation are all running on your host machine.

Deploying the containers using Docker Compose creates two distinct networks:

  • my-internal-network
    • Internal bridge network with internal: true.
    • Used for secure AWM isolation and MBE-to-AWM communication.
    • No external internet access for security.
  • my-public-network
    • Public bridge network.
    • Used for external access to MBE APIs.
    • Connected to host networking.

1. Start Services

# Navigate to project directory
cd advanced-wallet

# Start both services in background
docker-compose up -d

Step Result

Creating network "advanced-wallets_my-internal-network" with driver "bridge"
Creating network "advanced-wallets_my-public-network" with driver "bridge"
Creating advanced-wallet-manager ... done
Creating master-bitgo-express    ... done

2. Test Connections

Testing your secure endpoints in production deployments requires using valid client certificates that are trusted by your server.

Before testing, ensure your DNS records or load balancer are configured to route traffic to your servers. For production deployments, you typically configure:

  • DNS A records pointing your domain names to the server IP addresses (for example, awm.internal.example.com → AWM server, mbe.example.com → MBE server).
  • Load balancer rules to distribute traffic across multiple instances of each service.
  • Firewall rules to ensure AWM is only accessible from your internal network and MBE.

You can test your production endpoints using the following health checks. The first endpoint tests MBE directly on port 3081. The second endpoint tests the connection between MBE and AWM to ensure both services can communicate securely through mTLS.

Endpoints:

# Test MBE
curl --cert /path/to/client-cert.crt --key /path/to/client-key.key \
  --cacert /secure/certs/mbe-ca.crt \
  https://YOUR_IP_OR_HOSTNAME:3081/advancedwallet/ping

# Test connection between services
curl --cert /path/to/client-cert.crt --key /path/to/client-key.key \
  --cacert /secure/certs/mbe-ca.crt \
  https://YOUR_IP_OR_HOSTNAME:3081/ping/advancedWalletManager

Step Result

# Test MBE
{"status":"master express server is ok!","timestamp":"2025-12-24T18:30:15.482Z"}

# Test connection between services
{"status":"Successfully pinged advanced wallet manager","awmResponse":{"status":"advanced wallet manager server is ok!","timestamp":"2025-12-24T18:30:16.123Z"}}

3. Stop Services

To stop and remove the containers and networks, run:

# Stop and remove containers
docker-compose down

Step Result

Stopping master-bitgo-express    ... done
Stopping advanced-wallet-manager ... done
Removing master-bitgo-express    ... done
Removing advanced-wallet-manager ... done
Removing network advanced-wallets_my-internal-network
Removing network advanced-wallets_my-public-network

Next Steps

To continue building your integration, see Create Advanced Wallets.

You can also download the entire BitGo OpenAPI specification from the API Reference Overview. You can also download just the MBE specification from GitHub or by running the following:

npm run generate:openapi:masterExpress

See Also