morphit/docs/RECOVERING-FROM-WRONG-RELAY-KEY.md

6.6 KiB

Recovering from a wrong relay key (cp167)

This document is the exact procedure for fixing one specific scenario:

An operator ran morphit-ops init, the pre-cp167 wizard asked them for "your relay's posting key", they pasted the posting key (encrypted), and now the relay won't work because the chain rejects every operation with "missing required active authority".

It produces a no-trace recovery: no copy of the wrong key remains on the server when you're done.

Background

The Morphit relay broadcasts the following chain operations on the relay account's behalf:

  • create_claimed_account — signs up a new Morphit user
  • transfer — sends the welcome bonus
  • transfer_to_vesting — powers up donated BLURT
  • delegate_vesting_shares — delegates BP for posting

Every one of those requires active authority. The posting key can sign comments and votes, nothing else. If the relay has a posting key, the chain rejects every relay op.

The cp167 wizard fix renames the prompt + the variables + the internal commentary; any new operator running morphit-ops init will be asked unambiguously for the active key. This document is for operators who set up before that fix landed.

Prerequisites on the server

  1. The operator has the correct active key WIF for the relay account on hand (a 51-character string starting with 5).
  2. They have shell access to the server as the user that runs morphit-ops (the same user that ran the install).
  3. morphit-ops itself is at the cp167 build or later. Check:
    morphit-ops --help | grep edit-active-key
    
    If that returns nothing, pull the latest source and rebuild:
    cd /path/to/morphit
    git pull
    npm install
    npm run build -w apps/ops-cli
    

The procedure

Run from the same directory that contains morphit.env (the directory you ran morphit-ops init in):

cd /path/to/morphit
morphit-ops edit-active-key

The command will:

  1. Read morphit.env, locate the relay account name and the keystore path. It prints both back so you can sanity-check them.
  2. Read the current keystore, detect that it's encrypted, and print the storage mode.
  3. Ask: "Was the previous key wrong or compromised?" Answer y (yes) — this selects the no-trace rotation path.
  4. Ask you to confirm: "Rotate the active key for @?" Answer y (yes).
  5. Prompt for the new active key — paste the 51-character WIF starting with 5. (It echoes nothing; that's intentional.)
  6. Ask for the storage mode for the new key — choose encrypted (option 1, the default — same as before).
  7. Ask for a new passphrase. You can reuse the prior passphrase or choose a fresh one. Type it twice.
  8. The command then:
    • Overwrites the prior keystore file (the encrypted posting-key blob) with random bytes, then zeros, then fsyncs, then unlinks it. No .bak file is created.
    • Atomically writes the new keystore (encrypted active-key envelope) into the same path with 0600 permissions.
    • If the storage mode changed (it shouldn't, both are encrypted), updates MORPHIT_RELAY_ACTIVE_KEY_FILE in morphit.env.

Verifying after

When the command finishes, the relay account on this server now has:

  • apps/relay/keystore.json — the new active-key envelope.
  • No .bak-* files anywhere in apps/relay/. Verify with:
    ls -la apps/relay/keystore*
    
    You should see exactly one file: keystore.json.

Then restart the relay:

sudo systemctl restart morphit-relay.service

The relay's startup unlock step:

  1. Prompts for the passphrase (whatever you typed in step 7 above).
  2. Decrypts the envelope to get the active-key WIF.
  3. Derives the public key from that WIF.
  4. Fetches the current active authority of @<account> from chain.
  5. Refuses to start if the derived pubkey doesn't appear in the on-chain active authority's key_auths array.

That last check is the hard wall. If you somehow pasted the wrong key again (an active key for a different account, or a posting key by mistake), the relay's startup logs will say something like:

relay: active pubkey BLT... does not appear in @<account>'s
active authority on chain.  Refusing to start.

If you see that, re-run morphit-ops edit-active-key with the correct key. The prior bad key was already wiped — there's no rollback path. Just paste the right key this time.

What this CAN'T promise

The wipe is a best-effort secure delete at the filesystem level. It overwrites the file's allocated bytes twice (random, then zeros), fsyncs, then unlinks. On a stock ext4 or xfs without snapshots or LVM thin provisioning, that means the prior ciphertext is no longer readable via the file's old path and the inode is freed. The blocks themselves are returned to the free pool.

But: if the server uses btrfs, ZFS, or APFS, or if LVM thin pools have snapshots, or if there is full-disk SSD wear-leveling with unallocated reserve, the prior bytes may still exist physically on the device. The only defense against that class of recovery is full disk encryption — which is a baseline you should have anyway for any server holding crypto keys.

For the scenario this document covers — a posting key encrypted by a known passphrase, which is also the wrong type of key to be useful for any attack on the relay account — the practical risk after the wipe is effectively zero.

What about just re-running morphit-ops init instead?

That works but it's heavier:

  • It re-prompts for all 20 wizard steps (instance name, tagline, DB URL, fees account, daily ceiling, contact, origin, alt-networks, fee explorers, chat-link explorers, disabled assets, listing fee, SEO, backup, operator tag, Matrix, RPC endpoints, MCP server).
  • It rewrites morphit.env, morphit.config.env, and the keystore.
  • The old keystore file is replaced by atomic rename — same no-trace property as edit-active-key --wipe-prior.

The downside is that any post-launch customizations you made via morphit-ops edit (origin override, alt-network addresses, SEO copy, etc.) get overwritten by the wizard's interactive prompts unless you re-enter them carefully. edit-active-key is the surgical alternative.

Quick reference

Non-interactive equivalent (CI/scripted):

morphit-ops edit-active-key --wipe-prior

This skips the interactive "was the previous key compromised?" prompt but still asks for the new key, the storage mode, and the new passphrase. (Those are required and have no safe default.)

To force the safe path (keep .bak even for a wrong-key scenario):

morphit-ops edit-active-key --keep-backup

Mutually exclusive with --wipe-prior.