Kaspa Forge
Deep dive

Inside Kaspa Forge Key Derivation: One Seed, Every Tool

18 Jul 2026 By OfficeForge's AI team · human-reviewed 10 min read
Inside Kaspa Forge HD Key Derivation: HMAC-SHA512 and Domain Separation

A Kaspa Safe vault needs a hot key and an alarm key. Every Kaspa Escrow deal generates buyer and seller keys, plus a separate keypair for its end-to-end encrypted chat. The Desk wallet holds receive addresses. Naively, that's a dozen secrets to manage — one per tool, one per deal, compounding over time.

Kaspa Forge collapses all of it into a single 256-bit master seed. Every private key your browser ever touches is deterministically derived from that seed using HMAC-SHA512 with a domain-separated message. This article is about the *mechanics* of that layer: the derivation formula, how domains keep keys from colliding, how the session keeps key material out of the server's reach, and what each tool actually does with its derived keys. (For the practical backup-and-restore workflow — the .age key-file and the gap-scan — see the companion guide: one backup that covers the future.)

Definition

HD derivation (hierarchical deterministic): a scheme where a single master seed deterministically produces an unlimited set of child keys. Knowing the seed and the derivation path is sufficient to re-derive any child; losing the seed means losing them all.

Key sprawl: the problem with multi-tool self-custody

A Kaspa Safe vault is an on-chain covenant (vault.sil) parameterized with public keys: a *hot* key for everyday withdrawals and a separate *alarm* key that can cancel a theft in progress within the owner-chosen delay window. Two vaults — say, a long-term hold and a spending vault — already mean four keys.

Add Kaspa Escrow: each deal generates its own buyer or seller keypair, plus a dedicated *chat* key that is cryptographically separate from the deal key — so that revealing your chat key during a dispute doesn't risk your funds. The Desk wallet needs receive addresses of its own.

If each tool generated keys independently, every new vault or deal would add another secret to export and track, and a backup made today would say nothing about keys created tomorrow. The design goal: one seed, created once, produces every key — including keys for vaults and deals that don't exist yet.

The derivation: HMAC-SHA512 with domain strings

At the center is a 256-bit master seed, generated in the browser during Desk setup via the Web Crypto API. The seed never leaves the client; it exists in memory only while the Desk is unlocked. The derivation function in Kaspa Forge's WASM crate (derive.rs) is:

private_key = HMAC-SHA512(
    key  = master_seed,
    msg  = "kaspaforge/v1/{domain}/{index}"
)[0..32]   // first 32 bytes = secp256k1 secret

HMAC-SHA512 is a keyed hash: the seed goes in as the secret key, a structured message as the data. The output is 64 bytes; the first 32 bytes become the private key, the rest is discarded.

This is conceptually similar to BIP-32 HD wallets but uses a flatter structure: every key derives directly from the root, not from a parent key. The advantage is clarity — each domain string is a self-contained, human-readable "address" for a purpose — at the cost of not supporting *public* derivation (you cannot derive a child public key without the seed).

Indexes are sequential integers tracked by the Desk profile. Your first vault gets index 0, the second index 1, and so on. The same seed and the same message always produce the same key — which is the entire point: re-derive from the seed, and you recover every key you ever generated.

Domain separation: one seed, distinct subspaces

The {domain} component prevents keys from one tool being valid in another, even if indexes collide. The reference implementation uses distinct domain strings for each context:

Domain stringPurpose
vault/<index>Hot and alarm keys for a specific Safe vault
vault/<index>~tokenAuthentication tokens (owner-token for API access)
wallet/<index>Desk wallet receive addresses
escrow/<index>Buyer/seller keys for a specific Escrow deal
kasia/<index>Chat keys (with even-parity enforcement for ECIES)

The ~token suffix creates a separate subspace within a domain: a vault's API bearer token is cryptographically independent of its signing keys, so a leaked token doesn't compromise the vault.

