This procedure walks you through recovering a blockchain key and producing a raw signature from your independently held vault backup, using the rkit-cli tool. For the concepts, scope, and preparation behind it, read the Self-custody exit overview first.
This page is for whoever runs the recovery. It assumes comfort with a command line and, for hardware-backed providers, basic PKCS#11 and HSM concepts.
This procedure doesn't apply to MPC vaults. For MPC key management, see MPC backups and exit strategy.
This procedure requires Ripple Custody version 1.29 or later (released December 2025). The self-service HSM backup API in Step 1 didn't exist before that release. On an earlier version, this procedure doesn't apply yet: contact Ripple Support or your Customer Partner Engineer (CPE) to arrange a backup.
Also confirm that:
- You run the backup create and acknowledge operations from the root domain.
- You have the
rkit-clipackage that matches your HSM provider (see Step 3), ideally already installed from before the disaster. - For hardware HSM providers, you have network connectivity from where you run
rkit-clito your HSM cluster.
The Ripple Custody vault-backup lifecycle produces a signed trusted-entity envelope that contains your vault's wrapped seed material. The lifecycle has five stages.
Submit a v0_CreateBackup intent for the vault you need to recover, with backup type VaultHsm, from the root domain. Wait until Ripple Custody accepts and executes the intent.
Poll the backup's status with the Get Backup information or List Backups API operations until processing is complete.
Call Get Backup trusted entity details to retrieve the envelope. It contains four fields: encryptedSeed (the wrapped seed material itself), hash, signingKey, and signature.
You can call Get Backup trusted entity details successfully only before you acknowledge the backup (stage 5 below). Once you acknowledge a backup, the server permanently denies access to this envelope. There's no way to fetch it again. Save the full response securely and durably (see Step 6) before you acknowledge.
Before you trust anything in the envelope, verify its signature against Ripple's notary public key. Don't derive keys from an envelope whose signature you haven't verified.
Submit a v0_AcknowledgeBackup intent to confirm that you have taken custody of the backup.
Acknowledge only after you have durably and securely saved the envelope from stage 3. Acknowledging is the action that permanently closes off future access to it through the API.
To script this lifecycle instead of driving it by hand, use the ripple-custody-client-lib-python SDK. Its vaults.backups.create(), .wait_completed(), .get_trusted_entity(), .verify_trusted_entity(), and .acknowledge() methods implement these five stages directly.
rkit-cli derives and signs at a specific derivation path that you provide. It has no knowledge of which Ripple Custody account that path belongs to. There are two ways to find the right path for the account you're recovering.
Read the account's derivation path directly from the API. Call GET /domains/{domainId}/accounts/{accountId}. The path is at data.providerDetails.keys[*].derivationPath, with one entry per key ID (for example, SECP256K1_CUSTODY_1).
You can reconstruct any account's derivation path from the Ripple Custody numbering scheme, without any API access:
- Ripple Custody numbers every domain and account sequentially in creation order, starting at 1. Domain numbering is a single flat, global sequence. The UI lets you organize domains hierarchically (a domain can contain subdomains), but that nesting is organizational only and doesn't appear in the derivation path. Ripple Custody uses a subdomain's own index directly at the top level. For example, subdomain 4 uses
m/4'/..., neverm/<parent>'/4'/.... A domain has no derivation path of its own; only its accounts do. - Given domain index
dand account indexa, an account can have up to four keys:SECP256K1_CUSTODY_1atm/d'/a'(Bitcoin, Ethereum, Tron, XRPL);ED25519_CUSTODY_1atm/d'/a'(most Ed25519 chains);ED25519_CUSTODY_2atm/d'/a'/0'(Cardano spending); andED25519_STAKING_1atm/d'/a'/1'(Cardano staking). - UTXO-based chains (for example, Bitcoin) append
/change/address_indexto the account path.changeis0for a deposit (external) address or1for a change (internal) address.address_indexincrements each time Ripple Custody generates a new address.
| Domain | Account | Derivation path |
|---|---|---|
| Domain 1 | Account 1 | m/1'/1' |
| Domain 1 | Account 2 | m/1'/2' |
| Domain 2 | Account 1 | m/2'/1' |
Ripple shares the rkit-cli package for your HSM provider with you through SendSafely, typically as part of onboarding, well ahead of any disaster. Store this package on secure, durable storage alongside your other recovery materials.
Install the package for your platform:
# Debian/Ubuntu
sudo dpkg -i rkit-cli-<provider>_<version>_<arch>.deb
# RHEL/CentOS/Fedora
sudo rpm -i rkit-cli-<provider>-<version>.<arch>.rpmrkit-cli derive --provider <provider> --pin-file <pin-file> --partition <partition> \
--seed <seed-file> --path "<derivation-path>" --blockchain <chain>This prints the derivation path (echoed back), the account's public key (in several encodings), its chain code, and its blockchain address.
Before you use this key to sign anything, compare the printed public key and address against the value Ripple Custody shows for this account (from the application, or from the API response in Step 2). An exact match confirms that you have the right seed file, the right derivation path, and the right provider configuration before you commit to signing.
rkit-cli sign --provider <provider> --pin-file <pin-file> --partition <partition> \
--seed <seed-file> --path "<derivation-path>" --blockchain <chain> \
--hex <payload-hex>The --blockchain flag automatically selects the correct curve, pre-hash, and address format for that chain. For the full table, see the chain signature reference. You can also supply the payload from a file with --file instead of --hex.
The output includes the raw signature, low-S normalized where the chain requires it, and a DER-encoded form where applicable.
- Prefer
--pin-fileover--pin. The PIN then never appears inpsoutput or your shell history. - Treat the seed envelope and any PIN file as sensitive material for as long as they exist on disk. Use access-controlled, ideally encrypted, storage only, never an unencrypted shared drive.
- After you confirm that the recovery succeeded, securely delete (shred, don't just remove) the seed envelope file and any PIN file.
Steps 1, 2, 4, 5, and 6 are identical regardless of provider. The following sections cover only what differs: the authentication flags, how rkit-cli reaches your HSM, and a worked example. Use the section that matches the HSM provider your organization deployed. For the complete flag list, see the rkit-cli quick reference.
-p/--pinor--pin-file, and-n/--partition <serial-or-label>(required).--lib-pathoverrides the dlopen path (it defaults to the vendor's documented install location).--debug-pkcs11loads the vendor's logging library variant, which is useful when you diagnose a connection problem with Thales support.
Luna requires a working NTLS connection to your Luna HSM or HA group: a Chrystoki.conf (client certificate and key, CA certificate, and HSM server host and port) and the Luna Minimal Client PKCS#11 library installed. rkit-cli only consumes an already-configured library. To set these up, see Thales's own client installation and network documentation.
rkit-cli derive --provider luna --pin-file luna.pin --partition partition-2 \
--seed blobfile.sim --path "m/2'/1'/0/0" --blockchain bitcoin
rkit-cli sign --provider luna --pin-file luna.pin --partition partition-2 \
--seed blobfile.sim --path "m/2'/1'/0/0" --blockchain bitcoin \
--hex <sighash-hex>-p/--pinor--pin-file, and-n/--partition.--lib-pathdefaults to/usr/local/primus/lib/libprimusP11.so, or set thePKCS11_LIBRARY_PATHenvironment variable.- The seed file (
--seed) is theencryptedSeedenvelope from Step 1, passed as-is. It's a per-curve wrapped master key.
Primus requires the Securosys Primus PKCS#11 client library installed and network connectivity to your Primus HSM cluster. See Securosys's own client installation documentation.
rkit-cli derive --provider primus --pin-file primus.pin --partition primus-1 \
--seed primus_seed.bin --path "m/1'/1'" --blockchain ethereum \
--lib-path /opt/primus/lib/libprimusP11.so-p/--pinor--pin-file.-n/--partitionis a numeric slot ID here, not a label.--lib-pathcarries the CXI device address (for example,3001@my.hsm.hostor/dev/cs2), not a filesystem path, despite the flag name.
Utimaco requires the Utimaco CryptoServer PKCS#11 (libcs_pkcs11_R2.so) and CXI (libcxi.so) client libraries installed and reachable. See Utimaco's own client installation and network documentation.
rkit-cli derive --provider utimaco --pin-file utimaco.pin --partition 1 \
--seed utimaco_seed.bin --path "m/2'/1'/0/0" --blockchain bitcoin \
--lib-path 3001@my.hsm.host- There's no local PKCS#11 library.
rkit-cliconnects to a remote gRPC endpoint instead. --endpoint <host[:port]>(default port 443).--token/--token-file(a bearer token) or--api-key/--api-key-file(an IAM API key, exchanged for a bearer token). Pass exactly one of the two.--iam-endpoint,--instance, and--ca-fileare optional, for non-default IAM endpoints, Bluemix instance metadata, or a custom CA bundle.
IBM Cloud HPCS EP11 requires outbound network access from wherever you run rkit-cli to your IBM Cloud HPCS EP11 endpoint, and to IBM's IAM token endpoint if you use an API key. There's no local vendor library to install.
rkit-cli derive --provider ibm --endpoint hpcs.example.ibm.com:443 \
--token-file ibm.token --seed ibm_seed.bin \
--path "m/2'/1'/0/0" --blockchain bitcoinrkit-cli reports a specific, plain-language reason for every failure it can anticipate. The following table lists the most common messages.
| Message or symptom | What it means | What to do |
|---|---|---|
MissingPin | You supplied neither --pin nor --pin-file. | Supply one. Prefer --pin-file. |
MissingPartition | You gave no --partition for a provider that requires one (always for Luna and Utimaco; optional for Primus). | Pass -n/--partition with your HSM partition's serial number, label, or (Utimaco) numeric slot ID. |
...failed. PKCS#11 error: CKR_PIN_INCORRECT | The PIN doesn't match the PIN on the targeted HSM partition. | Double-check the PIN value or file, and confirm that you're targeting the correct partition. |
no slot matches --partition 'X' (searched N slot(s)) / TokenNotPresent / NoMatchingSlot | The partition name, label, or serial number you gave doesn't match any slot the HSM client can currently see. | Confirm the exact label or serial with your HSM administrator, and confirm network connectivity to the HSM. |
failed to open provider '...': FileNotFound (while loading the library) | rkit-cli couldn't find or load the vendor PKCS#11 library. | Check --lib-path, and confirm that you installed the vendor's client software. |
symbol '...' not found in ... library (SymbolNotFound) | rkit-cli loaded the wrong version of the vendor library, or an otherwise incompatible one. | Confirm that you have the vendor library version rkit-cli expects. |
failed to read file '...', err: FileNotFound (seed file) | The path you gave to --seed doesn't exist. | Confirm the path to your saved seed envelope. |
failed to load seed: EmptyEnvelope / InvalidEnvelopeBase64 / InvalidEnvelopeJson / InvalidProtobuf / UnknownCurveInEnvelope / MissingSecretMaterial | The seed file is empty, corrupted, truncated, or doesn't contain material for the curve you requested. | Re-fetch or re-copy the envelope, confirm that you saved the entire response from Get Backup trusted entity details exactly as returned, and confirm that --curve/--blockchain matches a curve present in the envelope. |
--pin and --pin-file are mutually exclusive; pass exactly one | You passed both flags at once. | Pass only one. Prefer --pin-file. |
--path is required | You omitted the --path/-d flag. | Supply -d/--path with the derivation path (see Step 2). |
failed to parse HD path '...', err: InvalidCharacter / Overflow | The derivation path string is malformed. | Check the path format, for example m/1'/1' (see Step 2). |
| Flag | Purpose |
|---|---|
--provider | The HSM provider: luna, primus, utimaco, or ibm. |
-p/--pin or --pin-file | The HSM PIN, inline or from a file. Prefer --pin-file so the PIN never appears in ps output or shell history. Pass exactly one. |
-n/--partition | The HSM partition. A serial number or label for Luna and Primus; a numeric slot ID for Utimaco. Required for Luna and Utimaco; optional for Primus. |
--seed | The path to your saved encryptedSeed envelope. |
-d/--path | The derivation path, for example m/1'/1'. |
--blockchain | The target chain. Selects the curve, pre-hash, and address format automatically (see the chain signature reference). |
--curve | Expert mode: secp256k1 or ed25519. Signs the same raw payload under every pre-hash rkit-cli knows for that curve, which is useful for confirming exactly which hash a given network expects. |
--hex / --file | The payload to sign, inline as hex or from a file. |
--lib-path | The vendor PKCS#11 library location. Its meaning varies by provider (see Connect to your HSM provider). |
rkit-cli consumes each HSM vendor's own configuration files and client libraries. It doesn't define their format. For the exact file format and available settings, see the official vendor documentation.
| Provider | Configuration files | Contents |
|---|---|---|
| Thales Luna | Chrystoki.conf | Client-wide PKCS#11 configuration: NTLS server, HA group, and logging settings. |
| Securosys Primus | primus.cfg / .secrets.cfg | primus.cfg holds connectivity and partition settings; .secrets.cfg holds the ciphered permanent secret that authenticates to the HSM. |
| Utimaco CryptoServer | cs_pkcs11_R2.cfg | PKCS#11 R2 module configuration: device address, timeouts, and logging. |
| IBM Cloud HPCS EP11 | None | You configure the connection entirely through CLI flags (--endpoint, --token, --api-key). |
--blockchain <name> automatically selects the signature scheme, elliptic curve, and pre-hash applied before signing for every chain rkit-cli supports.
| Chain | Scheme | Curve | Pre-hash before signing |
|---|---|---|---|
| Bitcoin | ECDSA | secp256k1 | double-SHA-256 (sighash) |
| Ethereum | ECDSA | secp256k1 | Keccak-256 over typed-tx + RLP |
| Tron | ECDSA | secp256k1 | SHA-256 over protobuf raw_data |
| XRPL | ECDSA | secp256k1 | SHA-512Half (SHA-512 truncated to 256 bits) |
| Algorand | EdDSA | Ed25519 | Pure Ed25519 over `"TX" |
| Cardano | EdDSA | Ed25519 | Blake2b-256 over the CBOR tx body |
| Diem | EdDSA | Ed25519 | SHA3-256 domain-separator prefix |
| Hedera | EdDSA | Ed25519 | Pure Ed25519 over TransactionBody |
| Polkadot / Substrate | EdDSA | Ed25519 | Blake2b-256 when the SCALE payload is larger than 256 bytes |
| Solana | EdDSA | Ed25519 | Pure Ed25519 over the message bytes |
| Stellar | EdDSA | Ed25519 | SHA-256 over the signature payload |
| Tezos (tz1) | EdDSA | Ed25519 | Blake2b-256 over `0x03 |
Pure Ed25519 chains feed the serialized message into Ed25519 directly. Ed25519 runs SHA-512 internally as part of the signature, but there's no application-level pre-hash step beyond that.
- Self-custody exit overview: concepts, scope, and preparation.
- Back up, restore, and recover: all Ripple Custody backup and recovery procedures.