For AI agents: visit https://vault.docs.blockdaemon.com/llms.txt for an index of all pages formatted in Markdown and endpoints in OpenAPI.

The AWS deployment of the self-installed Institutional Vault uses separate AWS KMS policies and Secrets Manager instances for each Policy Node quorum member. Policy Nodes run inside AWS Nitro enclaves during execution so sensitive in-memory data stays isolated. No single party should have access to multiple master key shards.

## Roles and Responsibilities

Assign team members before you begin. Multi-account installs split Policy Node KMS keys across accounts.

| Roles                   | Responsibilities                                                                                                                                                                                                         |
|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Infrastructure admin     | Manages wallet infrastructure in the wallet AWS account                                                                                                                                                               |
| Policy Node admin       | Manages one Policy Node partition and its KMS key (one admin per Policy Node account in the recommended layout)                                                                                                      |

In the recommended three-account layout:

| Actor    | Roles                                       | Responsibilities                                 |
|----------|--------------------------------------------|--------------------------------------------------|
| Actor A  | Infrastructure and Policy Node 0 admin    | Wallet infrastructure and Policy Node 0 KMS key  |
| Actor B  | Policy Node 1 admin                        | Policy Node 1 KMS key                             |
| Actor C  | Policy Node 2 admin                        | Policy Node 2 KMS key                             |

### Summary of Steps Per Role

| Roles                   | Steps performed                                          |
|-------------------------|--------------------------------------------------------|
| Infrastructure admin     | 1–11, 13–17                                          |
| Policy Node admins       | 1–7, 9 (baseline only in their account), 10 (their Policy Node long-lived stack), 12 (their partition secrets) |

## Prerequisites

