Skip to main content

Mnemonic management

This is the page with no undo. Everything else in ChainOS can be re-derived, restored from a backup or rebuilt from the chain. The mnemonic cannot.

There is no recovery path

Ziklag never receives your mnemonic and holds nothing from which it can be reconstructed. There is no reset flow, no support escalation and no key escrow — because any of those would mean Ziklag held something that could reconstruct your keys, which is the property the product exists to deny.

Lose the phrase and the funds derived from it are unreachable. By anyone, permanently.

Treat it as you would an HSM master key, because that is what it is.

Generating

Offline, on a machine you trust, with a tool you trust.

  • 12 or 24 words. Both are accepted. 24 words is 256 bits of entropy against 128 for 12; 128 bits is already beyond brute force, so the practical argument for 24 is margin against a future nobody has predicted rather than a present weakness.
  • Use a real generator. Any BIP-39 implementation from a hardware wallet, a reputable offline tool, or your language's audited library. Do not compose a phrase from words you chose; human-chosen "random" words are guessable at a rate that has emptied real wallets.
  • Do not use a passphrase-derived seed, brain wallet or any scheme where the entropy comes from something memorable.
  • Do not reuse a mnemonic that has ever been typed into a website, an online generator, a note-taking app, or a wallet on a machine that has been compromised.
  • Generate a separate one for sandbox. Cloud actively refuses the same identity in both environments (409 CROSS_ENVIRONMENT_IDENTITY), because sharing means your CI Edge holds the keys to live funds. See Environments.

Storing it for the Edge

The Edge reads the phrase from a file, once, at boot.

# On the host, outside any image build context.
umask 077
printf '%s' "$MNEMONIC" > /etc/chainos/mnemonic
chmod 0400 /etc/chainos/mnemonic
chown 10001 /etc/chainos/mnemonic # the uid the container runs as

Then mount it read-only, and set PASS_PHRASE_FILE to the mount path.

Things not to do, each of which has a specific leak:

Don'tBecause
Pass it as PASS_PHRASEVisible in docker inspect, /proc/<pid>/environ, crash dumps and most orchestrator UIs
Bake it into an imagePresent in every layer, every registry copy, and every machine that pulls the image
Put it in a Compose file you commitIt is in git history forever, including after you delete it
Write it with a trailing newline you did not intendSome tools include it in the phrase; BIP-39 validation then fails, which is at least loud
echo itShell history. Use printf with the value from your secret manager, or a heredoc with a leading space where your shell supports it

Secret managers. Vault, AWS Secrets Manager, Azure Key Vault, Google Secret Manager, Kubernetes Secrets with encryption-at-rest enabled and RBAC narrowed to the workload — any of these is appropriate. The Edge does not integrate with them directly; it reads a file, and your platform's existing secret-to-file mechanism is what you should use, because it is the one your operators already audit.

Backing it up

The backup is the part people get wrong, and they get it wrong in one of two directions: too few copies, or copies somewhere online.

  • At least two copies, geographically separated, both offline. Paper in a safe, or a steel plate, or an encrypted volume on removable media held in different locations.
  • Never in a password manager that syncs, in cloud storage, in email, in a screenshot, or in a photograph — a phone photo is in a cloud backup within minutes.
  • Split it if your risk model calls for it. A 2-of-3 Shamir split (SLIP-39) or a documented split-custody arrangement is reasonable at a bank. What is not reasonable is an undocumented split where one holder leaves and nobody knows the reconstruction procedure.
  • Restore it once, as a test, before you go live. An untested backup is not a backup. Do the restore into a throwaway sandbox Edge and confirm it derives the addresses you expect.

Export the address mapping too

The mnemonic recovers funds. It does not recover the mapping from address to customer — that is yours, and ChainOS is the only place it currently lives.

curl -s "$EDGE/addresses?size=1000" \
-H "X-API-Key: $CHAINOS_EDGE_KEY" \
| jq -r '.data.content[] | [.chain, .address, .derivationPath, .userRef, .tag] | @csv' \
>> chainos-addresses.csv

Run that on a schedule, page through if you have more than a thousand, and keep the result in your own systems. The console also exports CSV per screen.

With the mnemonic and this file you can reconstruct everything without ChainOS: the paths are deterministic and documented in HD derivation.

More than one wallet

If you arrived with wallets that predate ChainOS, your Edge can hold those too. They are called key sources, they are registered with chainos-edge import-mnemonic, and Migration covers the flow. Three operational points belong here rather than there.

They are supplied the same way PASS_PHRASE is. A file your secret management mounts, read at boot. The Edge does not invent a keystore for them and does not write key material anywhere — which is the same rule as for the primary phrase, and the reason there is no second thing to back up.

