The account signature
The single wallet signature every Umbra key is derived from, why signing again recovers the whole account, and the ways it can go wrong.
Umbra's clients, not the protocol
Deriving every key from one wallet signature is how Umbra's applications and SDK do it, so that a wallet alone can recover an account. Nothing on chain or in a proof checks it, and another client could generate keys at random or derive them some other way.
Your whole Umbra account comes from one signature: your main wallet signs a fixed Umbra message, and every key, the platform wallet and the encryption keypair are derived from the 64 bytes it returns. The same wallet signing the same message gives the same account on any device, on the same chain and deployment, which is the recovery mechanism and the main hazard at once.
- Account contextHKDF-SHA512, salt umbra/nimbus-solana/v0
- Wallet seedlabelled HKDF to a 24-word phrase under the platform-wallet domain, then its BIP-39 seed
- Platform walletSLIP-0010 at m/44'/501'/0'/0'
- Identity master secretlabelled HKDF, bound to the chain, the deployment and the platform wallet
- Spending, nullifier, outgoing viewing and proof-authorisation keys
- Eight slot blinders
- Sharing polynomialsfor the registration split
- ML-KEM-768 keypairFIPS 203 key generation from a seed
- Journal keyencrypts the local note store
The message
The message begins "Umbra Privacy: Signature v0" and asks the user to review the access it enables. It warns that the signature derives spending and viewing keys, explains local encrypted caching and the limits of clearing cached material, and authorizes operations permitted by the Terms, including restricted automation and applicable fees without a separate wallet confirmation. Signing the message itself does not submit a transaction. The notice includes non-custodial and third-party application risks, acceptance of the Terms, acknowledgement of the Privacy Policy, and their update procedures.
The exact notice uses UTF-8, LF line endings, no byte-order mark, and no leading or trailing whitespace. The SDK normalizes CRLF or CR to LF and applies trim() before hashing and encoding. Internal line breaks and spaces are preserved. Its 2,525 content bytes hash to:
1cfd6a3d865fd2da81c9f9c5675db58a89cd129e85151196cdd46beab890c24bBefore signing or restoring a signature, the SDK checks this SHA-256 pin and refuses a mismatch with message-altered. The wallet signs only the 2,525 readable notice bytes, with no binary preamble. This keeps ordinary message-signing prompts readable. The SHA-256 above is also the hash of the exact bytes signed.
The wallet adapter must sign these UTF-8 bytes verbatim. The SDK verifies the returned Ed25519 signature over the notice and wallet public key before HKDF. Signatures over modified or binary-framed messages, and signatures from another wallet, are rejected. Both creation and signature restoration require sha256, keypair and hkdf dependencies; creation additionally requires the connected wallet. Use HASH_TO_CURVE.sha256 from /primitives and KEYPAIR from /keypair for the standard bindings.
There is no nonce or timestamp, so signing again reproduces the same account. Account signing uses ordinary signMessage; wallets that cannot sign these exact bytes are unsupported for this flow. Hardware-wallet compatibility is not guaranteed. The platform-keypair SDK still supports Anza off-chain v1 for other operations, but account derivation does not use it or select signing formats by wallet.
From signature to keys
The SDK accepts only a 64-byte Ed25519 signature and refuses anything else, because a signature scheme that is not deterministic by specification would derive a different account next time.
- Account context. The signature is not uniformly random (half of it is a curve point), so it is first run through HKDF-SHA512: extract with the salt
umbra/nimbus-solana/v0, then expand to 64 bytes. The salt is also the scheme's version marker. - Wallet seed. The context becomes 32 bytes of entropy through HKDF-SHA512 in RFC 9180's labelled form, under a domain that names the chain (such as
solana:mainnet), the deployment (such asmainnetordevnet-test), the applicationcom.umbraprivacy.nimbus-solana, the purposeplatform-walletand a generation. Thirty-two bytes of entropy are exactly a 24-word BIP-39 phrase, and that phrase's BIP-39 seed, PBKDF2-HMAC-SHA512 at 2048 rounds, is the root of everything below. The SDK derives the seed once per session and holds it in memory. - Platform wallet. The seed goes through a SLIP-0010 ed25519 derivation along Solana's path
m/44'/501'/0'/0'. See the platform wallet. - Identity master secret. The seed goes through HKDF-SHA512, again in labelled form, under a context that names the chain, the deployment, a generation and the platform wallet's public key, to a 64-byte master secret. Every identity key and blinder is an HKDF expansion of the master secret under its own label, such as
key/spending, reduced to a non-zero BN254 field element. The ML-KEM-768 keypair comes from a 64-byte expansion fed to FIPS 203 key generation, and the two polynomials that split your outgoing viewing key at registration come from expansions under labels of their own. See your keys. - Journal key. The key that encrypts your device's note store is drawn from the seed under its own purpose,
note-journal.
Because each step is keyed by its own label, holding one derived value does not reveal any other. Because the chain, the deployment and the platform wallet all enter the derivation, the same signature gives an unrelated account on every chain and deployment.
Launch derivation versions
The account derivation namespace starts at v0 for the first launch. Every downstream key-derivation protocol already uses v0; platform-wallet and identity generations are both zero. The signature notice, extraction salt and expansion label must remain frozen after release. Changing a legal page does not require editing the signed notice.
| Derivation layer | Launch namespace or generation |
|---|---|
| Readable signature notice | Umbra Privacy: Signature v0 |
| Account context | umbra/nimbus-solana/v0 |
| Platform-wallet seed and journal key | umbra-platform-keypair-solana-v0, generation 0 |
| Identity master secret and its keys | umbra-nimbus-identity-v0, generation 0 |
| Registration sharing polynomials | umbra-nimbus-solana-sharing-v0 |
| Key Words encoding | umbra-key-words-v0 |
Chain, deployment, application, purpose, wallet account and identity wallet bindings continue separating derived values. Transaction versions, memo and note formats, guardian service domains, browser-session encryption namespaces and storage schema versions describe other protocols. They retain their existing versions; renaming them would require coordinated changes in their consumers.
Recovery is signing again
Nothing needs to be backed up beyond access to your main wallet. On a new device, connect the same wallet to the same deployment, sign the same message and the device re-derives everything, then rescans the note archive for your notes. See recovery.
The platform wallet's derivation passes through a standard 24-word phrase, but the Solana SDK does not show it, and offers no separate recovery phrase for the account. The main wallet is the backup.
Hazards
- Wallets that alter the message are refused. The SDK verifies the signature against the exact ASCII notice and signing wallet before deriving key material. An adapter that alters the notice or adds a binary envelope receives
signature-invalid. - A different key is a different account. Importing your wallet's seed into software that derives a different key, or signing with another wallet, gives another account.
- A different chain or deployment is a different account. Both enter the derivation, so an application pointed at another deployment derives an unrelated account from the same signature, with no notes in it.
- Different signing bytes or derivation versions produce different accounts. The launch "Signature v0" notice uses the salt
umbra/nimbus-solana/v0. This is a frozen production scheme, not a disposable development version. Earlier development flows used different text, framing, or salts (includingv2andv3). Their accounts require the original signing bytes and original derivation inputs for recovery. Moving to the launch flow requires moving funds from any earlier account; clearing cached signatures does not migrate it. - A leak is permanent. Keys cannot be rotated in place. If the signature leaks, the only remedy is to move your funds to an account derived from a different wallet, and the old account's history stays readable to whoever holds it.
Related
Last updated
Everything you own
The objects that make up an Umbra account, where each one lives, who can see it and how it comes back, and what Umbra's infrastructure holds about you.
The platform wallet
The Solana keypair Umbra derives from your account signature, what it signs, how it is bound to your main wallet, and why that binding is public.