# Set up and run CloudSign

CloudSign is the Wallet-as-a-Service signing node for programmatic MPC. Each CloudSign node holds an encrypted key shard and participates in MPC quorums to generate keys and sign transactions.

Programmatic MPC with CloudSign follows this workflow:

1. Set up three CloudSign devices in the Wallet-as-a-Service console and securely store the pairing key for each device.
2. Run a CloudSign node for each device, each with its respective pairing key.
3. Approve each CloudSign device, or have an administrator approve it.
4. Create an MPC quorum comprising the CloudSign devices.
5. Create an MPC wallet with the CloudSign quorum.


Why three devices?
A single MPC quorum requires a total of three signers, with two required to sign transactions.

AWS Nitro Enclaves
You can also run CloudSign inside an AWS Nitro Enclave for hardware-isolated execution. See [Run CloudSign in AWS Nitro Enclaves](/products/wallet/user-interface/devices/run-cloudsign-in-aws-nitro-enclaves).

## Create CloudSign devices in the console

Sign in to the Wallet-as-a-Service console as an administrator or a standard user and navigate to the **Devices** page.

Console only
Device management isn't part of the public API — [API credentials](/products/wallet/user-interface/api/what-are-credentials-in-wallet) don't cover devices. Create and approve devices in the console.

1. Click **Add a device**.
2. Choose **CloudSign** as the device type.
3. Give the device a meaningful name.
4. Click **Save and continue**.


