testnet-10 · 2026-09-24

One job, two agents, and every step on chain

A coordinator took a job, split it, hired a specialist for each part, and paid each one for its own piece. The rules were enforced by Kaspa rather than by anybody’s good behaviour. This is the whole run, from the first transaction to the last — including the payment that settled and delivered nothing, and how that was recovered without paying twice.

Every figure on this page is read from one-job.json, written by the script that ran it.

The cast · covenant v4

Three grants, three keys, one of which can spend nothing

The coordinator signs the genesis spend and both delegations and never signs a purchase. Each worker signs its own purchase and nothing else. A fourth key — the stopper — can end any of it at any moment and receives nothing, and no agent here holds it.

All three run the covenant version above — read from the manifests, not typed here. A grant’s script is committed in its address, so a version is not an upgrade: it is a second covenant, and grants issued under the first keep running it until they are reissued. covenant/versions.json maps every fingerprint to its version, and each one is re-derived from its archived template rather than asserted.

C
CoordinatorTook the job, hired both workers, signed no purchase
Settled
It can still pay
0.71KAS
of 0.8 KAS budget
coin below authority: it holds 0.562 KAS, fees having come out of the balance
89%left
Per payment
0.05 KAS
Payees
2
Can delegate
2 deep
Enforced on chainb5338f8f3fbaa910…
R
RESEARCHBought a record about a repository
Settled
It can still pay
0.2KAS
of 0.25 KAS budget
80%left
Per payment
0.05 KAS
Payees
1 of 2
Per epoch
0.15 KAS
Paid 0.05 KAS to one address186570c82688f263…
V
VERIFYBought an audit of the covenant that paid it
Settled
It can still pay
0.18KAS
of 0.22 KAS budget
82%left
Per payment
0.04 KAS
Payees
1 of 2
Per epoch
0.12 KAS
Paid 0.04 KAS to one addresse92bd705f3123830…

The narrowing

Each worker may pay exactly one seller

The coordinator’s allowlist has two members. Each child was delegated down to one of them — and the child does not merely promise that. It commits to a node of the parent’s allowlist tree and carries the path from that node up to the parent’s root, so the chain can check the narrowing without ever seeing the list. That is why the roots below differ: a different root is the narrowing, visible rather than asserted.

  • C
    Coordinator
    allowlist root 8c5b3907aedb…payees 2may delegate 2 deep
    0.8KAS
    its whole budget
    • R
      RESEARCH
      allowlist root 27c1371f6316…may pay 254385aa03ab…cap 0.05 KAS
      0.25KAS
      delegated to it
    • V
      VERIFY
      allowlist root f873e14df1e3…may pay 8e5ec153d5e3…cap 0.04 KAS
      0.22KAS
      delegated to it

RESEARCH could not have paid the Auditor. VERIFY could not have paid the Researcher. Neither could have paid anyone else at any price, however their code was written.

The sequence

Eleven steps, seven transactions

Hiring is serial because a grant is one UTXO: the second delegation’s input is the first one’s output and does not exist until it confirms. Settlement runs backwards because the reserve chain is popped from the end — VERIFY was hired second, so it came home first, and a child settled out of order is refused before anything is built.

UTCStepTransaction
11:49:35Generated three keys
one coordinator, two workers; the stopper is a fourth nobody here holds
11:49:36Created the coordinator grant
0.8 KAS, two payees, may delegate 2 deep
b5338f8f3fbaa910…
11:49:51Opened the batch
a local record of what is committed to whom
11:49:52Delegated to RESEARCH
narrowed to 1 of 2 payees, proven by witness
e889c208983f033d…
11:50:09Delegated to VERIFY
narrowed to the other one, same way
a7f3ea9f5d1c32d9…
11:50:29RESEARCH bought its record
from the Researcher, at its listed price
186570c82688f263…
11:57:36VERIFY bought the audit
paid at 11:50, delivered at 11:57 — see below
e92bd705f3123830…
11:57:38Settled VERIFY
reserve released, the parent charged what it spent
1c6b956e4e601101…
11:57:54Settled RESEARCH
last hired settles first; the chain pops from the end
290e06f53a5d3936…
11:58:10Closed the batch
nothing outstanding
12:02:45Wrote the trace
this page is built from it

