# BitGo Express local signing server (REST API) BitGo Express is suitable for developers working in a language without an official BitGo SDK. BitGo Express runs as a service in your own datacenter, and handles the client-side operations involving your own keys, such as partially signing transactions before submitting to BitGo. This ensures your keys never leave your network, and are not seen by BitGo. BitGo Express can also proxy the standard BitGo REST APIs, providing a unified interface to BitGo through a single REST API. # Documentation View comprehensive documentation on the BitGo [Developer Portal](https://developers.bitgo.com/docs/get-started-express-install/). # Running BitGo Express ## Docker For most users, we recommend running BitGo Express as a Docker container, since this's the most secure way to run BitGo Express. > Deprecation Warning: bitgosdk/express will no longer receive updates. Please use the official bitgo/express images. To try it out, run this command: ```bash $ docker run -it -p 3080:3080 bitgo/express:latest ``` You should see this output from the container: ``` BitGo-Express running Environment: test Base URI: http://0.0.0.0:3080 ``` You can then send a ping request to BitGo Express using curl: ```bash $ curl localhost:3080/api/v2/ping {"status":"service is ok!","environment":"BitGo Testnet","configEnv":"testnet","configVersion":79} ``` You can also give command line arguments to BitGo Express at the end of the docker run command: ```bash $ docker run -it -p 4000:4000 bitgo/express:latest --port 4000 ``` BitGo Express will start up on the specified port, 4000: ``` BitGo-Express running Environment: test Base URI: http://0.0.0.0:4000 ``` ### Docker in production When running BitGo Express in production, we strongly recommended using HTTPS to secure your connections. If you have your own certificate, you can use it. Or, to generate a self-signed certificate, first make a directory and navigate into it: ```bash $ mkdir certs $ cd certs ``` Then do the following and fill out the information that it prompts you for: ```bash $ openssl req -newkey rsa:2048 -nodes -keyout cert.key -x509 -days 3650 -out cert.crt ``` This will output a key file `cert.key` and certificate file `cert.crt` Finally you can run your Docker container like this (note, replace `/path/to` with the full path to your `certs` folder): ```bash $ docker run -it --volume /path/to/certs:/private -p 4000:4000 bitgo/express:latest -p 4000 -k /private/cert.key -c /private/cert.crt -e prod ``` An alternative way to provide the SSL certification is to export them as environment variables. ```bash $ export BITGO_SSL_KEY=$(cat /private/cert.key) $ export BITGO_SSL_CERT=$(cat /private/cert.cert) ``` ```bash $ docker run -it --volume /path/to/certs:/private -p 4000:4000 bitgo/express:latest -p 4000 -e BITGO_SSL_KEY -e BITGO_SSL_CERT -e prod ``` BitGo Express should start on the specified port, 4000: ``` BitGo-Express running Environment: prod Base URI: https://0.0.0.0:4000 ``` ### Building the Docker container To build the BitGo Express Docker container yourself from the source code, check out the latest **master** branch and run `docker build` from the project root. ```bash $ git clone https://github.com/bitgo/bitgojs $ cd bitgojs $ docker build -t bitgo-express:latest . $ docker run -it bitgo-express:latest ``` You can also build the BitGo Express Docker container using `yarn`: ```bash $ git clone https://github.com/bitgo/bitgojs $ cd bitgojs $ yarn build-docker-express $ docker run -it bitgo-express:latest ``` The BitGo Express Docker image tracks the older of the two most recent Node.js Long-Term Support (LTS) releases. It currently builds and runs on Node.js 24. ## From source For users who are unable to run BitGo Express as a Docker container, we recommend building and running from the source code. ### Prerequisites Use Node.js 22 or later. The required continuous integration checks cover Node.js 22, 24, and 26. Node.js 22 is deprecated and support will be removed in the first major release on or after 2027-04-30. We recommend using `nvm`, the [Node Version Manager](https://github.com/creationix/nvm/blob/master/README.markdown#installation), for setting your Node.js version. BitGo Express is part of the monorepo, so use `yarn` for local setup. ### Cloning the repository and installing dependencies Clone the latest **master** branch and run `yarn install` in the project root directory. ```bash $ git clone https://github.com/bitgo/bitgojs $ cd bitgojs $ yarn install --frozen-lockfile && yarn install --production --frozen-lockfile --non-interactive --ignore-scripts ``` ### Running BitGo Express From the Express module folder (`modules/express`), run this command: ```bash $ yarn run start ``` You should see BitGo Express start up in the default test environment: ``` BitGo-Express running Environment: test Base URI: http://localhost:3080 ``` You can also pass startup arguments to BitGo Express: ``` $ yarn run start --port 4000 ``` BitGo Express will start up on the custom port: ``` BitGo-Express running Environment: test Base URI: http://localhost:4000 ``` ### Running in production When running BitGo Express against the BitGo production environment using real funds, you should make sure the `NODE_ENV` environment variable is set to `production`. This will turn off some debugging information which could leak information about the system which is running BitGo Express. If an unsafe configuration is detected, BitGo Express will emit a warning upon startup. In a future version of BitGo Express, this will turn into a hard error and BitGo Express will fail to start up. Additionally, when running against the production env and listening on external interfaces, BitGo Express must be run with TLS enabled by setting the `keyPath` and `crtPath` configuration options, otherwise BitGo Express will error upon startup with the following message: ``` Fatal error: Must enable TLS when running against prod and listening on external interfaces! Error: Must enable TLS when running against prod and listening on external interfaces! ``` We strongly recommend always enabling TLS when running against the BitGo production environment. However, if you must opt out of this requirement, you may do so by setting the `disableSSL` configuration option. **Use at your own risk, as this may allow a man-in-the-middle to access sensitive information as it is sent over the wire in cleartext.** ### BitGo Express Proxy Support BitGo Express can also make requests to BitGo via a proxy. This can be done by setting the `BITGO_USE_PROXY` environment variable to the URI where your proxy is listening. For example, to instruct BitGo Express to use a SOCKS proxy which is listening at 192.0.2.1 on port 12000, you should start BitGo Express with the following command: ```shell docker run -e BITGO_USE_PROXY="socks://192.0.2.1:12000" -p 3080:3080 bitgo/express:latest ``` BitGo Express currently supports the following proxy protocols: - HTTP - HTTPS - SOCKSv4 - SOCKSv5 - PAC ### Domain-scoped CA trust for BitGo connections By default, BitGo Express restricts outbound HTTPS connections to official BitGo domains (`*.bitgo.com`, `*.bitgo-test.com`, `*.bitgo-staging.com`, `*.bitgo-dev.com`, and their apex domains) to a bundled set of root certificate authorities (`modules/express/bitgo-ca.pem`: Google Trust Services, Amazon Trust Services, DigiCert, Let's Encrypt/ISRG, and Sectigo). Every other outbound destination (external signer, block explorers, webhooks, KMS) continues to use the Node.js / system trust store. This is **enabled by default** to shrink the blast radius of a compromise of any unrelated CA in the global trust store without requiring manual certificate-pin maintenance across routine BitGo certificate renewals or CA migrations. - To opt out, pass `--disablepincerts` or set `BITGO_DISABLE_PIN_CERTS=true`. - When `BITGO_USE_PROXY` is set, Express logs a startup warning and skips the bundled CA policy because the proxy agent owns the TLS connection. - If your network uses a **transparent TLS-inspecting firewall/proxy** (no `BITGO_USE_PROXY`) whose CA you trust via `NODE_EXTRA_CA_CERTS`: note that the policy agent replaces the trust store for BitGo domains, so `NODE_EXTRA_CA_CERTS` is **ignored** for them. Express logs a startup warning in this case; either pass `--disablepincerts` / `BITGO_DISABLE_PIN_CERTS=true`, or the connections fail with `unable to get local issuer certificate` / `SELF_SIGNED_CERT_IN_CHAIN`. This is a behavior change from prior releases (BitGo domains previously trusted `NODE_EXTRA_CA_CERTS`). - Redirects are validated on each hop for covered BitGo hostnames: redirects to other covered `https://` BitGo hosts are followed, while redirects to uncovered hosts or plain `http://` are refused. - At startup, Express warns if any root CA in `bitgo-ca.pem` expires within 30 days. - The bundle is **not** self-updating. Regenerate it from the current Node.js root store (e.g. when BitGo switches CA providers or a root nears expiry) and re-verify against live endpoints: ```bash bash scripts/verify-bitgo-policy-data.sh --regenerate # rebuilds modules/express/bitgo-ca.pem cd modules/express && yarn test:data:live # offline checks + live handshakes to all BitGo endpoints ``` If BitGo ever moves `*.bitgo.com` to a CA provider outside the bundled roots, update `bitgo-ca.pem` **before** the cutover (deployed customers run older images). > The underlying policy helper (`createBitGoTlsPolicy` in `@bitgo/sdk-api`) is Node-only: it relies on the Node `crypto` and `tls` modules and refuses to build in a browser bundle. ### External Signing Mode BitGo Express can be run in an external signing mode, where the signing of transactions is performed in a separate instance of BitGo Express that has access to the private keys. This may be preferable for users who would like to apply their signature to their transactions using a more secure environment than BitGo SDK or BitGo Express, such as a signer with restricted access or network connectivity. For more information please see our [External Signing Mode Documentation](EXTERNAL_SIGNER.md). When running express in `--signerMode`, one signer process must own each `signerFileSystemPath`: the consumed-share registry file (`.consumedShares.json`) is read once per process and rewritten by the process that owns it, so sharing one registry file across replicas can lose replay-protection records. A relay retry after a lost signer response restarts the signing session from the K round (fail-closed). The registry location defaults to `.consumedShares.json`; the signer never assumes write access there — if it is not writable, a warning is logged and replay protection degrades to in-memory only (lost on restart). Point `--signerRegistryFilePath` (or `BITGO_SIGNER_REGISTRY_FILE_PATH`) at a writable location for full restart protection. ### KeepAliveTimeout/HeadersTimeout If the deployment destination is configured with a load balancer such as ELB, a 504 Gateway Timeout may occur. In such cases, it might be resolved by configuring the settings so that LB keepAliveTimeout < keepAliveTimeout < headersTimeout. This can be achieved by specifying the keepAliveTimeout and headersTimeout options. ## Configuration Values BitGo Express is able to take configuration options from either command line arguments, or via environment variables. | Flag Short Name | Flag Long Name | Environment Variable | Default Value | Description | | --------------- | ------------------------------- | ----------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -p | --port | `BITGO_PORT` | 3080 | Port which bitgo express should listen on. | | -b | --bind | `BITGO_BIND` | localhost | Interface which bitgo express should listen on. To listen on all interfaces, this should be set to `0.0.0.0`. | | -e | --env | `BITGO_ENV` | test | BitGo environment to interact with. | | -t | --timeout | `BITGO_TIMEOUT` | 305000 | Number of milliseconds to wait before requests made by `bitgo-express` time out. | | -d | --debug | N/A, use `BITGO_DEBUG_NAMESPACE` instead | N/A | Enable debug output for bitgo-express. This is equivalent to passing `--debugnamespace bitgo:express`. | | -D | --debugnamespace | `BITGO_DEBUG_NAMESPACE` | N/A | Enable debug output for a particular debug namespace. Multiple debug namespaces can be given as a comma separated list. See the [`bitgo` package README](https://github.com/BitGo/BitGoJS/blob/master/modules/bitgo/README.md#available-debug-namespaces) for a complete list of recognized options, in addition to those listed in the table below. | | -k | --keypath | `BITGO_KEYPATH` | N/A | Path to SSL .key file (either `keypath` or `sslkey` required if running against production environment). | | N/A | --sslkey | `BITGO_SSL_KEY` | N/A | The SSL key (either `keypath` or `sslkey` required if running against production environment). | | N/A | --enclavedExpressSSLCert | `ENCLAVED_SSL_CERT` | N/A | The SSL cert (either `crtpath` or `sslcert` for communicating with an enclaved express instance. | | N/A | --enclavedlExpressUrl | `ENCLAVED_EXPRESS_URL` | N/A | The URL to an enclaved express instance. | | -c | --crtpath | `BITGO_CRTPATH` | N/A | Path to SSL .crt file (required if running against production environment). | | N/A | --sslcert | `BITGO_SSL_CERT` | N/A | The SSL cert (either `crtpath` or `sslcert` required if running against production environment). | | -u | --customrooturi | `BITGO_CUSTOM_ROOT_URI` | N/A | Force a custom BitGo URI. | | -n | --custombitcoinnetwork | `BITGO_CUSTOM_BITCOIN_NETWORK` | N/A | Force a custom BitGo network | | -l | --logfile | `BITGO_LOGFILE` | N/A | Filepath to write access logs. | | N/A | --disablessl | `BITGO_DISABLE_SSL` 0 | N/A | Disable requiring SSL when accessing bitgo production environment. **USE AT YOUR OWN RISK, NOT RECOMMENDED**. | | N/A | --disableproxy | `BITGO_DISABLE_PROXY` 0 | N/A | Disable proxying of routes not explicitly handled by bitgo-express | | N/A | --disableenvcheck | `BITGO_DISABLE_ENV_CHECK` 0 | N/A | Disable checking for correct `NODE_ENV` environment variable when running against BitGo production environment. | | -i | --ipc | `BITGO_IPC` | N/A | If set, bind to the given IPC (unix domain) socket. Binding to an IPC socket can be useful if the caller of bitgo-express resides on the same host as the bitgo-express instance itself, since the socket can be secured using normal file permissions and ownership semantics. Note: This is not supported on Windows platforms. | | N/A | --authversion | `BITGO_AUTH_VERSION` | 2 | BitGo Authentication scheme version which should be used form making requests to the BitGo server. For more info on authentication scheme versions, view the API reference on the BitGo [Developer Portal](https://developers.bitgo.com/reference/overview#/). | | N/A | --externalSignerUrl | `BITGO_EXTERNAL_SIGNER_URL` | N/A | URL specifying the external API to call for remote signing. | | N/A | --signerMode | `BITGO_SIGNER_MODE ` | N/A | If set, run Express as a remote signer. | | N/A | --signerFileSystemPath | `BITGO_SIGNER_FILE_SYSTEM_PATH ` | N/A | Local path specifying where an Express signer machine keeps the encrypted user private keys. Required when signerMode is set. | | N/A | --signerRegistryFilePath | `BITGO_SIGNER_REGISTRY_FILE_PATH` | N/A | Optional path for the consumed-share registry file (defaults to `.consumedShares.json`). Must not equal `--signerFileSystemPath` (the registry would overwrite the encrypted private keys). Useful when the key-file directory is read-only. | | N/A | --lightningSignerFileSystemPath | `BITGO_LIGHTNING_SIGNER_FILE_SYSTEM_PATH` | N/A | Local path specifying how to contact the lightning signer node. | | N/A | --keepalivetimeout | `BITGO_KEEP_ALIVE_TIMEOUT` | N/A | BitGo Express server KeepAliveTimeout node. | | N/A | --headerstimeout | `BITGO_HEADERS_TIMEOUT` | N/A | BitGo Express server HeadersTimeout node. | | N/A | --disablepincerts | `BITGO_DISABLE_PIN_CERTS` | false | Disable the default domain-scoped CA bundle restriction for BitGo hostnames. See [Domain-scoped CA trust for BitGo connections](#domain-scoped-ca-trust-for-bitgo-connections). | \[0]: BitGo will also check the additional environment variables for some options for backwards compatibility, but these environment variables should be considered deprecated: - Disable SSL - `DISABLESSL` - `DISABLE_SSL` - `BITGO_DISABLESSL` - Disable Proxy - `DISABLE_PROXY` - Disable Environment Check - `DISABLE_ENV_CHECK` ## Enabling Additional Debug Output In addition to the debug namespaces defined in the [`bitgo` package README](https://github.com/BitGo/BitGoJS/blob/master/modules/bitgo/README.md#available-debug-namespaces) there is a special BitGo Express specific debug namespaces which can be enabled: | Namespace | Description | | --------------- | ------------------------------------------------------------------ | | `bitgo:express` | Additional debug information specific to BitGo Express operations. | ### Example To enable the `bitgo:v2:utxo` and `bitgo:express` debug namespaces, start BitGo Express with `--debug-namespace bitgo:v2:utxo,bitgo:express`. Wildcards using `*` are also supported. For example, all bitgo debug namespaces can be enabled with `--debug-namespace bitgo:*`, but beware, this can be very noisy. ## Wallet Safes (client-side generation) Safe creation and child-wallet minting run local key ceremonies, so they must be called through BitGo Express rather than the BitGo API. The passphrase never leaves the Express host. Production Express requires TLS. | Method | Path | SDK method | | ------ | ------------------------------------------------------------------- | ---------------------- | | POST | `/api/v2/enterprise/{enterpriseId}/safes/generate` | `Safes.generateSafe` | | POST | `/api/v2/enterprise/{enterpriseId}/safes/{safeId}/wallets/generate` | `Safe.createWallet` | | POST | `/api/v2/enterprise/{enterpriseId}/safes/{safeId}/keys/generate` | `Safes.createSafeKeys` | `POST /api/v2/enterprise/{enterpriseId}/safes` (without `/generate`) is initialize-only and is proxied to the BitGo API. Listing, get, finalize, archive, freeze, and update are also proxied. Default Express timeout is 305 seconds. `generateSafe` can run up to four parallel root-key ceremonies; raise `--timeout` if a run is cut off. When signing a safe child wallet through existing Express send/sign routes, `WALLET_{walletId}_PASSPHRASE` is the **safe** passphrase (it decrypts the root user keychain). # Release Notes You can find the complete release notes (since version 4.44.0) [here](https://github.com/BitGo/BitGoJS/blob/master/CHANGELOG.md).