A standard Kaspa wallet has no delay and no safety net. If someone gets your private key — through phishing, a compromised device, or physical coercion — they broadcast a transaction and the KAS is gone within a block. There is no "undo" button on a blockchain.
Kaspa Safe solves this with a covenant-based vault that enforces a withdrawal delay chosen by the owner. Every outgoing withdrawal is queued for a fixed period (from one hour to ninety days). During that window, a second, independently-stored alarm key can cancel the withdrawal and return all funds to the vault. A thief who obtains only the hot key gets nothing — the coins sit in the contract until the owner notices the alert and presses "Cancel."
This article explains how those two mechanisms work at the covenant-script level, how they interact, and where the security model has real boundaries.
The on-chain contract: state and spending paths
Kaspa Safe is built on a Toccata covenant — a Silverscript (vault.sil, currently v3) compiled into a Kaspa P2SH address. The contract holds two pieces of state:
mode:0= VAULT (normal),1= UNVAULTING (withdrawal in progress)dest: a 36-byte version-prefixed script pubkey — the destination address, locked in at the moment a withdrawal starts
There are seven spending paths out of the contract, each with different signature requirements and state transitions. The three that matter for theft protection:
| Path | Signature | State transition | Purpose |
|---|---|---|---|
initiate(hotSig, destPk) | Hot key | VAULT → UNVAULTING | Start a withdrawal; destination is fixed in dest |
cancel(alarmSig) | Alarm key | UNVAULTING → VAULT | Cancel during the delay window |
complete() | None | UNVAULTING → dest | Only after age >= delay; pays strictly to the locked dest |
The remaining paths handle check-ins (checkin), inheritance (inheritAuto / inheritSigned), and full-control migration (migrate, which requires both keys). All seven begin with a require(tx.inputs.length == 1) guard — this prevents a multi-UTXO siphon attack where two covenant UTXOs share one transaction output, leaking the smaller UTXO's value to fees.
How the withdrawal delay works, step by step
DAA score — Kaspa's Difficulty Adjustment Algorithm timestamp. The network targets roughly 10 blocks per second, so 36,000 DAA ≈ 1 hour. The vault measures time in DAA, not wall-clock seconds.
1. The owner initiates a withdrawal.
Using the hot key, the owner (or an attacker who stole it) calls initiate. The transaction includes two things: the hot-key signature and the destination public key. The covenant verifies the signature, then flips mode from 0 to 1 and writes the destination into dest. From this moment, dest is immutable — it is part of the UTXO's on-chain state and cannot be changed by anyone.
2. The delay window begins.
The UTXO's "age" (time since last state change, measured in DAA) resets to zero. The contract will not allow complete until age >= delay, where delay is the value the owner chose at vault creation. Presets range from 6 hours (216,000 DAA) to 14 days; the full custom range is 1 hour to 90 days.
During this window, the funds are frozen. Not the owner, not the attacker, not the Kaspa Forge servers — nobody can move them. The complete path explicitly checks the age and rejects early calls.
3. The owner (or anyone) can broadcast cancel.
If the withdrawal was unauthorized, the owner uses the alarm key to sign a cancel transaction. The covenant verifies the alarm signature, flips mode back to 0, and zeroes out dest. The funds are back in the vault, fully protected.
Crucially, cancel does not require the hot key, the original device, or even the Kaspa Forge website. It requires only the alarm key and access to any Kaspa node. The open-source vaultctl CLI tool can do this from a terminal against any Kaspa v2+ node with utxoindex.
4. If no cancel arrives, funds release to the locked destination.
Once age >= delay, the complete path becomes available. It requires no signature at all — the contract enforces that funds can only go to dest, which was fixed in step 1. This is by design: anyone (including Kaspa Forge's watcher service) can broadcast the completion transaction, but the money always goes to the address the initiator specified. There is no way to redirect it.
Auto-complete — an optional flag (recommended) where the Kaspa Forge watcher automatically broadcasts the complete transaction once the delay expires. This means the owner does not need to return to the site to claim funds. The contract guarantees the destination regardless of who triggers it.
Why two keys instead of one
The security model rests on key separation. Here is the matrix:
| Attacker has… | Can they steal funds? |
|---|---|
| Hot key only | They can start a withdrawal, but the owner gets the full delay window to cancel with the alarm key |
| Alarm key only | They can cancel withdrawals, but cannot start one or move funds anywhere |
| Both keys | Instant loss. The migrate path allows moving all funds to any address with no delay |
This is the honest trade-off: two keys give you a response window, but both keys together give full control. The entire model depends on storing the hot and alarm keys in different places — the hot key on a device or password manager (convenient for regular use), the alarm key on paper in a separate physical location (used only in emergencies).
If an attacker obtains both keys, the delay mechanism offers no protection. The migrate path exists for legitimate purposes — rotating an exposed key, moving to a new vault version — but it is also the instant-loss path. This is a conscious design decision: two signatures equal full owner authority, with no server or third party able to intervene.
What the alarm key can and cannot do
The alarm key has exactly one on-chain capability: it can sign the cancel path. Here is what that means in practice:
- Cancel moves funds back into the vault, never out. The
cancelpath transitions from UNVAULTING back to VAULT. It does not set a destination or release funds. A thief who somehow obtains only the alarm key cannot steal anything — they can only disrupt withdrawals. - Cancel works at any point during the delay window. There is no minimum wait. The moment an
initiatetransaction confirms, the alarm key can immediately reverse it. - Cancel zeroes
dest. The destination address is erased from the contract state. Even if the same attacker re-initiates a withdrawal, they must pick a destination again, and the full delay restarts from zero. - Cancel does not require the Kaspa Forge website. The alarm key plus the
vaultctlCLI or any compatible tool is sufficient. This matters if the site is unreachable or the attacker blocks access to it.
The alarm key is intentionally excluded from the initiate path. Only the hot key can start withdrawals. This means a compromised alarm key alone gives an attacker no ability to move funds — only to cancel, which returns money to the owner's vault.
Fee budget and the griefing boundary
Each path (except migrate) checks a feeBudget parameter, capped at 0.1 KAS. This prevents a griefing attack where a watcher or third party broadcasts a keyless path (like complete or inheritAuto) with an inflated fee, burning the vault's value into miner revenue. The migrate path deliberately has no such cap — it requires both owner signatures, so the owner controls the fee directly.
The current v3 implementation has a known limitation: constrained paths verify the required outputs[0] value but do not enforce tx.outputs.length == 1. This means a transaction author could add a second output within the fee budget (0.01–0.1 KAS) instead of paying it as a network fee. The Kaspa Forge builders always construct canonical single-output transactions, and the vault's core value is protected by the value invariant. A stricter output-count guard is planned for v4.
How the watcher and alerts fit in
The vault's on-chain logic is self-sufficient — it does not need a server to enforce the delay or honor the alarm key. But in practice, you need to know that a withdrawal has been initiated. This is where the Kaspa Forge watcher comes in:
A background service polls each registered vault every 10 seconds, comparing UTXO snapshots. When it detects a transition from VAULT to UNVAULTING, it sends an alert through every connected channel: Telegram, email, and browser push notifications. The alert contains the event type (withdrawal initiated, cancelled, completed) but never includes addresses, amounts, or labels — those are assembled client-side for privacy.
The alert subscription is optional and paid (100 KAS/year, 30-day free trial). The on-chain vault protection works regardless of whether alerts are active. But without alerts, you might not learn about a theft-initiated withdrawal until you happen to check the vault panel — and by then the delay window might have expired.
Kaspa Safe is live on mainnet. The vault contract, the alarm-key cancellation mechanism, and the watcher alerts are production infrastructure — not a prototype. Keys are generated in your browser and never leave your device; the contract code is open-source with a 25-check self-test suite. If you want to see the delay and alarm key in action, the vault creation wizard walks through every parameter, and the recovery page documents exactly what to do during a theft. For offline operation without the website, the vaultctl CLI tool does everything from a terminal against any Kaspa v2+ node.
Trade-offs and honest limits
No security mechanism is free. Here are the real costs:
- Your own withdrawals are delayed. If you need to move KAS quickly — say, to an exchange during a time-sensitive opportunity — you wait the full delay period. The
migratepath (both keys) is the only instant exit, which means keeping both keys accessible for emergencies defeats the separation model. Most users accept a 24-hour delay as a reasonable default.
- Lost alarm key means lost theft protection. You can still withdraw normally (waiting out the delay), but you cannot cancel an attacker's withdrawal. The recommended response: immediately do a normal withdrawal to a new vault with a fresh alarm key.
- Lost hot key means no normal withdrawals. If inheritance is on, stop checking in and wait for the heir to claim. Without inheritance, the funds are effectively burned — no one, including Kaspa Forge, can recover them. This is why the FAQ recommends always enabling inheritance, even to your own second address.
- Both keys compromised = instant loss. There is no recovery path. Key separation is the boundary of the security model, not a suggestion.
- The contract is unaudited. It has passed internal adversarial testing and a 25-check self-test, and the code is open for review, but there has been no third-party security audit. The vault holds real KAS on mainnet — users should verify before storing significant amounts.
- Time is measured in DAA, not wall-clock seconds. Kaspa's block rate targets 10 BPS, but actual DAA progression can vary. The interface converts to human-readable time, but on-chain enforcement uses DAA score. This is a protocol-level fact, not a Safe-specific issue — see the Kaspa wiki for details on DAA and block timing.
Putting it together
The theft-protection model in Kaspa Safe is deliberately simple at the contract level: one key starts a withdrawal, a second key can cancel it, and time does the rest. There is no oracle, no multisig committee, no off-chain approval step. The covenant script on the Kaspa blockchain enforces the delay and the destination — and it will do so whether Kaspa Forge exists or not.
The alarm key is not a sophisticated mechanism. It is a single signature on a single path that flips a single state bit from 1 back to 0. That simplicity is the point: there is nothing to break, nothing to misconfigure, and nothing that requires trust in a third party. If you store the alarm key separately from the hot key, a thief with one key gets a countdown timer instead of your KAS — and you get a chance to act.
FAQ
What happens if someone steals my hot key?
They can initiate a withdrawal, but the funds remain locked in the vault for the entire delay window (your chosen period). You use the separately-stored alarm key to cancel the withdrawal, and the coins return to the vault.
Can the alarm key be used to steal my funds?
No. The alarm key can only cancel an in-progress withdrawal — it moves funds back into the vault, never out. Only the hot key can start a withdrawal. The only path that uses both keys together is migrate, which requires both signatures.
What if I lose the alarm key?
You lose the ability to cancel theft-initiated withdrawals. You should immediately do a normal withdrawal (waiting out the delay), then move the KAS into a new vault with a fresh alarm key.
What if I lose the hot key?
You cannot start normal withdrawals. If inheritance is on, stop checking in — after the inheritance period, the funds go to your heir (which can be your own second address). Without inheritance, the funds are frozen permanently.
Can the Kaspa Forge team reverse or access my vault?
No. Keys are generated and exist only in your browser. The vault is a covenant script on the Kaspa blockchain — OfficeForge has no access to your keys or funds and cannot reverse transactions.
Is the vault contract audited?
The v3 contract is live on mainnet and has been through internal adversarial testing, but there has been no external audit yet. The contract code and a CLI tool (vaultctl) are open-source for independent verification.
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