What was bought

Two different jobs, two sellers, neither told about this

RESEARCH bought a checkable record about kaspanet/silverscript from Warda’s Researcher. VERIFY bought an audit of covenant/warda_grant.sil from the Covenant Auditor — so the thing being audited is the thing that enforced the payment. Both services were already running and neither was told anything about this demonstration.

UTCWhatResultPayment 
11:50:24research https://github.com/kaspanet/silverscript
warda-growth.vercel.app
200 bought186570c82688…first try
11:50:29audit warda_grant.sil
warda-node.tailc0c0ec.ts.net:8443
502 paid-but-refusede92bd705f312…first try
11:53:58audit warda_grant.sil
warda-node.tailc0c0ec.ts.net:8443
409 paid-but-refusede92bd705f312…resumed
11:55:13audit warda_grant.sil
warda-node.tailc0c0ec.ts.net:8443
409 paid-but-refusede92bd705f312…resumed
11:57:35audit warda_grant.sil
warda-node.tailc0c0ec.ts.net:8443
200 boughte92bd705f312…resumed

The payment that settled and delivered nothing

VERIFY’s 0.04 KAS reached the Auditor and the report did not come back. The fault was the seller’s: its delivery callback read the covenant off an argument that carries the payment’s metadata, not the request, so it compiled undefined. The free endpoint never had the bug, which is why it survived — the paid path was the only one that could break and the only one never run end to end.

The seller records a payment as spent before it delivers, on purpose: recorded after, a crash leaves a payment that can be replayed forever. So the second attempt was refused as a replay, correctly. Only the operator could undo that — and the buyer never paid twice, because it keeps the proof it was issued and re-presents it rather than buying again.

Fixed, then redeemed: e92bd705f312… paid at 11:50:29 and delivered at 11:57:35 UTC, against the same proof. Four records of one payment, one delivery.

What the network would carry

The budgets are not about the job

KIP-9 storage mass counts 1/value over a transaction’s outputs, and a grant that pays out creates two: the payment, and the grant’s own successor. A covenant output carries a 32-byte binding that pushes it into a second storage unit and squares its weight, so the successor dominates. What costs is what the grant has left. A nearly-empty grant is the expensive one.

This is consensus, not the covenant, and no grant term relaxes it. The per-payment caps below are the sellers’ listed prices to the sompi; the budgets are the smallest amounts the network will let a worker pay out of, with headroom. Every transaction is massed before anything is built.

TransactionStorage massAgainst a ceiling of 500,000
delegate research185,32937% of the ceiling
delegate verify243,47648% of the ceiling
buy research262,22252% of the ceiling
buy verify318,18263% of the ceiling
settle verify00% of the ceiling
settle research00% of the ceiling

What this does not show

Three limits, all deliberate

It is not atomic

Everyone joining one agreement in a single step is the shape people usually describe, and it is what the covenant forbids: #[covenant.fanout(to = 2)] with require(OpAuthOutputCount == 2) fixes the topology at parent plus one child. Two workers is two delegations, in two transactions.

They are not even concurrent

A grant is one UTXO, so the second delegation’s input is the first one’s output. Hiring is serial by construction, not by scheduling.

The workers had to be known before the job existed

A grant’s payee set is fixed at genesis and a child may only narrow it, so a coordinator can hire from the cast it was created with and nobody else. An agent discovered at run time cannot be paid, at any delegation depth. That is the honest distance between this and an open market, and no amount of tooling closes it — it needs the covenant to change.

And one thing it does show that is easy to miss

The reserve came back. reabsorb requires newState.reserved == reserved - child.budgetTotal, so settling a worker restores the coordinator’s capacity to delegate again rather than consuming it for the grant’s whole life. The cost is ordering: child.reserved == 0 means a subtree settles from the leaves up, and a long-lived child at the bottom of the chain holds everything above it.