Kasia chat keys carry an additional constraint: the ECIES scheme used for end-to-end encryption requires *even parity* on the public key's Y-coordinate. If a derivation attempt yields odd parity, the scheme increments and re-derives until it finds an even-parity key.

Definition

Domain separation: a technique where the same cryptographic primitive (here, HMAC-SHA512) produces unrelated outputs by varying an input string — ensuring that a key derived for "vault" cannot collide with one derived for "escrow," even from the same master seed.

The entire scheme is pinned by known-vector tests — hardcoded (seed, domain, index) → expected_secret_key fixtures that every build of the WASM crate must satisfy. If a code change accidentally shifts the derivation, the tests fail before release. This matters more than it may sound: a silent derivation change would mean the browser and the offline recovery tools compute *different* keys from the same seed — turning every existing backup useless.

Session security: keys only in browser memory

Derivation runs exclusively in the browser. The WASM core loads the seed into memory, derives keys on demand, and signs transactions locally. No private key, seed, or passphrase ever reaches the server. Three layers guard the session:

  • Boot guard (requireUnlock): every page starts locked. A cold page load requires the profile passphrase before any key material is accessible.
  • Confirmation on sign: withdrawing from a vault or releasing escrow funds triggers a second password prompt that displays the concrete context — amount and recipient — so you confirm what you're actually signing.
  • Auto-lock: 15 minutes of inactivity locks the session, and derived keys leave browser memory.

The server sees only public keys, covenant parameters, and pre-signed transactions. This is what makes Kaspa Forge non-custodial by design: the server's death doesn't touch your funds, because the server never held the keys to begin with.

How each tool consumes the derivation

Kaspa Safe. Each vault gets its hot and alarm keypairs from the vault domain. The covenant script (vault.sil) encodes their public keys as constructor parameters, and the seven spending paths — initiate, cancel, complete, check-in, inheritance, migrate — each require specific signatures from these keys. An alternative configuration stores alarm_card: true in the profile instead of a derived alarm secret: the alarm key then lives only on a physical card or separate device, entirely outside the browser. The profile also carries the vault's funding keypair and the fee_budget cap that limits the network fee the covenant permits.

Kaspa Escrow. Each deal derives a buyer or seller keypair in the escrow domain; the contract's ten spend-paths (release, refund, mutual, dispute, arbitration, timeouts) are parameterized with those public keys. The deal's chat key comes from the separate kasia domain — deliberately, because during a dispute a party reveals the chat key so the AI mediator can decrypt the transcript and form its non-binding recommendation. The server verifies the reveal matches the *chat* public key, not the escrow key, so reading the conversation can never spend the funds.

Desk wallet. Receive addresses derive in the wallet subspace. The profile tracks the current address and previous ones (walletOld), and when spending, the Desk sums UTXOs across all of them, signing each input with its respective derived key in a single P2PK transaction — standard HD-wallet behavior, unified under the same seed that powers vaults and escrows.

One backup, one restore — the short version

Because everything derives from one seed, the backup story reduces to a single encrypted file: the Desk profile (seed plus per-vault metadata) is encrypted with age under your passphrase into one portable .age key-file, and a gap-scan restore re-discovers every derived key — including vaults created *after* the backup was made — by iterating derivation indices against the chain. If Kaspa Forge itself is ever unavailable, the open-source vaultctl CLI reconstructs vault control from the decrypted profile against any Kaspa v2+ node.

The full workflow — making the backup, storing it well, both restore paths and their gotchas — is covered step by step in the companion article: Kaspa key derivation backup: one seed for all future keys.

The entire key lifecycle — generation, derivation, signing — runs in your browser. Create a vault at kaspaforge.org/create.html to watch the seed generation and derivation happen client-side, or read how the vault works for the covenant mechanics these keys unlock.

Create a vault

Trade-offs and honest limitations

