Self-custody is a binary property: either you hold your keys, or someone else holds your money. The problem is that raw self-custody gives you control but no guardrails. A hardware wallet protects your seed, but it cannot enforce a withdrawal delay, flag a theft in progress, or hand your assets to an heir if you disappear. Those rules have historically required a custodian — an exchange, a Telegram escrow bot, a multisig platform — reintroducing the very trust assumption self-custody was supposed to eliminate.
Kaspa's approach is to push those rules *into the UTXO itself*, enforced by every full node on the network. The Toccata hardfork activated covenant support on mainnet, and the protocol's proof-of-work blockDAG provides the settlement layer. The result is programmable money that does not depend on any server to enforce its rules: a vault that delays withdrawals, an escrow that routes funds only to named parties, a dead-man switch that triggers inheritance — all on-chain, all self-custodial.
This article walks through how the protocol layer (GHOSTDAG, DAA, PoW) and the contract layer (Toccata covenants in Silverscript) combine to make trustless tools possible, then shows how Kaspa Forge implements that stack in production.
GHOSTDAG: Ordering Blocks Without Orphaning Them
Kaspa is a proof-of-work blockDAG, not a linear chain. Instead of one block extending the tip every N seconds, multiple miners can publish blocks simultaneously. The GHOSTDAG protocol classifies each block as blue (well-connected, contributes to security) or red (too delayed to be reliably ordered), then selects a "selected chain" of chain blocks whose cumulative blue work determines the canonical ordering.
From any chain block C, the mergeset is the set of blocks in C's past but not in the selected parent's past. GHOSTDAG sorts this mergeset by blue work (tiebreaking by hash), and that order determines which transactions are accepted and in what sequence. The virtual block — a node-local pointer to all current tips — has its own selected parent (the selected tip), and walking "selected parent" links downward traces the selected chain.
The difficulty adjustment algorithm (DAA) uses a moving window of 2641 blocks (blue blocks plus red blocks that fit in the window, counted backward from the virtual's perspective) to keep the average block rate at the target — currently 10 blocks per second. Each block's blue score counts how many blue blocks precede it, and the DAA score (blue score plus successfully merged red blocks) controls the emission schedule.
For Kaspa self custody, the takeaway is straightforward: the PoW settlement layer is permissionless and immutable. No authority can freeze a UTXO, reverse a confirmed transaction, or alter the consensus rules unilaterally. A covenant deployed to the blockDAG is as irreversible as the proof-of-work that buried it.
For the full protocol specification, see the Kaspa wiki.
Toccata Covenants: Spending Rules Inside the UTXO
A conventional P2PK address says: "anyone with the private key can spend this." A covenant adds: "anyone with the private key can spend this, *but only* to address X, *only* after a delay, *only* if the transaction has exactly one input." The constraint is verified by every node — no server, no oracle, no off-chain signer required.
On Kaspa, covenants were activated by the Toccata hardfork and are written in Silverscript (currently ^0.1.0). The compiled script is embedded into a P2SH address. When a spending transaction hits the network, the node evaluates the covenant against the transaction's actual structure — inputs, outputs, signatures — and rejects anything that violates the rules.
Covenant: A UTXO script that constrains the spending transaction beyond simple signature verification — restricting destinations, timing, transaction structure, or state transitions. On Kaspa, covenants are written in Silverscript and enforced by the Toccata-activated consensus layer.
This is the mechanism that makes Kaspa self custody programmable. The "program" is not running on a server you have to trust; it is running inside the consensus verification that every full node performs independently.
The Vault Contract: Seven Paths, One Invariant
Kaspa Safe's core is a single on-chain contract, vault.sil. The vault has two states — VAULT (normal) and UNVAULTING (withdrawal in progress) — and seven spending paths:
| Path | Who signs | Transition | Purpose |
|---|---|---|---|
initiate(hot, dest) | hot key | VAULT → UNVAULTING | Start a withdrawal; destination locked in state |
cancel(alarm) | alarm key | UNVAULTING → VAULT | Reverse a theft before the delay expires |
complete() | *none* | UNVAULTING → dest | Finalize after delay; anyone can broadcast |
checkin(hot) | hot key | VAULT → VAULT | "I'm alive" — resets the inheritance timer |
inheritAuto() | *none* | VAULT → heir | Dead-man switch; triggers after inheritDelay |
inheritSigned(heir) | heir key | VAULT → heir | Heir claims manually |
migrate(hot, alarm) | *both* | any → anywhere | Full-owner escape hatch for key rotation or version upgrade |
Two invariants protect every vault UTXO:
Single-input constraint. Every path begins with require(tx.inputs.length == 1). This closes a drain vector where two UTXOs on the same P2SH address share one transaction output — the smaller UTXO's excess would leak to the miner fee. With single-input enforcement, each vault UTXO is spent independently.
Fee budget cap. Six of the seven paths enforce feeBudget > 0 && feeBudget <= 10_000_000 sompi (0.1 KAS). This prevents a griefing attack where a watcher or third party broadcasts a keyless path (complete, inheritAuto) with the vault balance consumed as a miner fee. The only exception is migrate, which requires both keys — the owner has full control and full responsibility.
The heir = 32 zero bytes sentinel disables inheritance entirely. When inheritance is active, exactly one of the two inherit paths is live (automatic *or* signed, never both). Keyless paths can be broadcast by anyone — the funds go exactly where the covenant says, regardless of who submits the transaction.
Escrow: Ten Paths, No Third-Party Recipient
The same covenant pattern extends to P2P deals. Kaspa Escrow uses escrow.sil with three named pubkeys (buyer, seller, arbiter) and ten spending paths. The critical design invariant: no spending path can send funds to anyone other than the buyer, seller, or the designated fee address. The arbiter — who resolves disputes — can only split between the two parties or send 100% to one side. Physically cannot redirect funds elsewhere.
The contract supports optimistic auto-release (buyer does not dispute within the window → seller gets paid, no signature needed), a dispute timeout (pre-agreed party receives funds if the arbiter disappears), and mutual release (both parties sign freely). The same single-input and fee-budget-cap invariants apply across all ten paths.
Together, the vault and escrow contracts define 17 entrypoints, all with single-input enforcement and 16 of 17 with fee-budget caps.
How Kaspa Forge Implements This in Production
Kaspa Forge is the application layer that makes these contracts usable. Three tools share a single encrypted browser profile (the Desk):
Kaspa Safe is the vault interface. The creation wizard generates keys in the browser via a WASM core (kaspa-safe-core), compiles the Silverscript contract with the chosen parameters (delay, alarm key, heir, inheritance mode, fee budget), and produces a P2SH deposit address. Private keys exist only in the browser's encrypted profile — the server receives public keys and signed transactions, never seeds or secrets.
The server runs a watcher loop that monitors vault UTXOs via gRPC to a Kaspa node. When a withdrawal is initiated, it sends alerts through every connected channel (Telegram, email, web push) — but the alarm key, which can cancel the theft, is held separately by the owner. The server can broadcast keyless paths after their conditions are met, but the covenant ensures funds go only to the pre-committed destination.
Kaspa Escrow applies the same architecture to P2P deals. Funds sit in the escrow.sil contract, not with a middleman. An AI mediator provides non-binding verdicts by analyzing the encrypted chat thread (E2E via the Kasia protocol — messages anchored in the blockDAG as ciph_msg: transactions). A human arbiter signs binding decisions with an offline key. The server relays ciphertexts but never sees plaintext.
The Desk holds everything together: a single HD-encrypted profile containing keys for vaults, deals, and the wallet — all derived from one master seed via HMAC-SHA512. The profile is encrypted under a user-chosen passphrase as an .age key-file, compatible with the standard age CLI. One backup covers all future vaults and deals created from that seed.
Try it: Kaspa Safe lets you create a covenant-secured vault in minutes — choose your delay window, assign an alarm key, and optionally set up inheritance. All on-chain, all self-custodial, no funds ever touch our servers. Create a vault →
Recovery Without the Service
The strongest test of non-custodial design is: what happens when the service disappears? Kaspa Forge's answer is layered:
1. .age key-file + passphrase — the encrypted profile, decrypted offline with any age-compatible tool. Contains the master seed and all vault/deal parameters. 2. vaultctl CLI — open-source Rust binary that reconstructs and broadcasts vault transactions against any Kaspa v2+ node. The same Silverscript source (vault.sil) is embedded in the binary and published on GitHub. Self-tests: 18/18. 3. recover.html — a single-file web guide for offline recovery.
The contract is the source of truth. The server is a convenience layer. If the server vanishes, the contract remains on the blockDAG and the CLI can interact with it directly.
Trade-offs and Honest Limitations
Covenant-based self-custody is not without costs:
- Compute budget. The vault contract allows a
COMPUTE_BUDGETof 20 for standard paths and 30 formigrate(the only two-checksig path). This node-enforced limit works today but constrains how much logic you can pack into a single covenant.
- Beta caps. Kaspa Escrow limits deal sizes to 50–10,000 KAS during beta. Below 50 KAS, dispute fees consume more than 10% of the deal. Above 10,000 KAS, the risk/reward of an unaudited contract is harder to justify.
migraterequires both keys. The vault's full-owner escape hatch needs the hot *and* alarm keys simultaneously. This means full power, but compromise of both keys is an instant, unrecoverable loss. Key separation discipline is essential.
- Oracle-free, not risk-free. Covenants enforce rules in code. If the code has a bug — or if the parameters (delay, heir address, fee budget) are set incorrectly — there is no customer support to reverse it. The feeBudget cap of 0.1 KAS protects against griefing, but not against owner misconfiguration.
- Watcher dependency for alerts. The covenant enforces the rules without any server, but the *alerting* layer (Telegram, email, web push) runs on Kaspa Forge's infrastructure. If the server is down, you still have the full delay window to act — but you will not be notified through our channels.
- AI mediator verdicts are non-binding. The escrow's AI mediator provides analysis, not enforcement. Only the human arbiter's cryptographic signature can trigger an arbitrated payout. The AI reduces workload but does not replace judgment.
These are not disclaimers buried in footnotes — they are the shape of the design. Proof-of-work settlement plus Toccata covenants give you trustless programmability, but "trustless" means the code *is* the contract. You are the auditor, the operator, and the keyholder. Kaspa Forge is the tool, not the guarantor.
FAQ
What is a covenant in Kaspa?
A covenant is a spending condition written into the UTXO itself. The Toccata hardfork activated covenants on Kaspa mainnet, allowing contracts like vault.sil to enforce rules — delay windows, key rotation, inheritance — without any third party.
Can I recover my funds if Kaspa Forge shuts down?
Yes. The vault is an on-chain contract. The open-source CLI tool vaultctl can broadcast every spending path against any Kaspa node. Your encrypted profile (.age key-file) plus the vault parameters are enough to move funds independently.
What prevents the escrow arbiter from stealing funds?
The escrow contract (escrow.sil) defines exactly three valid recipients: the buyer, the seller, and the fee address. No spending path — not even the arbiter's — can send funds anywhere else.
Do I need to run a node to use Kaspa Safe?
No. The browser wallet signs transactions locally, and the server relays them to the network. But if you want full independence, vaultctl connects to any public or private Kaspa v2+ node.
What is the feeBudget parameter in the vault contract?
feeBudget caps the maximum network fee that a keyless spending path (like complete or inheritAuto) can consume. It prevents a griefing attack where someone broadcasts a transaction that burns the vault balance as miner fee. The cap is 0.1 KAS.
Why does Kaspa use a DAG instead of a linear chain?
A blockDAG lets miners publish blocks simultaneously without orphaning each other. This supports higher block rates (currently 10 BPS) while maintaining security through the GHOSTDAG consensus protocol.
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
