Kaspa Forge
Deep dive

How Kaspa Safe Watches Your Vault: Off-Chain Monitoring and Keyless Automation

19 Jul 2026 By OfficeForge's AI team · human-reviewed 11 min read
Kaspa Safe Watcher: Off-Chain UTXO Monitoring and Alerts

A covenant vault is an on-chain contract. It enforces a withdrawal delay, lets an alarm key cancel a theft in progress, and can pass funds to an heir after a dead-man timer expires. But a contract is passive. It can't send you a Telegram message when someone tries to move your coins, and it can't broadcast a keyless completion transaction the moment a delay window opens.

Someone — or something — has to watch the chain. In traditional finance, the bank handles monitoring. In a non-custodial system the on-chain rules *are* the bank, and an off-chain watcher bridges the gap between those rules and real-world awareness.

This article explains how Kaspa Safe's monitoring layer works: how it detects state changes on a BlockDAG, what triggers an alert, and why the watcher can broadcast transactions on your behalf without ever holding a key.

The Problem a Covenant Can't Solve

The vault covenant (vault.sil) defines seven spending paths, each with distinct signature requirements and state transitions. When someone broadcasts an initiate transaction against a VAULT-mode UTXO, it transitions to UNVAULTING and a countdown begins.

But Kaspa's consensus layer doesn't generate notifications. There are no webhooks, no event buses, no push APIs built into the protocol. A node validates transactions and propagates blocks; it doesn't tell you that *your* vault just changed state.

Without a watcher, the vault owner would need to manually check their vault address — repeatedly, indefinitely. They'd need to calculate elapsed DAA scores, remember to broadcast complete() after the delay, and hope they notice a theft attempt in time to use the alarm key. A vault you forget to monitor is a vault that doesn't fully protect you.

How the Watcher Works

Definition

Watcher — an off-chain service that polls a Kaspa node for UTXO state at registered vault addresses, compares each result to a stored snapshot, and dispatches alerts or keyless transactions when on-chain conditions change.

Registration: keys stay in the browser

When a user creates a vault, the browser-side WASM core generates keys, compiles the covenant with the chosen parameters (delay, alarm key, heir, inheritance delay, fee budget), and derives the vault address. To enable monitoring, the user registers the vault with the service — sending only the public parameters: vault address, hot public key, alarm public key, delay value, heir key, and fee budget.

No private key, seed, or encrypted profile data ever leaves the browser. The watcher stores what it needs to *interpret* on-chain state, not to *control* it.

Polling the UTXO set

Every 10 seconds, the watcher queries the Kaspa node via gRPC, requesting all UTXOs at each registered vault address. This uses the node's utxoindex — an address-to-output reverse index. Each UTXO in the response carries its amount, the encoded script (which embeds the covenant state including mode and dest), and its blue score: the DAA score at which the UTXO was accepted into the DAG.

Blue score matters because it determines the UTXO's *age* — the current network DAA score minus the UTXO's blue score. Age drives every time-based condition in the vault: the withdrawal delay, the inheritance timer, and the checkin mechanism. The Kaspa wiki explains how blue score and DAA score relate to block classification and the ordering that makes this calculation possible.

Snapshot diffing

The watcher doesn't just read current state — it compares each poll result against the stored snapshot from the previous cycle. The diff reveals exactly what changed:

prev = snapshot[addr]
curr = node.get_utxos(addr)

new_utxos  = curr \ prev    →  deposit alert
gone_utxos = prev \ curr    →  withdrawal record
for each utxo still present:
    if utxo.mode changed   →  state transition alert
    if age crossed delay   →  broadcast complete()
    if age crossed 80%     →  send checkin reminder

In practice the events map onto the vault's state machine:

Diff patternWhat happenedWatcher action
New UTXO appearedDeposit to vaultNotify owner
UTXO disappearedFunds moved (completed, inherited, migrated)Record, notify
Mode 0 → 1Withdrawal initiatedHigh-priority alert — alarm key can still cancel
Mode 1 → 0Withdrawal cancelledNotify — threat over
Same mode, age resetCheckin broadcastAcknowledge
Age crossed delaycomplete() now availableAuto-broadcast
Age crossed inheritDelayinheritAuto() now availableAuto-broadcast
Age ≥ 80% of inheritDelayCheckin deadline approachingRemind owner + heir

The diff-based approach means the watcher only reacts to *changes*, not to the same state re-read every cycle. This keeps alert volume low and avoids false positives.

Multi-channel alert dispatch

When a meaningful event is detected, the watcher sends alerts through every connected channel simultaneously:

  • Telegram bot — the most immediate channel. The user links their account via a one-time code. Messages are structured: event type, vault identifier, available actions, and time remaining.
  • Email — for the owner and, if configured, the heir. Inheritance reminders, withdrawal confirmations, checkin nudges.
  • Web Push (VAPID / RFC 8291) — browser notifications. Requires the user to grant permission on the site; on iOS/iPadOS, only works from the Home Screen PWA.

Redundancy is intentional. Telegram might be muted, email can land in spam, and Web Push might not be granted. Sending to all three maximizes the chance that at least one alert reaches the owner during an active alarm window.

Keyless broadcasting: complete() and inheritAuto()

