Kaspa Forge
Deep dive

How a Kaspa Transaction Travels from Your Browser to the DAG

2 Aug 2026 By OfficeForge's AI team · human-reviewed 14 min read
Kaspa Transaction Lifecycle: From Browser to Confirmation

When you press "Send" in a non-custodial Kaspa wallet, a chain of events unfolds that is structurally different from what happens inside a custodial exchange. No server holds your keys. No database tracks your balance. Your browser is the transaction factory: it discovers your funds, constructs the transaction, signs it with a key that never leaves the device, and hands off a signed blob to be relayed to the network.

This article traces that journey end to end — from UTXO discovery in the browser through the Kaspa blockDAG to on-chain confirmation — using Kaspa Forge's architecture as a concrete example.

The Problem Self-Custody Creates

In a custodial system, the service's server builds and signs transactions on your behalf. It holds your private keys, selects UTXOs, calculates fees, and submits the result. Simple for the user; risky for the user's money.

A non-custodial wallet reverses the trust model: your browser does all of the work. The server is a relay and a data source — it tells you what UTXOs exist and forwards your signed transaction to a Kaspa node — but it never sees your private key and never constructs the transaction for you.

This creates an engineering problem. The browser must be able to compile covenant scripts, build valid transactions, sign them with secp256k1, and serialize them in a format the node accepts. Kaspa Forge solves this with a single Rust codebase compiled to WebAssembly for the browser and to a native library for the server.

Step 1: Finding What You Can Spend — UTXO Discovery

Every Kaspa transaction starts with a question: *what do I own?*

Kaspa uses a UTXO model (Unspent Transaction Output), inherited from Bitcoin. Your balance isn't a single number in a ledger — it's a collection of discrete "coins," each locked to a specific address. When you receive 100 KAS, you don't get a balance entry; you get one or more UTXOs assigned to your address. When you spend 30 KAS from a 100-KAS UTXO, the protocol destroys the original UTXO and creates two new ones: 30 KAS to the recipient and approximately 70 KAS back to you as change.

Definition

UTXO (Unspent Transaction Output): a discrete, indivisible chunk of Kaspa locked to an address. Spend the whole thing, receive change as a new one. Your wallet balance is the sum of all your UTXOs.

Kaspa Forge's Desk wallet holds a master seed from which multiple addresses are derived (via HMAC-SHA512 domain-separated derivation). When the wallet opens, it queries the Kaspa node for UTXOs across all derived addresses:

// Pseudocode: UTXO discovery
addresses = [derive(seed, "wallet"), derive(seed, "walletOld"), ...]
utxos = node.getUtxosByAddresses(addresses)
balance = sum(utxo.amount for utxo in utxos)

The node's UTXO index responds with every unspent output on those addresses. For covenant vaults, each UTXO carries additional data — the covenant script hash — which determines *how* it can be spent. The Desk aggregates UTXOs from multiple derived addresses and presents a unified balance; when spending, the builder selects individual UTXOs from that set.

For vault monitoring, the watcher service also tracks UTXO snapshots to detect changes and trigger alerts: new deposits, initiated withdrawals, completed transfers.

Step 2: Building the Transaction in WASM

This is where Kaspa Forge's architecture gets interesting. The same Rust crate — kaspa-safe-core — compiles to both WebAssembly (for the browser) and a native library (for the server). Transaction construction logic is written once and verified once.

The browser-side build proceeds in several stages:

UTXO selection. The wallet chooses which UTXOs to spend. For a simple P2PK send, it picks the smallest set that covers the amount plus fees. For covenant operations, the selection is constrained — vault and escrow paths enforce a single-input invariant:

require(tx.inputs.length == 1);

Each covenant spending path (initiate, cancel, complete, checkin, inherit, release, dispute, etc.) consumes exactly one UTXO per transaction. The reason is security: if a covenant UTXO were combined with other UTXOs in a multi-input transaction, value from the smaller input could leak to miners as excess fees — a "siphon" attack. The single-input rule eliminates this class of vulnerability. For regular wallet sends, multi-input transactions are fine — the builder aggregates UTXOs from derived addresses and attaches one signature per input.

