Skip to content

This tutorial describes how to rotate the notary signing and messaging keys by migrating from your current notary service (the old notary) to a replacement notary service (the new notary). After following these steps, the new notary operates as the active notary service with new collections, messaging, and API keys, while keeping the chain of trust with the data that the old notary created.

The old notary must remain operational

This procedure expects the old notary to be fully functional with its existing KMS backend throughout the migration. If you rotate keys on the HSM as part of this work, make sure the rotation does not overwrite the HSM master key that the old notary currently uses to operate.

When to do notary key rotation

Rotate the notary keys, or migrate to a new notary service, when you need to:

  • Satisfy compliance and regulatory requirements, such as the Digital Operational Resilience Act (DORA).
  • Comply with cybersecurity policies within your institution.
  • Migrate from one HSM to another, or from an on-premise notary to a Ripple-hosted managed notary service.

The procedure uses a chain-of-trust process: you configure the new notary so that it recognizes the public key of the old notary, which lets it read the Merkle-tree data that the old notary created.

Prerequisites

  • The state review authority public key is registered with Ripple Custody. The final migration step recovers the anti-rewind file (ARF) — see Recover your anti-rewind file — which requires a manifest signed by the state review authority. If this key is not yet registered, register it first — see Register the state review authority public key.
  • Operator access to both notary deployments. You must be able to set environment variables for the old notary and the new notary, start and stop each service, and read the new notary's log files.
  • A Ripple Custody API user who can propose, approve, and sign intents, and who can read system properties. See Manage intents and approvals.
  • A current backup of the Ripple Custody database. Take the backup before you begin. The recovery-manifest step runs against a copy of the database, and the backup lets you restore the system state if the migration fails. Follow PostgreSQL backup best practices, and enable point-in-time recovery (PITR) so that you can restore the database to a moment just before the migration.
Disruption notice

Steps 1–3 are non-disruptive: the old notary remains active and continues to sign intents. From Step 4 onward, the old notary stops processing intents, and the new notary does not process intents until it completes recovery in Step 6 — the system runs without a notary until that point. Wait until any in-progress intents have executed successfully, then do Steps 4–7 in quick succession.

Rotate the notary keys

The following sections describe the steps to migrate from the old notary to the new notary.

Step 1: Retrieve the old notary collections public key

Use the Ripple Custody API to retrieve the old notary collections public key from the system properties, so that you can register this key with the new notary:

  1. Call the List system properties operation, with query parameter id=NOTARY_COLLECTIONS_KEY.

  2. Copy the items[0].data.value.publicKey.value.

The response body is similar to the following:

{
    "items": [
        {
            "data": {
                "id": "NOTARY_COLLECTIONS_KEY",
                "value": {
                    "publicKey": {
                        "value": "<OLD_NOTARY_COLLECTIONS_PUBLIC_KEY>",
                        "type": "PublicKey"
                    }
                },
                "metadata": {
                    "description": null,
                    "revision": 1,
                    "createdAt": "2025-07-14T12:23:44.653Z",
                    "createdBy": {
                        "id": "f0ca2fb4-369f-4617-aaa3-6a2f3370e931",
                        "domainId": "5cd224fe-193e-8bce-c94c-c6c05245e2d1"
                    },
                    "lastModifiedAt": "2025-07-14T12:23:44.653Z",
                    "lastModifiedBy": {
                        "id": "f0ca2fb4-369f-4617-aaa3-6a2f3370e931",
                        "domainId": "5cd224fe-193e-8bce-c94c-c6c05245e2d1"
                    },
                    "customProperties": {}
                },
                "type": "NotaryCollectionsKeySystemProperty",
                "access": "ReadOnly"
            },
            "signature": "<SIGNATURE>",
            "signingKey": "<SIGNINGKEY>"
        }
    ],
    "count": 1
}

Step 2: Configure the new notary and launch it in standby mode

Register the old notary key with the new notary, and launch the new notary in standby mode. This generates all the new public keys — for the collections, messaging, and API calls — that you need in the next step.