01. AWS experience and comfort with shell scripts and `make`.
02. A local install host with **Docker**, **make**, **AWS CLI**, and **cosign** (recommended for image verification).
03. Separate AWS credentials per account. Do not use the AWS root user. Follow [AWS root user best practices](https://docs.aws.amazon.com/accounts/latest/reference/best-practices-root-user.html).
04. An OIDC identity provider for the wallet and Approver App (Auth0, Okta, Azure Entra ID, or equivalent). See [Configuring Okta](https://vault.docs.blockdaemon.com/docs/configuring-okta) for Okta-specific steps.
05. A DNS hosted zone for the wallet (for example `wallet.<companydomain.com>`). The installer creates records in this zone. OIDC redirect URIs must match the deployed hostnames.
06. AWS account IDs authorized to pull from the Blockdaemon delivery ECR (Blockdaemon configures this from your intake form).
07. Emergency Recovery Secret (ERS) public key. See [Step 2](https://vault.docs.blockdaemon.com/docs/aws-ecs-deployment-process#step-2-generate-emergency-recovery-secret).
08. Blockdaemon delivery artifacts: wallet release tag, deployer version, Policy Node / enclave tag (`MPA_IMAGE_TAG`), and API credentials. Blockdaemon sends these through a secure channel if they were not collected on your intake form.
09. **Blockdaemon API key** with Native API, Blocks, Events, Transactions, Fee Estimation, Balances for Wallet and UTXOs, and Historical Token Price. Confirm event streaming is enabled for your organization.
10. Optional integrations:
11. **Chainalysis** ( [https://www.chainalysis.com/](https://www.chainalysis.com/)) if you use transaction risk screening.

## Recommended Wallet Architecture

Review the architecture before you deploy.

## Makefile Variables

Set these as environment variables (recommended) or edit defaults at the top of the Makefile Blockdaemon delivers in your installer bundle.

| Variable                 | Purpose                                        | Example                                           |
|--------------------------|------------------------------------------------|-------------------------------------------------|
| `DEPLOYER_VERSION`       | Wallet deployer container tag                    | `v3.4.0`                                       |
| `MPA_IMAGE_TAG`          | Policy Node / Nitro EIF S3 prefix (`enclave/<tag>/`) | `v8.26.5`                                  |
| `CUSTOMER_SHARED_SEGMENT`| Wallet release tag for the shared S3 bundle      | `v3.4.0`                                       |
| `NAMESPACE`              | Deployment namespace (resource naming)          | `acme-custody`                                 |
| `ENV_PREFIX`             | Environment name and DNS prefix                 | `testnet`                                       |
| `AWS_REGION`             | Target AWS Region                               | `us-east-1`                                    |
| `BACKEND_API_PATH`      | Wallet API base URL (frontend and bootstrap)   | `https://api.testnet.wallet.example.com`       |

Run `make help` from the bundle directory for the full list.

Example exports:

Shell

```shell
export DEPLOYER_VERSION=<deployer-version>
export MPA_IMAGE_TAG=<policy-node-enclave-tag>
export CUSTOMER_SHARED_SEGMENT=<wallet-release-tag>
export NAMESPACE=<namespace>
export ENV_PREFIX=<environment-name>
export AWS_REGION=<aws-region>
export BACKEND_API_PATH=https://api.<environment-name>.wallet.<company-domain>
```

## Step 1: Create Local Installation Environment

**All infrastructure and Policy Node admins** configure a local host with:

- [Docker](https://www.docker.com/)
- `make`
- [AWS CLI](https://aws.amazon.com/cli/)
- [cosign](https://docs.sigstore.dev/cosign/system_config/installation/) (recommended)

Configure AWS credentials for the account you will deploy into. For AWS SSO profiles, set `AWS_PROFILE` and use `LOCAL_AWS_PROFILE_MODE=true` if your team documents that convention.

## Step 2: Generate Emergency Recovery Secret

Generate the ERS key pair before install. Follow [Create ERS Key](https://vault.docs.blockdaemon.com/docs/create-ers-key) for key generation, ceremony guidance, and how to supply `ers_public_key` in `customer-secrets.yml` during [Step 12](https://vault.docs.blockdaemon.com/docs/aws-ecs-deployment-process#step-12-generate-and-populate-secrets).

> 📘
> ### Note
> Skip this step if you already completed your ERS key ceremony.

## Step 3: Receive Blockdaemon Delivery Artifacts

Blockdaemon grants ECR and S3 access and sends:

- Target **wallet release tag** (`CUSTOMER_SHARED_SEGMENT`)
- **Deployer version** (`DEPLOYER_VERSION`)
- **Policy Node / enclave tag** (`MPA_IMAGE_TAG`) when it differs from the wallet tag
- API credentials and any delivery-specific notes

> 📘
> ### Note
> Skip this step if you already have current delivery values from a prior engagement.

## Step 4: Authenticate to the Institutional Vault Registry and Validate Images

**All admins** authenticate to the Blockdaemon ECR registry:

Shell

```shell
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin \
638663786504.dkr.ecr.us-east-1.amazonaws.com
```

Optionally verify the deployer image with cosign (set `DEPLOYER_VERSION` from [Step 3](https://vault.docs.blockdaemon.com/docs/aws-ecs-deployment-process#step-3-receive-blockdaemon-delivery-artifacts)):

Shell

```shell
export DEPLOYER_VERSION=<deployer-version>
BLOCKDAEMON_SIGNING_AUTHORITY="-----BEGIN PUBLIC KEY-----\
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfJLNcMpG3m8EOswCU7cqzWBsHSfY\
+rt4HRvbPWAqadeYxIhhBWXjWDiRk4SAa2XXhZVi7IEO7TMaJcuFyHvoBQ==\
-----END PUBLIC KEY-----" \
cosign verify --key env://BLOCKDAEMON_SIGNING_AUTHORITY \
638663786504.dkr.ecr.us-east-1.amazonaws.com/blockdaemon/wallet-deployer/aws:${DEPLOYER_VERSION}
```

## Step 5: Download the Installer Bundle

**All admins** download the shared installer bundle from S3 into a dedicated working directory. The bundle includes the Makefile, `customer-config.yml` template, `tenant-setup.json`, helper scripts, and cold-wallet templates.

The bundle path is `s3://638663786504-prod-mpa-deployment-customer-artifacts/customer/shared/<wallet-release-tag>/`.

Copy the `Makefile` from S3, then sync the rest of the bundle with `make`:

Shell

```shell
export CUSTOMER_SHARED_SEGMENT=<wallet-release-tag>
aws s3 cp s3://638663786504-prod-mpa-deployment-customer-artifacts/customer/shared/${CUSTOMER_SHARED_SEGMENT}/Makefile .
make pull-customer-assets
```

> 📘
> ### Note
> By default, `pull-customer-assets` does **not** overwrite a local `Makefile`. To refresh the Makefile from S3, run `PULL_SYNC_MAKEFILE=1 make pull-customer-assets` after backing up local edits.

## Step 6: Configure Makefile Variables

**All admins** set the variables in [Makefile Variables](https://vault.docs.blockdaemon.com/docs/aws-ecs-deployment-process#makefile-variables) for the environment they deploy. `ENV_PREFIX` is the environment name (Blockdaemon recommends `testnet` and `mainnet`) and DNS prefix. `NAMESPACE` must match the value you will use in `customer-config.yml`.

## Step 7: Generate Deployment Configuration

**All admins** complete local configuration before infrastructure deploy.

1. Pull the deployer image:

Shell

```shell
make pull-deployer
```

1. Edit `customer-config.yml` with your namespace, AWS accounts, hosted zones, admin users, optional Canton settings, and resource tags. See [customer-config.yml Reference](https://vault.docs.blockdaemon.com/docs/customer-config-yml).
2. Generate deployment assets:

Shell

```shell
make generate-customer-assets
```

### tenant-setup.json

Edit `tenant-setup.json` in the installer bundle before bootstrap ( [Step 16](https://vault.docs.blockdaemon.com/docs/aws-ecs-deployment-process#step-16-populate-initial-tenant-state)). This file is the request body for `POST /api/tenants/default/setup`. It seeds initial Vault users, the owner group, master keys, and registered chains. It does **not** define transaction restrictions or admin policies.

| Field                      | Purpose                                                      |
|----------------------------|-------------------------------------------------------------|
| `Users`                    | Initial Vault users (`ID` is email, `Name` is display name) |
| `Group`                    | Owner group name (for example `owner`)                      |
| `MasterKeysAndAccounts`    | Hot master keys and account names                            |
| `Assets.CAIP2s`           | CAIP-2 chain identifiers to register at bootstrap           |

Configure policies after bootstrap in the Vault UI or through supported policy APIs.

> 📘
> ### Note
> After the first install, you may edit `bd-wallet.yml` directly for some changes. Regenerate from `customer-config.yml` when you change namespace, accounts, or hosted zones.

## Step 8: Optional Cold Wallet Bundle

**Infrastructure admin** only, when cold wallet is in scope:

Shell

```shell
make generate-cold-wallet-bundle
```

This produces `cold-wallet.tar.gz` for air-gapped Policy Node operations. Skip when you deploy hot quorum only.

## Step 9: Deploy Stage 1 (Baseline and Long-Lived Infrastructure)

Confirm `ENV_PREFIX` and `NAMESPACE` match `bd-wallet.yml`, then deploy long-lived infrastructure.

### Infrastructure admin (wallet account)

Either run the stage wrapper:

Shell

```shell
make deploy-stage-1
```

Or run the same steps individually:

Shell

```shell
make deploy-baseline
make deploy-service-images
make generate-conf-files
make fetch-enclave-artifacts
make deploy-wallet-long-lived
make deploy-all-policy-nodes-long-lived
```

`deploy-stage-1` creates baseline CDK resources, populates customer ECR with service images, generates component config files, downloads Nitro `enclave.eif` / `pcr.json` for `MPA_IMAGE_TAG`, and deploys wallet and Policy Node long-lived stacks in the wallet account.

> 📘
> ### Note
> Do not delete long-lived stack resources unless you are decommissioning the environment.

### Policy Node admins (separate accounts)

In each Policy Node account:

Shell

```shell
make deploy-baseline
make fetch-enclave-artifacts
make deploy-policy-node0-long-lived   # or node1 / node2 for that admin
```

Policy Node admins do **not** run `deploy-service-images` or `deploy-wallet-long-lived`.

## Step 10: Deploy Policy Node KMS Keys (Multi-Account)

When Policy Node KMS keys live in accounts other than the wallet account, each **Policy Node admin** deploys only their partition (if not already done in Step 9):

Shell

```shell
make deploy-policy-node0-long-lived
make deploy-policy-node1-long-lived
make deploy-policy-node2-long-lived
```

Run only the target matching the account you control.

## Step 11: Verify Deployer Image

If any admin skipped Step 7.1, pull the deployer before secrets or Stage 2:

Shell

```shell
make pull-deployer
```

## Step 12: Generate and Populate Secrets

The Encryptor Master Password encrypts each Policy Node database that holds a master key share. One password exists per Policy Node partition. The install tooling encrypts and stores values in AWS Secrets Manager.

1. **Infrastructure admin** generates the secrets template:

Shell

```shell
make generate-secret-file
```

1. **Infrastructure admin** edits `customer-secrets.yml` with:

- `ubiquity_org_username` and `ubiquity_auth_token` (Blockdaemon Workspace and API key)
- OIDC values under the `auth0` block (`AUDIENCE`, `ISSUER`, `CLIENT_ID_SPA`, `CLIENT_ID_NATIVE`) for Auth0-shaped configs; use the keys your IdP mapping expects
- `ers_public_key` (see [Create ERS Key](https://vault.docs.blockdaemon.com/docs/create-ers-key))
- `broker-root-ca` and `broker-root-ca-key` (NATS mTLS root; use YAML `|` literal blocks with PEM newlines)
- Optional: `chainalysis-user`, `chainalysis-token`, `canton-client-id`, `canton-client-secret`, custom chain RPC URLs

Do not commit `customer-secrets.yml`.

2. **Infrastructure admin**:

Shell

```shell
make populate-secrets-wallet
```

3. **Policy Node 0 admin**:

Shell

```shell
make populate-secrets-policy-node0
```

4. **Policy Node 1 admin**:

Shell

```shell
make populate-secrets-policy-node1
```

5. **Policy Node 2 admin**:

Shell

```shell
make populate-secrets-policy-node2
```

> 🚧
> ### Warning
> Re-running `populate-secrets-wallet` on a live environment **overwrites** most wallet-partition secrets in AWS Secrets Manager. It does **not** replace Policy Node Encryptor Master Passwords (those belong to the policy-node populate targets). Avoid blind re-runs: `db-info` and `webhook-token` values are regenerated and can break a running wallet. Re-run populate only when you intend to rotate the secrets you changed in `customer-secrets.yml`.

## Step 13: Deploy Stage 2 (Wallet Runtime and Policy Nodes)

After secrets are populated, **infrastructure admin** deploys runtime services on ECS and Nitro hosts.

Either:

Shell

```shell
make deploy-stage-2
```

Or individually:

Shell

```shell
make generate-fe-env
make deploy-wallet
make deploy-nitro-nodes
make start-wallet
```

- `**generate-fe-env**` requires `BACKEND_API_PATH`. It builds `env-config.js` and packages `frontend.zip`.
- `**deploy-wallet**` deploys ECS task definitions with wallet count **0** (services defined, wallet not started).
- `**deploy-nitro-nodes`\*\* deploys Nitro EC2 instances for all three Policy Nodes.
- `**start-wallet`\*\* sets `WalletServiceCount=1` and starts the wallet service.

> 🚧
> ### Warning
> After go-live, do not change the public API URL without a planned migration. Event streaming and integrations depend on a stable endpoint.

If CDK fails pulling the AWS SAM CLI image from `public.ecr.aws`, run `make ecr-public-login` on the host and retry.

## Step 14: Apply Configuration Changes After Install

When you change `bd-wallet.yml` (or regenerate it) on an **already running** wallet:

Shell

```shell
make generate-conf-files
make deploy-wallet
```

Add `make deploy-nitro-nodes` when Policy Node configuration changed. Add `make generate-fe-env` when `BACKEND_API_PATH` changed.

Use `make start-wallet` only if the wallet service is still at desired count **0**. Do not use `start-wallet` as a substitute for `deploy-wallet` when applying config.

For secret value changes, edit `customer-secrets.yml` and run the appropriate `populate-secrets-`\* target for the partition you changed, then redeploy affected services.

## Step 15: Optional RDS Snapshot Before Upgrades

Before major upgrades:

Shell

```shell
export RDS_DB_INSTANCE_IDENTIFIER=<your-cluster-id>
make backup-rds-snapshot
```

See [AWS Wallet Upgrade](https://vault.docs.blockdaemon.com/docs/aws-wallet-upgrade).

## Step 16: Populate Initial Tenant State

After the wallet is healthy, **infrastructure admin** seeds users, groups, master keys, and registered chains from `tenant-setup.json` (see [Step 7](https://vault.docs.blockdaemon.com/docs/aws-ecs-deployment-process#step-7-generate-deployment-configuration)).

The wallet creates a bootstrap Admin API key automatically on first start ( [Step 13](https://vault.docs.blockdaemon.com/docs/aws-ecs-deployment-process#step-13-deploy-stage-2-wallet-runtime-and-policy-nodes)) and stores it in AWS Secrets Manager. Retrieve it before bootstrap (replace `NAMESPACE` and `ENV_PREFIX` with your Makefile values, or substitute your namespace and environment name):

Shell

```shell
export WALLET_API_KEY=$(aws secretsmanager get-secret-value \
  --region "${AWS_REGION}" \
  --secret-id "bd-wallet.${NAMESPACE}.${ENV_PREFIX}.bootstrap-key" \
  --query SecretString --output text)
export BACKEND_API_PATH=https://api.<environment-name>.wallet.<company-domain>
make bootstrap-policies-and-users
```

This calls `POST /api/tenants/default/setup` as the `bootstrap@blockdaemon-wallet.com` system user. It is **one-shot**: a second successful run returns an "already populated" response. Configure transaction restrictions and admin policies afterward in the Vault UI or through supported policy APIs.

> 📘
> ### Note
> Override the JSON path with `TENANT_SETUP_JSON=/path/to/file.json` when needed.

## Step 17: Backup the Wallet Master Private Key

After first login and Approver App pairing, approve master key backup in the Approver App. Store backup material separately from the live system.

See [Back Up Master Key](https://vault.docs.blockdaemon.com/docs/backup-master-key).

Updated about 1 month ago.
