{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-products/wallet/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Disaster recovery","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":"disaster-recovery","__idx":0},"children":["Disaster recovery"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use this guide when something has gone wrong and you need to restore: a CloudSign node has failed, a device holding key shards is gone for good, you need to prove your recovery key still works, or you need to recover wallet assets without the Wallet-as-a-Service platform."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/configure-backup-and-recovery"},"children":["Configure backup and recovery"]}," covers everything you do ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["in advance"]}," (configuring backups, downloading the recovery CLI, and building your wallet inventory), especially in its ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/configure-backup-and-recovery#prepare-for-disaster-recovery"},"children":["preparation checklist"]},". This page assumes that you've completed that preparation."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"which-situation-are-you-in","__idx":1},"children":["Which situation are you in?"]},{"$$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":"Situation"},"children":["Situation"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What to do"},"children":["What to do"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A CloudSign upgrade failed, or a node's database is corrupted"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#restore-a-cloudsign-node-database"},"children":["Restore the node database"]}," (no key recovery needed)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A CloudSign node or mobile device holding key shards is permanently lost, and the remaining devices still meet the quorum threshold"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#replace-a-lost-shard-holding-device"},"children":["Replace the lost device"]}," by restructuring the quorum (no key recovery needed)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["You need to verify that your backup encryption keys are still operational (at an annual review, after a personnel change, or before a planned recovery)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#verify-your-recovery-private-key"},"children":["Verify your recovery private key"]}," with the recovery CLI"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The Wallet-as-a-Service platform is permanently unavailable, or you've lost so many shard-holding devices that the remaining ones can't meet the quorum threshold"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#recover-wallets-independently-of-ripple"},"children":["Recover wallets independently of Ripple"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["You need to confirm where Wallet-as-a-Service stores your backups (for an audit or annual review)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Check each kit under ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Settings → Backup & Recovery → Manage backups"]}," in the console, and see the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/configure-backup-and-recovery#own-s3-bucket-or-managed-storage-only"},"children":["storage options"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"restore-a-cloudsign-node-database","__idx":2},"children":["Restore a CloudSign node database"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If an upgrade fails or a node's database becomes corrupted, restore the database from your backup and restart the node against it. On startup, the node reloads its persisted quorums and rejoins them with its existing key shards, so you don't need to re-pair or regenerate keys. Keep these constraints in mind:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Restore all of the node's stores as one consistent snapshot."]}," With local storage, the stores live under ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/var/cloudsign"]}," and depend on each other. If you restore only part of the directory, or mix files from different snapshots, the node can end up unable to decrypt its own key shards. With PostgreSQL storage (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DRIVER=postgres"]},"), the node uses two databases, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_DATA_SOURCE"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TSM_DB_DATA_SOURCE"]},"; restore both to the same point in time."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Restart the node with the same database encryption key."]}," If you use KMS-based encryption (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_REF"]},"), the snapshot must include ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dbkey.encrypted"]},", and the host must retain permission to decrypt with that exact KMS key. On the KMS path, if ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dbkey.encrypted"]}," is missing, CloudSign doesn't report a missing file. Instead, it generates a new database key, and the restored data then fails with decryption errors. If you use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DB_ENCRYPTION_KEY_HEX"]},", restart with the same hex key."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Never restore a snapshot taken before a key reshare or quorum restructure."]}," Resharing replaces the key shards without changing wallet addresses, so a pre-reshare snapshot silently mismatches the other quorum members' shards and signing fails."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Restore the same CloudSign version you backed up."]}," If you downgrade a data directory that you paired on a newer major version, the downgrade can wipe the keychain."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fix file ownership after a host-side restore."]}," The CloudSign container runs as a non-root user. If you restore files as root, they cause ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["permission denied"]}," errors at startup. To fix this, run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["chown -R 999:999"]}," on the data directory."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/user-interface/devices/set-up-and-run-cloudsign#storage-options"},"children":["Set up and run CloudSign"]}," for storage options and upgrade procedures, and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/configure-backup-and-recovery#back-up-cloudsign-node-databases"},"children":["back up node databases"]}," for the backup side."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"replace-a-lost-shard-holding-device","__idx":3},"children":["Replace a lost shard-holding device"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If a CloudSign node or mobile device that holds key shards is gone for good (the host is destroyed and has no usable database backup, or the phone is lost), the wallet key itself stays intact as long as the remaining devices can still reach the quorum's required-signatures threshold. You don't recover the key. Instead, you ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["restructure the quorum"]}," to swap the lost device for a new one. The wallet's private key and addresses don't change."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Pair the replacement device first"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Restructuring only lets you add devices that are already paired ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["and approved"]}," in your organization. Before you start, pair the new CloudSign node or mobile device, have an owner or administrator approve it, and confirm that it shows as ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Approved"]}," and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Enabled"]},". For details, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/manage-devices"},"children":["Manage devices"]},". After restructuring begins, the quorum is in maintenance mode and can't sign until restructuring completes."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Don't disable or delete the lost device first"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Wallet-as-a-Service checks the devices you remove as well as the devices you add. If the lost device is already disabled or deleted, Wallet-as-a-Service rejects the restructure with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["device is not compatible for this operation"]},". Keep the lost device enabled in ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Devices"]}," until restructuring completes, and then decommission it in step 5. If you already disabled it, re-enable it before you restructure. If you already deleted it, contact Ripple support."]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Decide whether you really need to restructure."]}," If the lost device is a CloudSign node and you have a consistent backup of its data directory, ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#restore-a-cloudsign-node-database"},"children":["restore the node database"]}," instead. It's faster and involves no quorum change."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Check that you can still restructure the quorum."]}," You must retain at least as many original members as the current required-signatures threshold. In a 2-of-3 quorum, you can lose one device and restructure. If you've lost two, you can't restructure, and you're in the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#recover-wallets-independently-of-ripple"},"children":["independent recovery"]}," situation instead."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Pair and approve the replacement device"]},", as the note above describes."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Restructure the quorum."]}," In the console, open the quorum's detail page, select ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Restructure quorum"]}," from the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Actions"]}," menu, add the new device, remove the lost one, and confirm. ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/manage-mpc-quorums#perform-a-restructure"},"children":["Manage MPC quorums"]}," describes the full procedure, including the maintenance-mode warning."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Decommission the lost device."]}," After restructuring completes, the lost device's shards are obsolete and can no longer sign, but treat them as sensitive material anyway: disable or delete the device under ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Devices"]}," (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/manage-devices#delete-a-device"},"children":["Manage devices"]},"), and revoke any credentials or cloud resources it used."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Restructuring is currently self-service for Cloud quorums running CloudSign 1.10.0 or later. The quorum must be active, and the new device set must include at least one device that isn't in the current quorum. Wallet-as-a-Service rejects a restructure that only removes devices. If ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Restructure quorum"]}," doesn't appear in the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Actions"]}," menu, or the lost device belongs to a Mobile or Mixed quorum, contact Ripple support to coordinate the restructure."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Restructuring replaces the key shards without changing the wallet's underlying key, so your key shard backups stay valid and need no action. Wallet-as-a-Service binds each backup file to the wallet's key ID and quorum ID, and neither of them changes. The backup still holds the key shares of the devices that were in the quorum when you created the wallet. That doesn't affect recovery, because the recovery CLI reconstructs the private key from the backup itself rather than restoring shards to devices. After the restructure, don't restore node-database snapshots from ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["before"]}," the restructure (see the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#restore-a-cloudsign-node-database"},"children":["restore constraints"]},")."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"verify-your-recovery-private-key","__idx":4},"children":["Verify your recovery private key"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your key shard backups are only as good as the RSA private key that decrypts them. Periodically verify that the key file is intact and that the password you hold still decrypts it. Do this at least at your annual review, whenever the person who holds the key changes, and before any planned recovery. To verify the key, run:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"wallet-recovery-cli validate-private-key --private-key-file=recovery-private.hex\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The command detects whether the file is encrypted, prompts for the password, decrypts the file in memory, and confirms that the result is a valid RSA private key. It never modifies the file, and it clears the decrypted key from memory when it finishes. On success, it prints:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"Validation successful: password is correct and private key is valid\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you see ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Validation failed: incorrect password or corrupted file"]},", first re-check the password against your secure record. If the password is right, the file is damaged. Restore it from your other secure copies or, as a last resort, switch to another recovery key pair in the kit. If the key file isn't encrypted, the command reports ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Private key file is not encrypted"]}," and exits without a password prompt."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"This checks the key, not the backups"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validate-private-key"]}," proves that the private key is readable. It doesn't prove that the key matches the public key on your backup kit, or that the CLI can actually decrypt a backup file. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recover"]}," step checks that, and it fails if the key and kit don't match. The only end-to-end test is the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#test-this-procedure-in-sandbox"},"children":["sandbox rehearsal"]}," of the full recovery procedure."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you lose the private key, or no password unlocks it, you can't use it to decrypt existing backups. Check whether the kit holds other recovery public keys whose private keys you still control. Any one of them is enough. Otherwise, create a new backup kit with a new key pair for future quorums, and contact Ripple support about wallets under the affected quorums. For more information, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/configure-backup-and-recovery#maintain-your-backups"},"children":["Maintain your backups"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"recover-wallets-independently-of-ripple","__idx":5},"children":["Recover wallets independently of Ripple"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Follow this procedure when the platform is permanently unavailable and you need to move assets out of your wallets. The procedure uses the key shard backups that Wallet-as-a-Service writes to your S3 bucket to reconstruct each wallet's private key with the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/palisadeinc/wallet-recovery-cli"},"children":["Wallet-as-a-Service Wallet Recovery CLI"]},". Recovery is per wallet and interactive: the CLI prompts for passwords on the terminal and has no unattended mode, so you can't script it over your inventory. Repeat the procedure for each wallet, and plan the time this takes if you hold many wallets. If you need to recover a large number of wallets, contact Ripple."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"prerequisites","__idx":6},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before you start, you must already have the following items from the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/configure-backup-and-recovery#prepare-for-disaster-recovery"},"children":["preparation checklist"]},":"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The recovery CLI binary, downloaded in advance"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your RSA recovery private key, and its password if encrypted (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#verify-your-recovery-private-key"},"children":["verify it first"]},")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your offline wallet inventory, which lists the quorum ID, key ID (= wallet ID), and key type for each wallet. The backup files ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["don't"]}," store the quorum ID anywhere (the key ID is only the S3 folder name), so without the inventory, you can't recover wallets from the backups alone."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["AWS access to read your backup bucket, and to decrypt with the KMS key if the objects use SSE-KMS"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["An air-gapped machine with the CLI installed"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If Wallet-as-a-Service only ever stored your backups in its managed bucket (the default configuration), you have no self-service way to retrieve them. Ripple-independent recovery requires that you configured your own bucket before you created the wallets."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"danger","name":"The recovered key is the wallet"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This procedure reconstructs the wallet's raw private key in one piece. That never happens during normal MPC operation, where the key exists only as distributed shards. Anyone who obtains the recovered key has irrevocable control of the wallet's assets. Perform step 3 and all later steps on an air-gapped machine, and treat every file involved as highly sensitive."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-1-retrieve-the-backup-files","__idx":7},"children":["Step 1: Retrieve the backup files"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Download the recovery data for the key from your S3 bucket:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# List all recovery kits\naws s3 ls s3://your-backup-bucket/ --recursive\n\n# Download a specific wallet's recovery kit (the key ID is the wallet ID)\naws s3 cp s3://your-backup-bucket/<key_id>/recovery_shard-0.txt ./recovery-kit.b64\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You need only the file that matches the recovery private key you hold. If your kit has multiple recovery public keys, don't rely on the number in the filename. Either run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recover"]}," with each file in turn, or decode the file (base64) and compare its ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recoveryPublicKeyHex"]}," field with the hex form of your public key. If a file belongs to a different key, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recover"]}," stops with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Recovery public key does not match the private key."]}," before it recovers anything. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recoveryPublicKeyHex"]}," is lowercase hex of the DER public key. If your public key file is binary DER (the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["generate-recovery-keypair"]}," default), convert it with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["xxd -p recovery-public.der | tr -d '\\n'"]},". For the file format, see the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/user-interface/security-controls/wallet-backup-configuration#data-structure"},"children":["data structure reference"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-move-materials-to-the-air-gapped-machine","__idx":8},"children":["Step 2: Move materials to the air-gapped machine"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use removable media to transfer the following items to the air-gapped machine:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The recovery kit file that you downloaded from S3"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your RSA recovery private key"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The recovery CLI binary (which you downloaded in advance)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your inventory record of the wallet's quorum ID, key ID, and key type"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-3-eject-the-private-key","__idx":9},"children":["Step 3: Eject the private key"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Run the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recover"]}," command:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"wallet-recovery-cli recover \\\n  --recovery-kit-file=recovery-kit.b64 \\\n  --private-key-file=recovery-private.hex \\\n  --quorum-id=<QUORUM_UUID> \\\n  --key-id=<KEY_UUID> \\\n  --key-type=SECP256K1 \\\n  --output-file=recovered.enc \\\n  --encrypt-output=true\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--key-type=ED25519"]}," for Solana wallets. SECP256K1 (the default) covers Ethereum and other EVM chains, XRP, and Bitcoin."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The quorum ID and key ID aren't just labels. Wallet-as-a-Service binds them cryptographically into the recovery data, so recovery fails if either is wrong. The CLI validates that your RSA private key matches the public key in the recovery data, verifies the integrity of the recovery data, and then reconstructs the private key. By default, the CLI encrypts the output file with AES-256 by using a password you choose (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--encrypt-output=true"]},"; passwords must be at least 8 characters)."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The CLI also prints the blockchain addresses derived from the recovered key. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Verify these match the wallet's addresses"]}," in your inventory before you go further. This confirms that you recovered the right key."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-4-decrypt-the-key-when-youre-ready-to-use-it","__idx":10},"children":["Step 4: Decrypt the key when you're ready to use it"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you saved the output encrypted, decrypt it when you're ready to sign:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"wallet-recovery-cli decrypt \\\n  --encrypted-private-key-file=recovered.enc \\\n  --decrypted-output-file=recovered-key.bin\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can re-check the derived addresses at any time with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["print-address"]},". It detects an encrypted file and prompts for the password. Always pass ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--key-type"]}," from your inventory. Without it, the tool tries SECP256K1 first, and an ED25519 key passes that check, so for every Solana wallet it prints EVM, XRP, and Bitcoin addresses that aren't yours."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"wallet-recovery-cli print-address --private-key-file=recovered.enc --key-type=<KEY_TYPE>\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-5-sign-a-transaction-and-move-the-assets","__idx":11},"children":["Step 5: Sign a transaction and move the assets"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The decrypted output is the raw 32-byte private key with no encoding. (Without ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--output-file"]},", the CLI prints it to stdout as base64.) Most tooling expects the key as hex text, so convert it first:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"xxd -p recovered-key.bin | tr -d '\\n'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the key with standard blockchain tooling to sign a transaction that sends the wallet's assets to a secure destination address (for example, a wallet at another custodian or a hardware wallet you control):"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Ethereum and EVM chains"]},": Import the hex key into any EVM-compatible wallet or library (for example, MetaMask's import account, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["new ethers.Wallet('0x' + hexKey)"]}," in ethers.js, or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["web3.eth.accounts.privateKeyToAccount"]}," in web3.js)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Bitcoin"]},": Convert the key to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["compressed"]}," WIF and import it as a Native SegWit (P2WPKH) key, for example, with a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wpkh()"]}," descriptor in Bitcoin Core or a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["p2wpkh:"]}," prefixed key in Electrum. The wallet's address derives from the compressed public key, so an uncompressed or legacy import produces a different address."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["XRP"]},": Use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["xrpl"]}," library with the secp256k1 key in its 33-byte hex form, which you get by prefixing the 32-byte hex key with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["00"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Solana"]},": The output is the raw ED25519 private scalar, not the 32-byte seed that Solana tooling expects, and you can't convert a scalar back to a seed. As a result, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["solana-keygen"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Keypair.fromSecretKey"]},", and standard Solana wallets can't import it. Verify the recovered key with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["print-address --key-type ED25519"]},", and contact Ripple support for help moving Solana assets."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Where possible, sign the transaction offline on the air-gapped machine, and broadcast the signed transaction from a separate, connected machine."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-6-clean-up","__idx":12},"children":["Step 6: Clean up"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["After you confirm that the assets have arrived at the destination:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Securely delete the recovered private key, the decrypted output files, and the recovery kit copies from the air-gapped machine and any removable media. On Linux, use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["shred -u <file>"]},". On macOS, APFS doesn't support secure erase at the file level, so rely on FileVault full-disk encryption and delete normally."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Treat the recovered wallet as compromised. The key existed in one piece, so don't send assets to it again."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"test-this-procedure-in-sandbox","__idx":13},"children":["Test this procedure in sandbox"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In the sandbox rehearsal from the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/configure-backup-and-recovery#prepare-for-disaster-recovery"},"children":["preparation checklist"]},", you run exactly the procedure above against a sandbox wallet. There's no dry-run or verify-only mode. A test recovery produces a live private key, so finish every rehearsal with ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-6-clean-up"},"children":["Step 6: Clean up"]},", including secure deletion of the recovered key."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related-guides","__idx":14},"children":["Related guides"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/configure-backup-and-recovery"},"children":["Configure backup and recovery"]},": Set up backups and complete the preparation checklist this procedure depends on"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/user-interface/security-controls/wallet-backup-configuration"},"children":["AWS backup infrastructure reference"]},": Backup file format, folder structure, and troubleshooting"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/user-interface/devices/set-up-and-run-cloudsign"},"children":["Set up and run CloudSign"]},": CloudSign storage options and upgrades"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/manage-mpc-quorums"},"children":["Manage MPC quorums"]}," and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/wallet/admin-guide/manage-devices"},"children":["Manage devices"]},": Restructuring procedure and device pairing, approval, and removal"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/palisadeinc/wallet-recovery-cli"},"children":["Wallet-as-a-Service Wallet Recovery CLI"]},": Complete CLI documentation, including ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["generate-recovery-keypair"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validate-private-key"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recover"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["decrypt"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["print-address"]}]}]}]},"headings":[{"value":"Disaster recovery","id":"disaster-recovery","depth":1},{"value":"Which situation are you in?","id":"which-situation-are-you-in","depth":2},{"value":"Restore a CloudSign node database","id":"restore-a-cloudsign-node-database","depth":2},{"value":"Replace a lost shard-holding device","id":"replace-a-lost-shard-holding-device","depth":2},{"value":"Verify your recovery private key","id":"verify-your-recovery-private-key","depth":2},{"value":"Recover wallets independently of Ripple","id":"recover-wallets-independently-of-ripple","depth":2},{"value":"Prerequisites","id":"prerequisites","depth":3},{"value":"Step 1: Retrieve the backup files","id":"step-1-retrieve-the-backup-files","depth":3},{"value":"Step 2: Move materials to the air-gapped machine","id":"step-2-move-materials-to-the-air-gapped-machine","depth":3},{"value":"Step 3: Eject the private key","id":"step-3-eject-the-private-key","depth":3},{"value":"Step 4: Decrypt the key when you're ready to use it","id":"step-4-decrypt-the-key-when-youre-ready-to-use-it","depth":3},{"value":"Step 5: Sign a transaction and move the assets","id":"step-5-sign-a-transaction-and-move-the-assets","depth":3},{"value":"Step 6: Clean up","id":"step-6-clean-up","depth":3},{"value":"Test this procedure in sandbox","id":"test-this-procedure-in-sandbox","depth":2},{"value":"Related guides","id":"related-guides","depth":2}],"frontmatter":{"title":"Disaster recovery","seo":{"title":"Disaster recovery"}},"lastModified":"2026-09-29T15:45:00.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/products/wallet/admin-guide/disaster-recovery","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}