In standby mode, the new notary starts without taking over as the active notary. It reads the old notary collections public key to establish the chain of trust, generates its new key material, and writes that material to its logs — it does not process intents. The old notary remains the active notary until the final migration step.

You control both behaviors through two environment variables in the new notary's configuration:

VariableValue
MIGRATION_PREVIOUS_NOTARY_COLLECTIONS_PUBLIC_KEYThe value of the NOTARY_COLLECTIONS_KEY system property that you retrieved in Step 1. This is how you insert the old key into the new notary.
MIGRATION_STANDBY_MODE"true" — starts the notary in standby mode.
Keep the new notary disconnected from the notary bridge

Make sure the new notary is not connected to the notary bridge while it runs in standby mode. If the notary uses HTTP mode, make sure HMZ_NOTARY_BRIDGE_HTTP_URI does not point to the bridge.

To launch in standby mode:

  1. Set the two environment variables in the new notary's configuration, and start the service:

    MIGRATION_PREVIOUS_NOTARY_COLLECTIONS_PUBLIC_KEY: "<OLD_NOTARY_COLLECTIONS_PUBLIC_KEY>"
    MIGRATION_STANDBY_MODE: "true"

    Replace <OLD_NOTARY_COLLECTIONS_PUBLIC_KEY> with the value that you retrieved in Step 1.

  2. Check the log files for the new notary. The logs contain the following six items, labeled as follows:

    • Migration key material generated
    • New trusted collections public key
    • New messaging public key
    • New API public key
    • Previous notary public key
    • Previous notary public key signature

    Record all six values. You need them for the key-migration intent in the next step. The log labels differ from the intent payload field names, so use the mapping table in Step 3 when building the payload.

    At this point, the new notary is not running — standby mode generates the key material, writes it to the logs, and stops. The old notary remains the active notary.

If the new notary reports that it cannot create the new keys file, see Troubleshooting.

Step 3: Create the key-migration intent

Submit an intent to execute the key migration, using the information that the new notary generated. When you approve and sign this intent, it registers both the old and new public keys with Ripple Custody in preparation for bringing up the new notary.

  1. In the Custody API, call the Propose an intent operation with the v0_AddTrustedPublicKeysForMigration intent. (For more information about requesting intents, see Manage intents and approvals.)

    The intent payload has the following structure:

    {
        "newCollectionSigningPublicKey":             "",
        "newMessageSigningPublicKey":                "",
        "newApiSigningPublicKey":                    "",
        "newKeyMaterial":                            "",
        "currentCollectionSigningPublicKey":         "",
        "currentCollectionSigningPublicKeySignature": "",
        "type": "v0_AddTrustedPublicKeysForMigration"
    }
  2. Copy the six values from the new notary logs into the payload fields. The payload field names differ from the labels the notary writes to its logs, so use this mapping:

    Payload fieldNotary log label
    newCollectionSigningPublicKeyNew trusted collections public key
    newMessageSigningPublicKeyNew messaging public key
    newApiSigningPublicKeyNew API public key
    newKeyMaterialMigration key material generated
    currentCollectionSigningPublicKeyPrevious notary public key
    currentCollectionSigningPublicKeySignaturePrevious notary public key signature
  3. Approve and sign the intent.

To verify that the keys are registered, call the following API operations:

Note

At this point the old notary is still active, and continues to process intents using the current (old) public keys, until you are ready to migrate to the new notary.

Step 4: Scale down the old notary

From this step, the old notary stops processing intents, and the new notary does not process intents until it completes recovery in Step 6 — the system runs without a notary until that point.

  1. Wait until any in-progress intents have executed successfully.

  2. Scale down the old notary instance.

Step 5: Generate the recovery manifest for the anti-rewind file

The new notary needs the anti-rewind file (ARF) to load the current Ripple Custody system state before it can sign intents. To rebuild the ARF, generate a signed manifest of the system state. (For more information about the ARF, see Anti-rewind file.)

