Kaspa Forge
Deep dive

How Kaspa Forge Encrypts Your Keys: .Age Profiles, Session Locks, and Offline Recovery

7 Aug 2026 By OfficeForge's AI team · human-reviewed 9 min read
Kaspa Forge Key Security: .Age Encryption and Session Locks

Every browser wallet answers the same question differently: *where do private keys exist, and who can read them?* Kaspa Forge's Desk takes a specific position — keys are generated in your browser, encrypted at rest with the age file-encryption tool, and held in memory only during an active session that auto-locks after 15 minutes. No server ever sees the plaintext. And a single backup file, decrypted with your password alone, restores every key you will ever use across Kaspa Safe, Escrow, Deposit, and Marketplace.

This deep-dive walks through the encryption envelope, the profile format, the session security model, and the recovery paths — with honest notes on what this design does and doesn't protect.

Where do keys live — and the trade-off space

Three common patterns exist for browser-wallet key storage, each with a known weakness:

  • Server-held encrypted keys. Convenient, but you trust the operator not to log your password or comply with a data request.
  • Plaintext browser storage (localStorage / IndexedDB). Fast, but any JavaScript injection — XSS, compromised dependency, rogue extension — can exfiltrate the key.
  • Hardware signer (Ledger, Trezor). Keys never touch the browser. Strong, but adds cost and UX friction.

Kaspa Forge's Desk takes a fourth path: encrypt the entire profile with a user-chosen passphrase using .age (a modern, audited file-encryption tool), store the ciphertext in the browser, and decrypt into memory only inside an active session. The passphrase itself is never persisted.

What .age encryption is

age (pronounced like the English word) is a file-encryption tool designed by Filippo Valsorda. Its goals are simplicity and modern cryptography: one command to encrypt, one to decrypt, no keyrings or PKI. Passphrase mode uses scrypt — a memory-hard key derivation function that makes GPU-accelerated brute-force expensive.

Kaspa Forge implements the same protocol in Rust inside the WASM core crate (age_crypto.rs). Two public functions handle the envelope:

pub fn encrypt_armored(passphrase: &str, plaintext: &[u8]) -> String
pub fn decrypt_armored(passphrase: &str, armored: &str) -> Result<Vec<u8>>

Both execute entirely in the browser — the WASM module runs sandboxed, the passphrase never leaves the client, and only the ciphertext is persisted to localStorage (or exported as a .age file for backup).

Definition

.age file — a file encrypted with the age encryption tool. Kaspa Forge uses scrypt-passphrase mode: your password is the only key. The output is ASCII-armored, so it survives clipboard paste, email attachment, and any text-only transport.

A critical design property: the .age file you download as a backup is byte-compatible with the upstream CLI. On any machine with age installed, you can decrypt your Kaspa Forge profile independently:

age -d -o profile.json my-backup.age
# enter your passphrase
# → decrypted profile.json with your HD seed, keys, and vault metadata

No Kaspa Forge software. No running server. This is the foundation of the non-custodial guarantee: service death does not equal fund loss.

Inside the encrypted profile (version 3)

The decrypted profile is a JSON document tagged version: 3. Here's what it holds:

FieldWhat it is
seedHD master seed — every key derives from this single 32-byte value
hot_skHot signing key for vault withdrawals (or derived from seed)
alarm_sk / alarm_cardAlarm key (hex) or true — see below
funding_sk / funding_pkKey pair for vault funding addresses
fee_budgetDefault network-fee cap (sompi) for covenant transactions
tokenOwner authentication token for the Kaspa Forge API
wallet / walletOldCurrent and previous receiving addresses
noteUser's private label for a vault

The most consequential field is seed. From this single value, every key for every Kaspa Forge product is derived deterministically using HMAC-SHA512 with domain-separated messages:

HMAC-SHA512(key=seed, msg="kaspaforge/v1/vault/0")      → vault 0 hot key
HMAC-SHA512(key=seed, msg="kaspaforge/v1/vault/1")      → vault 1 hot key
HMAC-SHA512(key=seed, msg="kaspaforge/v1/escrow/0")     → escrow deal 0 chat key

The domain string encodes the product and index, so keys never collide across tools. The practical consequence: a single .age backup taken today covers every vault, deal, deposit, and listing you will ever create — even those that don't exist yet. Derivation generates them on demand.

The alarm card: a key outside the profile

When creating a Kaspa Safe vault, you configure two keys: a hot key (to initiate withdrawals) and an alarm key (to cancel a withdrawal in progress, as a theft countermeasure). The hot key always lives in the encrypted profile. But the alarm key offers a choice.

If you enable alarm_card, the alarm private key is generated once, displayed for you to record on paper or a physical card, and never stored in the profile. The profile records only the boolean flag.

The security benefit is separation: an attacker who extracts your .age file and cracks your passphrase holds the hot key but not the alarm key. They can initiate a withdrawal — but you can cancel it during the delay window using the physical card. Conversely, theft of the card alone cannot initiate a withdrawal. The two failure modes are independent.

Session security: the browser as a lockbox

Encrypting at rest is half the protection. When you actually use the Desk — checking balances, signing withdrawals, joining escrow deals — keys must be decrypted. Here's how the runtime layer works:

Boot guard (requireUnlock). Every Kaspa Forge page checks for a valid session on load. If none exists, the UI presents a password prompt. The encrypted profile is decrypted into memory and a session timestamp is recorded.

Per-action confirmation (confirmPassword). Any operation that produces a signed transaction — sending KAS, initiating a vault withdrawal, releasing escrow funds — re-prompts for the password. The prompt displays context: the amount and recipient address. This provides a deliberate "are you sure?" gate that also resists shoulder-surfing.