Mount each one on every replica that should be able to sign with it. A key source registered from one machine is registered for the account; the material is on whichever machines you put it on. A replica without it refuses a withdrawal from those addresses, which is correct and is not the failure you want to discover under load.

The manifest is metadata. chainos-edge import-mnemonic writes key-sources.json into EDGE_DATA_DIR: a label, a fingerprint, the chains, the base path, and the path of the file to read each phrase from. It contains no key material, it is safe to back up, and losing it costs you the registration record on that machine and nothing else — re-running the import rebuilds it, and ChainOS recognises the same wallet by its fingerprint rather than creating a second one.

chainos-edge list-key-sources

shows what this machine knows about, and flags any source whose material is not configured here — the case where the phrase was piped in once and nothing kept it.

Signing with them happens at boot, or not at all. The Edge reads each source's secret when it starts, opens the wallet beside the primary one, and derives the addresses it registered so it knows which of them it holds keys for. A source whose secret is missing on that replica is reported at startup and its addresses are refused there — it is not retried later, because adding a wallet to a running signer would mean the same withdrawal got different answers depending on when it arrived. Mount the secret and restart.

chainos-edge list-key-sources --check

is the same work on demand: it loads every source and reports what this machine can actually sign for, rather than what it was once told about. Run it on a new replica before you send traffic to it. Without --check the command reads the manifest and nothing else, which is why it is the default — a listing should not be a reason to read every recovery phrase on the box.

Signing routes by address, never by what Cloud says. When a transaction arrives, the Edge re-derives the sender from the primary phrase; if it does not match, it looks the address up among the key sources it loaded and signs only if one of them actually derives it. ChainOS knows which wallet owns which address and is never asked — the same principle as chainos-edge prove-control, which searches for a derivation index rather than being handed one. A withdrawal from an address no wallet on the machine derives is refused, and the refusal names the sources whose material is not mounted there, because that is almost always the cause.

Only EVM chains can be signed from a key source today. The Edge carries address codecs for Ethereum, BSC, Polygon, Avalanche and Base and deliberately not for Bitcoin, TRON or XRP — those addresses are derived in Cloud from the extended public key you registered. That is fine for watching and for reporting, and it means the Edge cannot tell whose address a Bitcoin sender is. So if you register a key source covering Bitcoin, TRON or XRP, withdrawals on that chain are refused on this Edge rather than signed with the primary phrase's key, which would produce a valid-looking signature for the wrong wallet. Sweep or withdraw those chains from the wallet that holds them.

Rotating

Prefer not to. Rotating a mnemonic is not like rotating an API key. Addresses derived from the old phrase stay valid on-chain and stay funded; addresses derived from the new one are different. Any deposit sent to an old address after rotation is recoverable only with the old phrase, so "rotation" in practice means "run two mnemonics indefinitely".

ChainOS handles that correctly — addresses derived from prior extended public keys stay monitored forever, so nothing already issued goes dark — but the operational burden of two sets of keys is real and permanent.

Rotate when the old phrase may be compromised. Do not rotate on a schedule.

The procedure

  1. Generate the new mnemonic and back it up, fully, before touching anything.
  2. Sweep or plan to sweep funds from addresses derived from the old phrase into your treasury, using the old phrase. Do this first — after step 4 you are running two Edges.
  3. Tell your customers which deposit addresses are changing, if you re-issue. If you do not re-issue, old addresses keep working and this step is unnecessary.
  4. Start an Edge with the new mnemonic. Cloud sees a different identity key, refuses to derive against it, quarantines the new extended public keys, and pages your account owner. This is the expected behaviour, not a failure.
  5. Approve the rotation in the console, with a fresh TOTP code, as an owner. Read Identity rotation before you do — that page is what explains why the gate is there.
  6. Keep the old phrase. Forever. It is the only key to funds already sitting at old addresses, including deposits that arrive after the rotation.

What must never happen

Do not start an Edge with a different mnemonic while expecting it to serve the same account, in the belief that ChainOS will "just use the new keys". It will not: it will refuse authentication before reading the payload, quarantine the keys, and alert. That refusal is the feature. An implementation that accepted the new phrase silently would derive new addresses for existing customers while their deposits kept arriving at addresses whose keys your new Edge does not hold.

If the Edge cannot read the file

The failure modes, in the order they occur:

SymptomCause
mnemonic file not readableFile mode or ownership. The container's uid must be able to read it
invalid BIP-39 checksumA typo, a wrong word, or a trailing newline included in the phrase
mlock failedThe container lacks IPC_LOCK, or the host's memlock limit is too low. The Edge fails rather than continuing with a swappable seed
Derives the wrong addressesThe phrase is right but a word is transposed. Check against the published test vectors in Installation

Troubleshooting has the diagnostics for each.