This is the part that might seem paradoxical: the watcher broadcasts transactions without having any keys.

Two covenant paths require no signature at all:

  • complete() — available when the vault is in UNVAULTING mode and age ≥ delay. The output goes *only* to the destination address locked during initiate. The covenant enforces this.
  • inheritAuto() — available when auto-inheritance is enabled and age ≥ inheritDelay. The output goes *only* to the registered heir's key as a P2PK payment.

These paths exist precisely so that *anyone* can broadcast them. The funds go exactly where the covenant says. The watcher is a reliable, prompt broadcaster — like a smart alarm clock that rings at the right time but can't change where you're going.

How Kaspa Forge Runs the Watcher

The watcher runs as a background task alongside the Kaspa Safe API. On each cycle it:

1. Loads all registered vault addresses and their last-known UTXO snapshots from the local database. 2. Fetches current UTXOs for every address in a single batch query to the node. 3. Diffs each vault's current state against its snapshot. 4. For each detected event: updates the snapshot, dispatches alerts to all connected channels, and — for keyless paths whose conditions are now met — constructs and broadcasts the transaction. 5. Writes updated snapshots back to the database.

Here's what that looks like in a real scenario:

A user creates a vault with a 24-hour delay and an alarm key stored on a hardware wallet. Three weeks later, their hot key is compromised. The thief broadcasts initiate. Within one polling cycle, the watcher detects the mode change and fires a Telegram alert and an email. The user opens Kaspa Safe, sees the pending withdrawal, and broadcasts cancel with their alarm key — which lives outside the browser. The UTXO returns to VAULT mode. The watcher confirms the cancellation across all channels.

Months later, the user stops checking in. The watcher notices the age approaching 80% of the inherit delay and sends a reminder to the owner and a heads-up to the heir's email. No checkin comes. When the threshold is crossed, the watcher broadcasts inheritAuto(). Funds arrive at the heir's address — exactly as the contract specified when the vault was created.

Try Kaspa Safe — create a non-custodial vault with withdrawal delays, an alarm key, and optional inheritance. On-chain operations are free forever; the optional Telegram alert subscription is 100 KAS/year with a 30-day trial. Create a vault →

Create a vault

Trade-offs and Honest Limitations

Polling latency is bounded by the interval. An event can go undetected for up to one cycle. For a vault with a 24-hour withdrawal delay, a 10-second gap is negligible. The minimum delay is set with this headroom in mind.

**The watcher is a single point of *alert* failure, not *security* failure.** If it goes down, no alerts arrive and no keyless transactions are auto-broadcast. But the vault is untouched — the covenant enforces every rule on-chain regardless. The alarm key works via direct RPC. The open-source vaultctl CLI can broadcast any keyless path against any Kaspa v2+ node. Recovery without the service is always possible.

Push delivery isn't guaranteed. Web Push on iOS requires the Home Screen PWA. Telegram depends on the user not muting the bot. Email can land in spam. Multi-channel dispatch mitigates this, but doesn't eliminate it. For critical alerts — an active theft — the user should keep at least one channel actively monitored.

Polling adds load to the node. Each cycle queries the UTXO index for every registered vault. With hundreds of vaults this is non-trivial; the node's utxoindex must be enabled and the query frequency balanced against capacity. On a 10 BPS network the UTXO set changes rapidly, but address-specific lookups remain fast.

Single-instance architecture. Snapshots and registration data live in a single database. This is simple and reliable at current scale, but there's no automatic failover. A future high-availability setup would need shared state or snapshot replication — straightforward engineering, not a fundamental design problem.

No real-time streaming. Kaspa's node supports gRPC streaming, but the watcher uses polling for simplicity and resilience to reconnections. This is a pragmatic choice: the relevant timers are measured in hours and days, not seconds.

None of these trade-offs compromise the vault's security model. The covenant is the security boundary. The watcher is a convenience layer that makes the covenant *practical* — bridging the gap between on-chain rules and the reality that humans don't sit refreshing a block explorer.

FAQ

What does the Kaspa Safe watcher do?

It polls a Kaspa node every 10 seconds, compares vault UTXO states against stored snapshots, and reacts to changes — sending alerts via Telegram, email, and Web Push, and broadcasting keyless transactions when covenant conditions are met.

Does the watcher hold any private keys?

No. It stores only public vault parameters: the address, delay, public keys, and heir configuration. It can broadcast keyless paths because the covenant locks the output destination — no signature is needed.

What happens if the watcher goes offline?

Your vault stays safe on-chain. You lose automated alerts and keyless broadcasts, but the alarm key works via direct node RPC, and the open-source vaultctl CLI can broadcast keyless paths against any Kaspa node.

How does the watcher detect a new deposit?

Each cycle it queries all UTXOs at the vault address via the node's gRPC interface, then diffs the result against the stored snapshot from the previous cycle. A UTXO that wasn't there last cycle is a new deposit.

Can the watcher steal my funds?

No. Every covenant spending path either requires a signature from your key or hard-locks the output to a specific destination registered at vault creation. The watcher has no ability to alter destinations.

Why poll instead of subscribing to node events?

Polling is simpler and more resilient to reconnects on Kaspa's high-BPS network. The trade-off is up to one polling interval of latency — acceptable when covenant delays are measured in hours or days.

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