Output construction. The builder calculates:

  • The destination output (recipient address + amount)
  • The change output (back to sender, if applicable)
  • For covenants: a feeBudget — the maximum network fee the on-chain script permits, capped at 0.1 KAS. This prevents a scenario where a watcher or third party broadcasts a keyless covenant transaction with an inflated fee, draining the vault's value into mining rewards.

Script embedding. For covenant transactions, the builder embeds the compiled covenant script into the output. Kaspa Forge uses Silverscript to define spending rules — the vault source (vault.sil) and escrow source (escrow.sil) are compiled at build time and included directly in the WASM binary via Rust's include_str!. The scripts are parameterized: the same template produces different on-chain contracts depending on the hot key, alarm key, delay, heir, and other parameters chosen at creation.

Serialization — the txjson protocol. The constructed transaction is serialized into a JSON-compatible format for transmission from browser to server. A practical trap: Kaspa amounts are 64-bit integers (sompi), which exceed JavaScript's safe integer range (2^53 - 1). The txjson protocol encodes all amounts as strings to prevent silent precision loss. Any builder that runs amounts through JSON.parse without this safeguard will corrupt values.

Step 3: Signing in the Browser

The signing step is the core of non-custody. The private key exists only in the browser's memory — derived from the encrypted Desk profile, decrypted momentarily with the user's password, used to produce a secp256k1 signature, then discarded.

Kaspa Forge enforces a UX pattern called confirmPassword: when you initiate a withdrawal or sign a transaction, the wallet re-prompts for your password and shows the context (amount and recipient). This prevents a scenario where an unlocked session is hijacked by a malicious tab or extension.

For covenant paths that require multiple signatures — like the vault's migrate path, which needs both the hot key and the alarm key — the browser collects both signatures before submission. For the escrow's mutual path (buyer + seller agree), each party signs independently and the signatures are combined.

For keyless paths — like complete() (withdrawal after delay expires) or autoRelease() (escrow auto-release after the dispute window) — no signature is needed at all. Anyone can broadcast these transactions. In Kaspa Forge's architecture, the watcher service monitors the relevant conditions and broadcasts keyless transactions automatically, but the covenant ensures funds always travel to the correct destination regardless of who presses the button.

Step 4: From Browser to Node

The signed transaction leaves the browser as an HTTP POST to the Kaspa Forge server. The server:

1. Validates the transaction structurally — correct format, reasonable fee, known covenant parameters 2. Relays it to the Kaspa node via gRPC (submitTransaction) 3. Returns the transaction ID to the browser

The server never holds private keys and never modifies the transaction — it provides the network transport layer between the browser (which cannot speak gRPC) and the node (which accepts gRPC or wRPC). For keyless covenant paths, the server's watcher service builds and submits the transaction directly without any browser involvement.

Steps 5–7: Mempool, Block Inclusion, and Confirmation

Once the node accepts the transaction, it enters the mempool — a staging area for unconfirmed transactions. The node validates against consensus rules (correct signatures, valid UTXOs, sufficient fees, covenant constraints) and, on success, broadcasts the transaction hash to its peers.

According to the Kaspa wiki, a transaction submitted via RPC (from a wallet or service) never expires from the mempool — it is periodically rebroadcast. Transactions relayed peer-to-peer expire after 60 blocks if unmined. This matters for covenant keyless paths: if a complete() transaction is broadcast before the delay expires, it waits in the mempool until the condition is met, then miners can include it.

Block inclusion follows standard mining economics. When the mempool holds more transactions than fit in one block, miners select the highest-fee transactions first. Kaspa miners build blocks that reference multiple parent blocks in the DAG, and the GHOSTDAG protocol determines canonical ordering. A transaction included in one of these blocks is part of the DAG — but "confirmed" is a spectrum.

Confirmation and finality in Kaspa are measured in blue work. The more blue blocks (well-connected blocks, as classified by GHOSTDAG) that build on top of the block containing your transaction, the more secure it becomes. At 10 blocks per second, inclusion happens within roughly one second. But "confirmed enough" depends on the use case:

  • A small payment may settle in a couple of seconds.
  • A covenant vault withdrawal doesn't care about blue work accumulation — it enforces a delay measured in DAA score (thousands of blocks, typically hours or days) before the complete() path becomes valid. The on-chain script reads the UTXO's age — the difference between the current DAA score and the score when the UTXO was created.
  • An escrow dispute window (24–168 hours in DAA score) gives both parties time to contest before auto-release triggers.