This step requires the state review authority key described in Prerequisites: the state review authority must sign the manifest.

Follow the disaster recovery procedure described in Recover your anti-rewind file to generate the signed manifest.

Before you continue, confirm that both the manifest JSON file and the signature file are present.

Step 6: Launch the new notary in recovery mode

Reconfigure the new notary so that it starts in recovery mode instead of standby mode:

  1. Remove all of the MIGRATION_* environment variables that you set for standby mode:

    • MIGRATION_PREVIOUS_NOTARY_COLLECTIONS_PUBLIC_KEY
    • MIGRATION_STANDBY_MODE
    • MIGRATION_KEY_MATERIAL_DIRECTORY, if you set it
  2. Set the recovery environment variables with the manifest and signature that you generated in Step 5:

    VariableValue
    ANTI_REWIND_RECOVERY_STATE_MANIFESTThe manifest, as a serialized JSON string.
    ANTI_REWIND_RECOVERY_STATE_MANIFEST_SIGNATUREThe manifest signature, encoded in base64.
  3. Start the new notary. The service runs in anti-rewind-file recovery mode and rebuilds the ARF. While in recovery mode, the notary does not serve requests, so no intents can be executed.

  4. Wait until the new notary logs show the following message, which confirms that the recovery is complete:

    Recovery completed, remember to unset ANTI_REWIND_RECOVERY_* env variables before running it again

    Wait for recovery to complete

    Do not continue to the next step until this message appears.

  5. To verify, call any of the following API operations, and check the publicKey value and that the revision number is updated:

Step 7: Remove the recovery environment variables

After the recovery completes, remove the recovery environment variables and restart the new notary one more time:

  1. Remove ANTI_REWIND_RECOVERY_STATE_MANIFEST and ANTI_REWIND_RECOVERY_STATE_MANIFEST_SIGNATURE.

  2. Restart the new notary service.

The new notary is now out of recovery mode and operates as the active notary service.

Verification

Before you return the system to normal operations, verify that the new notary processes governed operations end to end:

  1. Check the new notary service logs for a normal startup, and confirm that no MIGRATION_* or ANTI_REWIND_RECOVERY_* environment variables remain in its configuration.
  2. Call the List system properties operation with query parameter id=NOTARY_COLLECTIONS_KEY, as in Step 1, and confirm that the returned public key is now the new notary collections public key, with an updated revision.
  3. Submit a test intent, approve and sign it, and verify that it reaches an executed state. Any governed intent confirms that the new notary attests intents end to end — for example, create a user, create an account (a v0_CreateAccount intent — see Manage accounts with the API), or send a transaction.
  4. Send a low-value test transaction from an existing account, and verify that it completes signing and broadcasts. See Send assets with the API.
  5. Confirm that intents submitted before the migration window, if any remain, complete or fail cleanly rather than staying stuck in a pending state.

If any check fails, stop and contact your Customer Partner Engineer (CPE) or the Technical Services team before processing production operations.

Troubleshooting

  • The new notary reports that it cannot create the new keys file: Set a third environment variable, MIGRATION_KEY_MATERIAL_DIRECTORY: /tmp, in the new notary's configuration and restart the service. Remove this variable, together with the other MIGRATION_* variables, when you launch recovery mode in Step 6.

  • The new notary does not write the six key items to its logs: Make sure MIGRATION_STANDBY_MODE is set to "true" and that MIGRATION_PREVIOUS_NOTARY_COLLECTIONS_PUBLIC_KEY contains the value from Step 1. Also make sure the new notary is not connected to the notary bridge — in HTTP mode, HMZ_NOTARY_BRIDGE_HTTP_URI must not point to the bridge.

  • The key-migration intent is rejected or the values don't match: The notary log labels differ from the intent payload field names. Check each value against the mapping table in Step 3.

  • The public keys or revision numbers don't update after recovery: Confirm that the key-migration intent from Step 3 was approved and signed before you scaled down the old notary, and that you removed all MIGRATION_* variables before starting recovery mode.