{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-products/wallet/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Set up and run CloudSign","description":"User guides, API reference, and support resources.","siteUrl":"https://docs.ripple.com","lang":"en-US","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"set-up-and-run-cloudsign","__idx":0},"children":["Set up and run CloudSign"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["CloudSign is the Ripple Custody (Wallet) signing node for programmatic MPC. Each CloudSign node holds an encrypted key shard and participates in MPC quorums to generate keys and sign transactions."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Programmatic MPC with CloudSign follows this workflow:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Set up three CloudSign devices in the Ripple Custody (Wallet) console and securely store the pairing key for each device."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Run a CloudSign node for each device, each with its respective pairing key."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Have an owner or administrator approve each CloudSign device."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Create an MPC quorum comprising the CloudSign devices."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Create an MPC wallet with the CloudSign quorum."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Why three devices?"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A quorum needs at least two devices. This guide uses the recommended 2-of-3 configuration: three signers, with two required to sign transactions. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/signing-and-key-management/mpc-quorums#required-number-of-devices-and-signatories"},"children":["Required number of devices and signatories"]},"."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"AWS Nitro Enclaves"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can also run CloudSign inside an AWS Nitro Enclave for hardware-isolated execution. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/signing-and-key-management/devices/run-cloudsign-in-aws-nitro-enclaves"},"children":["Run CloudSign in AWS Nitro Enclaves"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"create-cloudsign-devices-via-the-console","__idx":1},"children":["Create CloudSign devices via the console"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Required role:"]}," Owner, Administrator, Proposer, or Approver."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sign in to the Ripple Custody (Wallet) console and navigate to the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Devices"]}," page."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Console only"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Device management isn't part of the public API — ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/organization-and-access/api-credentials"},"children":["API credentials"]}," don't cover devices. Create and approve devices in the console."]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Add new device"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Choose ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Cloud"]}," as the device type."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Give the device a meaningful name, up to 50 characters."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Save and continue"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"https://files.readme.io/14c7c6cefc0d7efcbc1996016100bc1f3a20827f7d1c0e4f4ad769ff8cfec636-cs1.png","alt":"CloudSign device creation"},"children":[]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The console opens the device page, with the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Pairing key"]}," next to the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Unpaired"]}," status badge:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"https://files.readme.io/83a1ed3b37698e3685ca1bbfcd771bc211dbb4a36b20e7d9b24318b947b81d8d-cs2.png","alt":"CloudSign pairing key screen"},"children":[]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The new device starts in the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["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 page. The pairing key contains one-time authentication credentials, so copy it and store it securely."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Repeat these steps until you have three CloudSign devices, each with its own recorded pairing key."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"set-up-the-cloudsign-node","__idx":2},"children":["Set up the CloudSign node"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"prerequisites","__idx":3},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set up your AWS environment with the following:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["An EC2 instance"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Docker"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The AWS CLI"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We also recommend an IAM role and an AWS KMS key so that CloudSign can encrypt its local database with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_REF"]},". See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#storage-options"},"children":["Storage options"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"authenticate-to-the-ripple-custody-wallet-container-registry","__idx":4},"children":["Authenticate to the Ripple Custody (Wallet) container registry"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ripple Custody (Wallet) uses AWS IAM authentication for access to its container registry. Export the following variables using the credentials ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["provided to you by Ripple"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"shell","header":{"controls":{"copy":{}}},"source":"export AWS_ACCESS_KEY_ID=<palisade_provided_access_key_id>\nexport AWS_SECRET_ACCESS_KEY=<palisade_provided_secret_access_key>\nexport AWS_DEFAULT_REGION=eu-west-2\n","lang":"shell"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Keep these credentials confidential"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ripple Custody (Wallet) provides these credentials specifically for access to the CloudSign container repository. Don't share them or commit them to source control."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sign in to the repository:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"shell","header":{"controls":{"copy":{}}},"source":"aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin 335650072995.dkr.ecr.eu-west-2.amazonaws.com\n","lang":"shell"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"pull-the-cloudsign-image","__idx":5},"children":["Pull the CloudSign image"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"shell","header":{"controls":{"copy":{}}},"source":"docker pull 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>\n","lang":"shell"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Docker image version"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Confirm the current CloudSign image version with your Ripple representative before deployment, and use it in place of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<version>"]}," throughout this guide."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"quick-start-testing-only","__idx":6},"children":["Quick start (testing only)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Run the following command to start a CloudSign node in the foreground:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"docker run \\\n  -v /path/to/volume:/var/cloudsign \\\n  -e DB_ENCRYPTION_KEY_HEX=YOUR_ENCRYPTION_KEY \\\n  -e PAIRING_KEY=YOUR_PAIRING_KEY \\\n  -e LOG_LEVEL=debug \\\n  --rm \\\n  --name cloudsign-node \\\n  -it 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Replace ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/path/to/volume"]}," with your shard storage location and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["YOUR_PAIRING_KEY"]}," with the pairing key from the console."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["CloudSign encrypts its data with the key you provide in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_HEX"]},". Back up and protect the storage directory, and change the encryption key before first production use."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Production recommendation"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In production, use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_REF"]}," with the ARN of an AWS KMS key instead of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_HEX"]},". This encrypts all node data with a key protected by a physical HSM on AWS. The CloudSign node needs ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["encrypt"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["decrypt"]}," permissions on the KMS key."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"run-the-nodes-with-docker-compose","__idx":7},"children":["Run the nodes with Docker Compose"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following example runs three CloudSign nodes, matching the three devices you added in the console:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"services:\n  cloudsign1:\n    image: 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>\n    container_name: cloudsign1\n    stop_grace_period: 110s\n    environment:\n      DB_ENCRYPTION_KEY_HEX: ${ENCRYPTION_KEY_1}\n      PAIRING_KEY: ${PAIRING_KEY_1}\n      CLOUDSIGN_TERMINATION_GRACE_SECONDS: \"110\"\n    volumes:\n      - ${VOLUME_1}:/var/cloudsign\n  cloudsign2:\n    image: 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>\n    container_name: cloudsign2\n    stop_grace_period: 110s\n    environment:\n      DB_ENCRYPTION_KEY_HEX: ${ENCRYPTION_KEY_2}\n      PAIRING_KEY: ${PAIRING_KEY_2}\n      CLOUDSIGN_TERMINATION_GRACE_SECONDS: \"110\"\n    volumes:\n      - ${VOLUME_2}:/var/cloudsign\n  cloudsign3:\n    image: 335650072995.dkr.ecr.eu-west-2.amazonaws.com/external/custody/cloudsign/app:<version>\n    container_name: cloudsign3\n    stop_grace_period: 110s\n    environment:\n      DB_ENCRYPTION_KEY_HEX: ${ENCRYPTION_KEY_3}\n      PAIRING_KEY: ${PAIRING_KEY_3}\n      CLOUDSIGN_TERMINATION_GRACE_SECONDS: \"110\"\n    volumes:\n      - ${VOLUME_3}:/var/cloudsign\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The compose file requires ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ENCRYPTION_KEY"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAIRING_KEY"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["VOLUME"]}," variables suffixed by a number identifying each CloudSign node. Example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]}," file:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"shell","header":{"controls":{"copy":{}}},"source":"# cloudsign1\nENCRYPTION_KEY_1=c6516198ba86603e2fda776706b44373d5630cd2e89352772a9b75cda2df67ed\nPAIRING_KEY_1=eyJkZXZpY2VJZCI6ImQz...8ifQ==\nVOLUME_1=/var/lib/cloudsign1\n\n# cloudsign2\nENCRYPTION_KEY_2=0c92f60527b778f2f0036067429b3982d2c7f79ef657943f0e18c1e918362aa6\nPAIRING_KEY_2=eyJkZXZpY2VJZCI6IjJm...8ifQ==\nVOLUME_2=/var/lib/cloudsign2\n\n# cloudsign3\nENCRYPTION_KEY_3=bf7e463d11347c486f4b8e79284a02e0543369009ff0d1efc1d2644fe63b21a7\nPAIRING_KEY_3=eyJkZXZpY2VJZCI6Ijg5...8ifQ==\nVOLUME_3=/var/lib/cloudsign3\n","lang":"shell"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create the directories in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["VOLUME_x"]}," variables before you run Docker Compose."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"configuration-reference","__idx":8},"children":["Configuration reference"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["CloudSign reads its configuration from environment variables:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Variable"},"children":["Variable"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Default"},"children":["Default"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAIRING_KEY"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["— (required)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["One-time pairing credentials from the console."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_HEX"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["—"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Hex-encoded 32-byte key that encrypts the local database. With ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DRIVER=local"]},", set exactly one of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_HEX"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_REF"]},". With ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DRIVER=postgres"]},", set neither."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_REF"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["—"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["ARN of an AWS KMS key. CloudSign generates a database key, encrypts it with KMS, and stores it as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dbkey.encrypted"]},". Recommended for production."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DRIVER"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["local"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Storage backend: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["local"]}," (embedded encrypted store) or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["postgres"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DATA_SOURCE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["—"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["PostgreSQL connection string. Required when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DRIVER=postgres"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TSM_DB_DATA_SOURCE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["—"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["PostgreSQL connection string for MPC quorum data. Required when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DRIVER=postgres"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CLOUDSIGN_HOME"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/var/cloudsign"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Data directory for the local store."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SIGN_MODE_ACTIVE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Whether this node actively participates in signing."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SESSION_LISTENER_WORKERS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Number of concurrent signing workers (maximum 256). Size to your expected signing rate."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SESSION_LISTENER_SHUTDOWN_DRAIN_MS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["5000"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["How long a stopping node waits for in-flight signing sessions to finish (maximum 60000)."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CLOUDSIGN_TERMINATION_GRACE_SECONDS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["45"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["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."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["HEARTBEAT_INTERVAL_MS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["30000"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Heartbeat reporting interval. The heartbeat drives the device's connection status in the console. Set to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0"]}," to disable."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["LOG_LEVEL"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["—"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Log verbosity, for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["debug"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NEW_RELIC_NAME"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NEW_RELIC_LICENSE"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NEW_RELIC_LABELS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["—"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Optional New Relic monitoring integration."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"storage-options","__idx":9},"children":["Storage options"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"local-encrypted-store-default","__idx":10},"children":["Local encrypted store (default)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By default, CloudSign stores key shards in an embedded database under ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/var/cloudsign"]},", encrypted at rest with a 256-bit AES key. Provide the key directly with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_HEX"]},", or reference an AWS KMS key with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_REF"]},"."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Back up the data directory"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"postgresql","__idx":11},"children":["PostgreSQL"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To store data in PostgreSQL instead of the local store, set:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"shell","header":{"controls":{"copy":{}}},"source":"DB_DRIVER=postgres\nDB_DATA_SOURCE=postgres://user:password@host:5432/cloudsign?sslmode=require\nTSM_DB_DATA_SOURCE=postgres://user:password@host:5432/cloudsign?sslmode=require\n","lang":"shell"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Don't set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_HEX"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_REF"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DRIVER=postgres"]},": CloudSign refuses to start if either is set. CloudSign runs its schema migrations automatically at startup. For quorum data, CloudSign creates a separate database per quorum, so the database user in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TSM_DB_DATA_SOURCE"]}," needs the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CREATEDB"]}," privilege."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"network-requirements","__idx":12},"children":["Network requirements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["CloudSign makes outbound connections only. Allow the following egress:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Destination"},"children":["Destination"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Protocol and port"},"children":["Protocol and port"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Purpose"},"children":["Purpose"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["api.<environment>.palisade.co"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["HTTPS 443"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Device pairing, session coordination, and heartbeats"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["graphql.<environment>.palisade.co"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["WSS 443"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Real-time session notifications"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["MPC message broker"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["AMQPS 5671"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["MPC coordination between quorum members. CloudSign fetches the broker address and credentials from the platform."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["AWS KMS"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["HTTPS 443"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Only when you use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_REF"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["CloudSign requires no inbound access. It exposes a health server on port ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["8000"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/liveness"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/readiness"]}," endpoints — use these for container health probes, but don't expose the port publicly."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"graceful-shutdown-and-upgrades","__idx":13},"children":["Graceful shutdown and upgrades"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When a CloudSign node receives a stop signal, it finishes in-flight signing sessions before exiting, up to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SESSION_LISTENER_SHUTDOWN_DRAIN_MS"]},". Configure your orchestrator's termination grace period (for example, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stop_grace_period"]}," in Docker Compose) to at least the drain time plus a 40-second margin, and set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To upgrade a node:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Pull the new image version."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Stop the running container and let it drain."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Start the new version against the same data volume or PostgreSQL database. Schema migrations run automatically."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Upgrade nodes one at a time so the quorum keeps enough online members to sign."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"approve-cloudsign-devices-via-the-console","__idx":14},"children":["Approve CloudSign devices via the console"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Once the CloudSign nodes are running, each device in the console moves from the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Unpaired"]}," state to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Waiting for approval"]},". This means the device is initialized but not yet ready to use."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An owner or administrator needs to approve each CloudSign device in the console. Approved devices move to the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Paired"]}," state and are ready to use in an MPC quorum."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"create-an-mpc-quorum-via-the-console","__idx":15},"children":["Create an MPC quorum via the console"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Required role:"]}," Owner or Administrator."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each CloudSign device can be a member of many quorums. To create a quorum, navigate to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Controls → MPC Quorums"]}," in the console and click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Create quorum"]},":"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Cloud"]}," as the quorum type."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Give the quorum a meaningful name and description."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select the three CloudSign devices to include."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["2"]}," under ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Required signatures"]},"."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Changing a quorum later"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/signing-and-key-management/mpc-quorums/key-restructuring"},"children":["key restructuring"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Create"]},", review the summary, and then click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Create quorum"]}," to confirm. The CloudSign nodes receive the quorum creation request and join the quorum. Once all devices join successfully, the quorum moves to the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Confirmed"]}," state."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"create-an-mpc-wallet-via-the-console","__idx":16},"children":["Create an MPC wallet via the console"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Required role:"]}," Owner, Administrator, or Proposer."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An MPC wallet requires at least one quorum in the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Confirmed"]}," state. Navigate to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Vaults → ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["[your vault]"]}]}," and click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Create wallet"]},":"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Give the wallet a meaningful name and description."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select the blockchain you want to use."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["MPC"]}," as the keystore."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select the quorum you created."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Keystore is permanent"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Select the method for securely generating and storing your wallet's key carefully — you can't change it after the wallet is created."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"deployment-architectures","__idx":17},"children":["Deployment architectures"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"single-node","__idx":18},"children":["Single node"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"https://files.readme.io/e6ef56d0dfb3cba971b8c03da4168e13898d8d3d6e99cbdb183aa6d0e57a8223-image.png","alt":"Single node setup architecture"},"children":[]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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 Ripple Custody (Wallet) platform and the MPC messaging layer. The NAT gateway allows no inbound traffic."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"quorum","__idx":19},"children":["Quorum"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"https://files.readme.io/6109ba5c94d4160e1f9babd881f2890be174cb76ea1d439c962f06772b483ffa-image_1.png","alt":"Quorum setup architecture"},"children":[]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"best-practices","__idx":20},"children":["Best practices"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use separate AWS accounts or organizations to segregate access and privileges for CloudSign."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use separate, single-purpose, multi-region replicated KMS keys for encrypting disks and EBS volumes."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Don't allow any inbound access to the EC2 instances, including SSH."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use trusted AMIs for EC2 instances."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Back up EBS volumes securely using snapshots."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting","__idx":21},"children":["Troubleshooting"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Problem"},"children":["Problem"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Solution"},"children":["Solution"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Device stays in ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Unpaired"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Node not running or pairing key incorrect"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify the container is running; check the pairing key matches"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Device stuck on ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Waiting for approval"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No owner or administrator approval"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Have an owner or administrator approve the device"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Quorum not moving to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Confirmed"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["One or more nodes offline"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure all three CloudSign containers are running"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Wallet provisioning fails"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Quorum not confirmed or nodes unreachable"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify the quorum status is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Confirmed"]},"; check node connectivity"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Node unhealthy or unreachable"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Blocked egress"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Check ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/liveness"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/readiness"]}," on port 8000; verify outbound access to the API, GraphQL, and AMQPS endpoints"]}]}]}]}]}]},"headings":[{"value":"Set up and run CloudSign","id":"set-up-and-run-cloudsign","depth":1},{"value":"Create CloudSign devices via the console","id":"create-cloudsign-devices-via-the-console","depth":2},{"value":"Set up the CloudSign node","id":"set-up-the-cloudsign-node","depth":2},{"value":"Prerequisites","id":"prerequisites","depth":3},{"value":"Authenticate to the Ripple Custody (Wallet) container registry","id":"authenticate-to-the-ripple-custody-wallet-container-registry","depth":3},{"value":"Pull the CloudSign image","id":"pull-the-cloudsign-image","depth":3},{"value":"Quick start (testing only)","id":"quick-start-testing-only","depth":3},{"value":"Run the nodes with Docker Compose","id":"run-the-nodes-with-docker-compose","depth":3},{"value":"Configuration reference","id":"configuration-reference","depth":2},{"value":"Storage options","id":"storage-options","depth":2},{"value":"Local encrypted store (default)","id":"local-encrypted-store-default","depth":3},{"value":"PostgreSQL","id":"postgresql","depth":3},{"value":"Network requirements","id":"network-requirements","depth":2},{"value":"Graceful shutdown and upgrades","id":"graceful-shutdown-and-upgrades","depth":2},{"value":"Approve CloudSign devices via the console","id":"approve-cloudsign-devices-via-the-console","depth":2},{"value":"Create an MPC quorum via the console","id":"create-an-mpc-quorum-via-the-console","depth":2},{"value":"Create an MPC wallet via the console","id":"create-an-mpc-wallet-via-the-console","depth":2},{"value":"Deployment architectures","id":"deployment-architectures","depth":2},{"value":"Single node","id":"single-node","depth":3},{"value":"Quorum","id":"quorum","depth":3},{"value":"Best practices","id":"best-practices","depth":3},{"value":"Troubleshooting","id":"troubleshooting","depth":2}],"frontmatter":{"title":"Set up and run CloudSign","seo":{"title":"Set up and run CloudSign"}},"lastModified":"2026-10-09T11:09:54.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/products/wallet/signing-and-key-management/devices/set-up-cloudsign","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}