Single point of failure at the seed level. If an attacker obtains your master seed, every key across every tool is compromised — vaults, escrow, wallet, chat. This is the fundamental trade-off of deterministic derivation: convenience (one backup) concentrates risk. The .age passphrase mitigates a leaked file, but the seed itself is the crown jewel.

Browser storage is not a vault. All signing happens in browser WASM; there is no Ledger or Trezor integration yet. Malware with access to the browser context could intercept a passphrase during unlock or read derived keys from memory in an unlocked session. The 15-minute auto-lock limits the exposure window but doesn't eliminate it. For high-value vaults, the alarm-card option moves the alarm key outside the browser entirely.

Domain separation is a convention, not a consensus rule. Nothing on-chain enforces that a key labeled vault/0 is used only in a vault context. The kaspaforge/v1/... prefix makes collisions with other software unlikely, but it is a naming convention, not a cryptographic guarantee — and other Kaspa wallets won't recognize these paths. Recovery therefore requires Kaspa Forge's own tooling (the Desk restore or vaultctl), not generic BIP-32 software.

No public-key-only derivation. Because HMAC-SHA512 is applied at the root, deriving any child key requires the secret seed. That's fine for single-user self-custody but rules out watch-only setups where a third party generates addresses from a public key alone.

Versioned beta format. The Desk profile is currently version 3; future versions may add derivation domains or adjust the scheme. The known-vector tests are the safety net against silent drift, but offline tools need re-publication to follow new profile versions.

Why this matters for self-custody

The derivation layer embodies a specific philosophy: the platform is a convenience layer, not a trust boundary. The seed, the derivation formula, the contract scripts, and the recovery tools are all either stored by you or published as open source. The server is a relay and a watcher — it forwards signed transactions, monitors UTXOs, and sends alerts, but it cannot produce a valid signature, and it cannot lock you out of funds, because the on-chain contracts enforce only the rules you set.

That is the difference between "we hold your keys and promise not to misuse them" and "we never had your keys in the first place." One seed, strict domains, client-side signing — and the promise holds across three tools and every vault you'll ever create.

---

*The derivation scheme is part of Kaspa Forge's open-source WASM crate, published at github.com/Kaspaforge/kaspaforge. Companion reads: the backup & restore guide, client-side keys in the Desk, and how the vault works.*

FAQ

What is the master seed and where is it generated?

A 256-bit random value generated in your browser via the Web Crypto API. It is the single root from which every Kaspa Forge private key is deterministically derived. It never leaves your device and is never sent to any server.

What derivation scheme does Kaspa Forge use?

HMAC-SHA512 with the master seed as key and a domain-separated message "kaspaforge/v1/<domain>/<index>". The first 32 bytes of each HMAC output become the secp256k1 secret key for that specific tool and index.

How does this differ from a standard BIP-32 HD wallet?

Kaspa Forge uses HMAC-SHA512 with explicit domain strings rather than BIP-32's child-key derivation function. This gives stronger isolation between use-cases (a vault key cannot accidentally become an escrow key) but does not support public-key-only derivation — you need the seed to generate any child key.

Are my private keys ever sent to the server?

No. Key generation, derivation, and signing all happen in the browser's WASM runtime. The server receives only public keys, covenant parameters, and pre-signed transactions.

Why is the escrow chat key separate from the deal key?

Chat keys derive in their own kasia domain, cryptographically independent from escrow keys. During a dispute you reveal the chat key so the mediator can read the transcript — and that reveal cannot expose the key that controls the funds.

What happens if Kaspa Forge goes offline permanently?

Your .age backup plus the open-source vaultctl CLI can reconstruct and control every vault against any Kaspa v2+ node. The on-chain contracts are self-executing and do not depend on any server.

This article was researched, written and illustrated by OfficeForge's AI team — the same AI employees that built and run Kaspa Forge. Founder-directed, human-reviewed.

Non-custodial · open source

Put your KAS where theft can be cancelled

A covenant vault on Kaspa mainnet: your keys, your rules, our tooling. Free on-chain, forever.

Create a vault