Auto-lock (15 minutes). After 15 minutes without interaction, the session is destroyed. Decrypted keys are zeroed from memory (to the extent JavaScript allows), and the next action requires the full unlock.

No persistent password. The passphrase is never written to localStorage, sessionStorage, cookies, or any durable store. It exists only in a JavaScript variable during the live session. A page refresh or tab close requires re-entry.

The trade-off is intentional friction: you type your password more often than a custodial app would demand. For a tool holding real funds on a proof-of-work network, each prompt is a conscious authorization.

Recovery without the server

Three paths restore access if you lose your device, none requiring Kaspa Forge to be online:

The .age backup file + passphrase. Decrypt with the upstream age -d CLI or with Kaspa Forge's published keyfile-decrypt.html — a self-contained HTML page that runs age decryption in your browser, offline. Extract the HD seed. Then use recover.html for a gap-scan: it derives addresses from your seed in order and queries the Kaspa network for any with UTXO history.

The seed alone. If you recorded the HD master seed at vault-creation time, you can skip the .age file entirely. The open-source CLI tool vaultctl performs a gap-scan against any public Kaspa node:

vaultctl recover --seed <hex> --node node.kaspaforge.org

vaultctl for everything. Beyond recovery, vaultctl performs all vault operations — check balance, initiate withdrawal, cancel, complete, check-in, inherit, migrate — from a terminal against any Kaspa v2+ node. No web UI, no JavaScript, no server.

The underlying guarantee: your vaults are on-chain covenant contracts enforced by the Kaspa network. A vault's withdrawal delay, alarm key rule, and inheritance logic are embedded in the blockchain script — not in a database controlled by kaspaforge.org.

Try it yourself. Create a Kaspa Safe vault — on-chain operations are free forever. Download your .age backup, then verify you can decrypt it independently with age -d. You'll have first-hand confidence that your keys survive any single point of failure.

Create a vault

What this model does and doesn't protect against

Honest security analysis requires listing both sides.

Protected against:

  • Server compromise. The backend never sees keys, seed, or passphrase. A breach reveals nothing about your funds.
  • Browser-tab injection (partially). Auto-lock and per-action password prompts limit the window for an injected script. A persistent XSS on the exact page where keys are in memory could theoretically act during an active session — but the window is bounded.
  • Device loss. The .age backup restores everything. The alarm card (if used) provides an independent recovery path for cancellation.
  • Service death. Covenant rules, vaultctl, and keyfile-decrypt.html all function without Kaspa Forge's servers.

Not protected against:

  • Weak passphrase. The .age file's security reduces to your password strength against scrypt brute-force. A short or dictionary password is the single weakest link in the chain.
  • Full device compromise. If malware has root or kernel access, it can capture the password at entry time and read memory during the session. This is a fundamental limitation of software wallets — hardware signers address it. Kaspa Forge's architecture doesn't preclude future hardware-wallet integration.
  • Loss of all backups plus password. There is no server-side escrow. This is by design: the user bears full responsibility for redundancy. One .age file on a USB drive, one written-down passphrase in a separate location — that's the minimum viable backup.
  • Compromise of both hot and alarm keys. If an attacker obtains both keys simultaneously (e.g., from the profile when alarm_card is disabled), the vault's time-delay is their only remaining obstacle. The migrate path — which requires both signatures — lets them move funds instantly. This is an accepted trade-off in the vault design, motivating the alarm-card option for high-value vaults.

One profile, every product

The encrypted Desk profile is the shared identity layer across the entire Kaspa Forge stack:

  • Kaspa Safe reads hot/alarm keys and vault parameters from the profile.
  • Kaspa Escrow derives per-deal chat keys and escrow signing keys from the same HD seed.
  • Kaspa Deposit uses the escrow key paths (it runs on the same on-chain covenant).
  • Marketplace reuses escrow infrastructure — one profile powers listing chat, deal creation, and dispute participation.
  • The wallet derives receiving addresses and signs spending transactions with derived keys.

One .age file, one password, every product. The domain-separated HD seed at the center of the encryption envelope makes this possible without key reuse or collision — and a single backup covers them all.

FAQ

Can Kaspa Forge see my private keys?

No. Keys are generated in your browser via WASM, encrypted with your passphrase using age, and stored only in your browser's localStorage. The server never receives the plaintext seed, signing keys, or password.

What is a .age file?

A file encrypted with the age encryption tool (age-encryption.org). Kaspa Forge uses scrypt-passphrase mode: your password derives the decryption key via a memory-hard KDF. The format is ASCII-armored and compatible with the standard age CLI.

What happens if I forget my Desk password?

If you have the .age backup but forgot the password, the file cannot be decrypted — scrypt makes brute-force impractical for reasonable passwords. If you separately recorded the HD master seed at vault creation, you can restore keys without the .age file. Otherwise, funds remain on-chain but inaccessible until you recover the keys.

Does the backup cover future vaults and deals?

Yes. All keys derive from a single HD master seed using domain-separated HMAC-SHA512 paths. A backup taken today covers every vault, escrow deal, deposit, and marketplace interaction you will ever create.

What does the alarm card do?

When enabled, the vault's alarm (cancellation) key is stored on a physical card — not in the encrypted profile. An attacker who cracks your .age file gets the hot key but not the alarm key: they can initiate a withdrawal, but you can still cancel it during the delay window.

Can I use a hardware wallet with Kaspa Forge?

Not currently. The Desk profile uses software keys processed in browser-side WASM. The architecture does not preclude future hardware signer integration, but today all signing happens in the browser.

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