How Kaspa Forge Uses This Pipeline

Every product on the platform flows through the same WASM core and server relay:

Kaspa Safe uses it for vault operations — 7 distinct spending paths, each with its own transaction builder compiled into the WASM core. The initiate path builds a covenant transition from VAULT to UNVAULTING mode; the keyless complete() path is broadcast by the watcher after the delay expires; the migrate path requires both hot and alarm signatures for instant, unrestricted exit.

Kaspa Escrow uses it for deal settlement — 10 spending paths covering release, refund, mutual agreement, dispute, arbitration, and timeout. The covenant ensures funds can only ever go to buyer, seller, or the fee address. The AI mediator and human arbiter resolve disputes, but neither can redirect funds.

The Desk wallet uses it for regular P2PK sends — aggregating UTXOs from HD-derived addresses, building standard transactions, and signing with the derived key.

All three products share the same Rust crate, the same server relay, the same encrypted Desk profile, and the same .age key-file backup format. The covenant scripts differ (vault vs. escrow), but the transaction construction and signing pipeline is identical.

See the pipeline in action. Create a Kaspa Safe vault and watch a transaction flow from your browser to the blockDAG — keys generated client-side, rules enforced on-chain. On-chain operations are free forever; you pay only the Kaspa network fee (≈0.0001 KAS per UTXO).

Create a vault

Trade-offs and Current Boundaries

WASM bundle weight. The kaspa-safe-core WASM binary includes the Silverscript compiler, secp256k1 cryptography, and all transaction builders. This adds to the initial page load (cached after first visit). The alternative — server-side construction — would break non-custody. It is a deliberate trade.

u64 precision in JavaScript. Kaspa amounts in sompi exceed JavaScript's safe integer range. The txjson protocol handles this with string-encoded amounts, and arithmetic happens inside the WASM module. Any integration that runs raw amounts through JSON.parse without string encoding will silently corrupt values — a real-world pitfall for every Kaspa web wallet.

Single-input means one vault deposit per transaction. The security invariant that each covenant path consumes exactly one UTXO means you cannot batch multiple vault deposits into a single withdrawal. Each deposit creates an independent UTXO that must be spent in its own transaction. Security over convenience.

The server relay is a soft dependency. If the Kaspa Forge server is down, the normal UI cannot submit transactions. Because the architecture is non-custodial, you can always use the open-source CLI tool (vaultctl) or submit directly to any Kaspa node. The server is a convenience, not a gatekeeper.

Confirmation thresholds are a judgment call. At 10 BPS, transactions enter the DAG almost instantly, but "confirmed enough" depends on the amount and context. Kaspa Forge's watcher uses configurable blue-score thresholds for its automated actions — there is no single number that fits every scenario. For covenant time-locks, the DAA score provides a deterministic clock independent of blue-score thresholds.

FAQ

How long does a Kaspa transaction take to confirm?

At 10 blocks per second, a transaction typically appears in the DAG within one second. "Confirmed enough" depends on your threat model — a coffee payment needs seconds, while a vault withdrawal enforces a delay measured in thousands of DAA-score blocks.

What is a UTXO in Kaspa?

A UTXO (Unspent Transaction Output) is a discrete chunk of KAS assigned to an address, like a bill in your wallet. You spend whole UTXOs and receive change as a new one. Your balance is the sum of all your UTXOs.

Why does Kaspa Forge use WebAssembly instead of JavaScript?

The same Rust crate compiles to browser WASM and native server binaries. Transaction-building and covenant-verification logic is written once and shared across both environments — no second implementation to drift out of sync.

Can a Kaspa transaction be reversed after it confirms?

No. Once a transaction is included in the blockDAG and sufficient blue work accumulates above it, it is practically irreversible. That is the fundamental guarantee of proof-of-work.

What happens if my browser crashes while signing?

If the transaction hasn't been submitted yet, nothing happens — no funds move. The Desk profile is encrypted locally; a crash doesn't expose keys. Reopen the page and try again.

How are Kaspa transaction fees calculated?

The base fee is approximately 0.0001 KAS per UTXO consumed. Covenant transactions add a configurable feeBudget (0.01–0.1 KAS) that caps the maximum fee the on-chain script permits.

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