![CloudSign device creation](https://files.readme.io/14c7c6cefc0d7efcbc1996016100bc1f3a20827f7d1c0e4f4ad769ff8cfec636-cs1.png)

The console displays the pairing key screen:

![CloudSign pairing key screen](https://files.readme.io/83a1ed3b37698e3685ca1bbfcd771bc211dbb4a36b20e7d9b24318b947b81d8d-cs2.png)

The new device starts in the **Unpaired** state, which means it isn't initialized or available for use. The CloudSign node you run in the next step needs the pairing key shown on this screen. The pairing key contains one-time authentication credentials, so copy it and store it securely.

Repeat these steps until you have three CloudSign devices, each with its own recorded pairing key.

## Set up the CloudSign node

### Prerequisites

Set up your AWS environment with the following:

- An EC2 instance
- Docker
- The AWS CLI


We also recommend an IAM role and an AWS KMS key so that CloudSign can encrypt its local database with `DB_ENCRYPTION_KEY_REF`. See [Storage options](#storage-options).

### Authenticate to the Wallet-as-a-Service container registry

Wallet-as-a-Service uses AWS IAM authentication for access to its container registry. Export the following variables using the credentials **provided to you by Ripple**:

```shell
export AWS_ACCESS_KEY_ID=<palisade_provided_access_key_id>
export AWS_SECRET_ACCESS_KEY=<palisade_provided_secret_access_key>
export AWS_DEFAULT_REGION=eu-west-2
```

Keep these credentials confidential
Wallet-as-a-Service provides these credentials specifically for access to the CloudSign container repository. Don't share them or commit them to source control.

Sign in to the repository:

```shell
aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin 335650072995.dkr.ecr.eu-west-2.amazonaws.com
```

### Pull the CloudSign image

```shell
docker pull 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>
```

Docker image version
Confirm the current CloudSign image version with your Ripple representative before deployment, and use it in place of `<version>` throughout this guide.

### Quick start (testing only)

Run the following command to start a CloudSign node in the foreground:

```bash
docker run \
  -v /path/to/volume:/var/cloudsign \
  -e DB_ENCRYPTION_KEY_HEX=YOUR_ENCRYPTION_KEY \
  -e PAIRING_KEY=YOUR_PAIRING_KEY \
  -e LOG_LEVEL=debug \
  --rm \
  --name cloudsign-node \
  -it 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>
```

Replace `/path/to/volume` with your shard storage location and `YOUR_PAIRING_KEY` with the pairing key from the console.

CloudSign encrypts its data with the key you provide in `DB_ENCRYPTION_KEY_HEX`. Back up and protect the storage directory, and change the encryption key before first production use.

Production recommendation
In production, use `DB_ENCRYPTION_KEY_REF` with the ARN of an AWS KMS key instead of `DB_ENCRYPTION_KEY_HEX`. This encrypts all node data with a key protected by a physical HSM on AWS. The CloudSign node needs `encrypt` and `decrypt` permissions on the KMS key.

### Run the nodes with Docker Compose

The following example runs three CloudSign nodes, matching the three devices you added in the console:

```yaml
services:
  cloudsign1:
    image: 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>
    container_name: cloudsign1
    stop_grace_period: 110s
    environment:
      DB_ENCRYPTION_KEY_HEX: ${ENCRYPTION_KEY_1}
      PAIRING_KEY: ${PAIRING_KEY_1}
      CLOUDSIGN_TERMINATION_GRACE_SECONDS: "110"
    volumes:
      - ${VOLUME_1}:/var/cloudsign
  cloudsign2:
    image: 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>
    container_name: cloudsign2
    stop_grace_period: 110s
    environment:
      DB_ENCRYPTION_KEY_HEX: ${ENCRYPTION_KEY_2}
      PAIRING_KEY: ${PAIRING_KEY_2}
      CLOUDSIGN_TERMINATION_GRACE_SECONDS: "110"
    volumes:
      - ${VOLUME_2}:/var/cloudsign
  cloudsign3:
    image: 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>
    container_name: cloudsign3
    stop_grace_period: 110s
    environment:
      DB_ENCRYPTION_KEY_HEX: ${ENCRYPTION_KEY_3}
      PAIRING_KEY: ${PAIRING_KEY_3}
      CLOUDSIGN_TERMINATION_GRACE_SECONDS: "110"
    volumes:
      - ${VOLUME_3}:/var/cloudsign
```

The compose file requires `ENCRYPTION_KEY`, `PAIRING_KEY`, and `VOLUME` variables suffixed by a number identifying each CloudSign node. Example `.env` file:

```shell
# cloudsign1
ENCRYPTION_KEY_1=c6516198ba86603e2fda776706b44373d5630cd2e89352772a9b75cda2df67ed
PAIRING_KEY_1=eyJkZXZpY2VJZCI6ImQz...8ifQ==
VOLUME_1=/var/lib/cloudsign1

# cloudsign2
ENCRYPTION_KEY_2=0c92f60527b778f2f0036067429b3982d2c7f79ef657943f0e18c1e918362aa6
PAIRING_KEY_2=eyJkZXZpY2VJZCI6IjJm...8ifQ==
VOLUME_2=/var/lib/cloudsign2

# cloudsign3
ENCRYPTION_KEY_3=bf7e463d11347c486f4b8e79284a02e0543369009ff0d1efc1d2644fe63b21a7
PAIRING_KEY_3=eyJkZXZpY2VJZCI6Ijg5...8ifQ==
VOLUME_3=/var/lib/cloudsign3
```

Create the directories in the `VOLUME_x` variables before you run Docker Compose.

## Configuration reference

CloudSign reads its configuration from environment variables:

| Variable | Default | Description |
|  --- | --- | --- |
| `PAIRING_KEY` | — (required) | One-time pairing credentials from the console. |
| `DB_ENCRYPTION_KEY_HEX` | — | Hex-encoded 32-byte key that encrypts the local database. Set exactly one of `DB_ENCRYPTION_KEY_HEX` or `DB_ENCRYPTION_KEY_REF`. |
| `DB_ENCRYPTION_KEY_REF` | — | ARN of an AWS KMS key. CloudSign generates a database key, encrypts it with KMS, and stores it as `dbkey.encrypted`. Recommended for production. |
| `DB_DRIVER` | `local` | Storage backend: `local` (embedded encrypted store) or `postgres`. |
| `DB_DATA_SOURCE` | — | PostgreSQL connection string. Required when `DB_DRIVER=postgres`. |
| `TSM_DB_DATA_SOURCE` | — | PostgreSQL connection string for MPC quorum data. Required when `DB_DRIVER=postgres`. |
| `CLOUDSIGN_HOME` | `/var/cloudsign` | Data directory for the local store. |
| `SIGN_MODE_ACTIVE` | `true` | Whether this node actively participates in signing. |
| `SESSION_LISTENER_WORKERS` | `1` | Number of concurrent signing workers (maximum 256). Size to your expected signing rate. |
| `SESSION_LISTENER_SHUTDOWN_DRAIN_MS` | `5000` | How long a stopping node waits for in-flight signing sessions to finish (maximum 60000). |
| `CLOUDSIGN_TERMINATION_GRACE_SECONDS` | `45` | The termination grace period you have configured in your orchestrator. Must cover the shutdown drain plus a 40-second margin; CloudSign validates this at startup. |
| `HEARTBEAT_INTERVAL_MS` | `30000` | Heartbeat reporting interval. Set to `0` to disable. |
| `LOG_LEVEL` | — | Log verbosity, for example `debug`. |
| `NEW_RELIC_NAME`, `NEW_RELIC_LICENSE`, `NEW_RELIC_LABELS` | — | Optional New Relic monitoring integration. |


## Storage options

### Local encrypted store (default)

By default, CloudSign stores key shards in an embedded database under `/var/cloudsign`, encrypted at rest with a 256-bit AES key. Provide the key directly with `DB_ENCRYPTION_KEY_HEX`, or reference an AWS KMS key with `DB_ENCRYPTION_KEY_REF`.

Back up the data directory
The data directory contains the node's encrypted key shards. Back it up securely — for example, with encrypted EBS snapshots. If you lose more shards than the quorum threshold allows, you lose access to the keys.

### PostgreSQL

To store data in PostgreSQL instead of the local store, set:

```shell
DB_DRIVER=postgres
DB_DATA_SOURCE=postgres://user:password@host:5432/cloudsign?sslmode=require
TSM_DB_DATA_SOURCE=postgres://user:password@host:5432/cloudsign?sslmode=require
```

CloudSign runs its schema migrations automatically at startup. For quorum data, CloudSign creates a separate database per quorum, so the database user in `TSM_DB_DATA_SOURCE` needs the `CREATEDB` privilege.

## Network requirements

CloudSign makes outbound connections only. Allow the following egress:

| Destination | Protocol and port | Purpose |
|  --- | --- | --- |
| `api.<environment>.palisade.co` | HTTPS 443 | Device pairing, session coordination, and heartbeats |
| `graphql.<environment>.palisade.co` | WSS 443 | Real-time session notifications |
| MPC message broker | AMQPS 5671 | MPC coordination between quorum members. CloudSign fetches the broker address and credentials from the platform. |
| AWS KMS | HTTPS 443 | Only when you use `DB_ENCRYPTION_KEY_REF` |


CloudSign requires no inbound access. It exposes a health server on port `8000` with `/liveness` and `/readiness` endpoints — use these for container health probes, but don't expose the port publicly.

## Graceful shutdown and upgrades

When a CloudSign node receives a stop signal, it finishes in-flight signing sessions before exiting, up to `SESSION_LISTENER_SHUTDOWN_DRAIN_MS`. Configure your orchestrator's termination grace period (for example, `stop_grace_period` in Docker Compose) to at least the drain time plus a 40-second margin, and set `CLOUDSIGN_TERMINATION_GRACE_SECONDS` to the same value — CloudSign validates the configuration at startup. If the orchestrator kills the process too early, signing sessions fail and the node exits with an error.

To upgrade a node:

1. Pull the new image version.
2. Stop the running container and let it drain.
3. Start the new version against the same data volume or PostgreSQL database. Schema migrations run automatically.


Upgrade nodes one at a time so the quorum keeps enough online members to sign.

## Approve CloudSign devices

Once the CloudSign nodes are running, each device in the console moves from the **Unpaired** state to **Waiting for approval**. This means the device is initialized but not yet ready to use.

An administrator needs to approve each CloudSign device in the console. Approved devices move to the **Paired** state and are ready to use in an MPC quorum.

## Create an MPC quorum

Each CloudSign device can be a member of many quorums. To create a quorum, navigate to **Controls → MPC Quorums** in the console and click **Create quorum**:

1. Select **CloudSign** as the quorum type.
2. Give the quorum a meaningful name and description.
3. Select the three CloudSign devices to include.
4. Select **2** under **Required signatures**.


Changing a quorum later
You can't edit a quorum in place after you create it. To change its membership or signing threshold while keeping the same keys, use [key restructuring](/products/wallet/user-interface/security-controls/key-restructuring).

Click **Create**. The CloudSign nodes receive the quorum creation request and join the quorum. Once all devices join successfully, the quorum moves to the **Confirmed** state.

## Create an MPC wallet

An MPC wallet requires at least one quorum in the **Confirmed** state. Navigate to **Vaults → *[your vault]*** and click **Create Wallet**:

1. Give the wallet a meaningful name and description.
2. Select the blockchain you want to use.
3. Select **MPC** as the keystore.
4. Select the quorum you created.


Keystore is permanent
Select the method for securely generating and storing your wallet's key carefully — you can't change it after the wallet is created.

Each CloudSign node in the quorum receives a provisioning request. Once provisioning completes, the wallet page shows the wallet status. A successfully provisioned wallet displays a public address and can be enabled for use.

## Deployment architectures

### Single node

A single node setup suits cases where you hold one shard among other shard holders — that is, you don't host the entire quorum yourself.

![Single node setup architecture](https://files.readme.io/e6ef56d0dfb3cba971b8c03da4168e13898d8d3d6e99cbdb183aa6d0e57a8223-image.png)

One CloudSign instance runs on an EC2 machine. It stores state on a mounted, encrypted EBS volume and connects outbound through a NAT gateway in a public subnet to the Wallet-as-a-Service platform and the MPC messaging layer. The NAT gateway allows no inbound traffic.

### Quorum

A quorum setup suits cases where you hold all the shards yourself. This reference architecture lowers network risk and provides redundancy if some shards become unavailable.

![Quorum setup architecture](https://files.readme.io/6109ba5c94d4160e1f9babd881f2890be174cb76ea1d439c962f06772b483ffa-image_1.png)

This example runs a 2-of-3 quorum: two of the three CloudSign devices must sign each transaction. Each individual CloudSign instance is set up the same way as in the single node architecture.

### Best practices

- Use separate AWS accounts or organizations to segregate access and privileges for CloudSign.
- Use separate, single-purpose, multi-region replicated KMS keys for encrypting disks and EBS volumes.
- Don't allow any inbound access to the EC2 instances, including SSH.
- Use trusted AMIs for EC2 instances.
- Back up EBS volumes securely using snapshots.


## Troubleshooting

| Problem | Cause | Solution |
|  --- | --- | --- |
| Device stays in **Unpaired** | Node not running or pairing key incorrect | Verify the container is running; check the pairing key matches |
| Device stuck on **Waiting for approval** | No administrator approval | Have an administrator approve the device |
| Quorum not moving to **Confirmed** | One or more nodes offline | Ensure all three CloudSign containers are running |
| Wallet provisioning fails | Quorum not confirmed or nodes unreachable | Verify the quorum status is **Confirmed**; check node connectivity |
| Node unhealthy or unreachable | Blocked egress | Check `/liveness` and `/readiness` on port 8000; verify outbound access to the API, GraphQL, and AMQPS endpoints |