@zanii/escrow
v0.1.1
Published
Conditional payments between agents as verifiable agreements - co-signed terms, a deterministic release verdict when a matching receipt lands in the log with an inclusion proof, and signed release/refund instructions. Zanii never holds funds: money moves
Readme
@zanii/escrow
Conditional payments between agents, as verifiable agreements — not held funds.
An escrow is three artifacts: terms both parties co-sign (who pays whom how much,
and the exact criteria a ledger receipt must meet), a deterministic release verdict
(does this receipt match every criterion AND carry a verified inclusion proof?), and a
signed settlement instruction authorized by whoever gives something up. The money
then moves on x402/on-chain rails — @zanii/x402 verifySettlement ties the tx back.
npm install @zanii/escrow @zanii/core @zanii/paymentsimport { buildEscrowBody, signEscrow, assembleEscrow, checkRelease, buildRelease, signInstruction, assembleInstruction, verifyInstruction } from '@zanii/escrow';
// 1. Terms — both sign. Amounts are integer minor units (floats are banned).
const body = buildEscrowBody({
payer: client.did, payee: worker.did, amount: '250.00', currency: 'USD',
criteria: { target: 'deploy.*', deadline: '2026-08-01T00:00:00Z' },
arbiters: { dids: [arb1.did, arb2.did, arb3.did], threshold: 2 }, // optional M-of-N
createdAt: now, ref: 'order-42',
});
const escrow = assembleEscrow(body, [signEscrow(body, client.did, clientKey), signEscrow(body, worker.did, workerKey)]);
// 2. The release verdict — pure computation, same answer for everyone.
// A receipt without inclusion evidence is a claim, not a trigger.
const verdict = checkRelease(escrow, receipt, { sth, index, proof });
verdict.ok; // receipt valid + matches EVERY criterion + Merkle-proven in the log
// 3. Settlement — signed by whoever gives something up:
// release → the payer (or the arbiter threshold); refund → the payee (or the threshold).
const rel = buildRelease(escrow, { receiptHash, ts: now });
const instr = assembleInstruction(rel, [signInstruction(rel, client.did, clientKey)]);
verifyInstruction(instr, escrow).ok; // authorized under the co-signed termsPython: from zanii.escrow import build_escrow_body, check_release, verify_instruction, ...
— byte-identical JCS objects and hashes.
Who authorizes what
| Instruction | Gives something up | Authorized by |
|---|---|---|
| escrow.release (pay the payee) | the payer's funds | payer — or M-of-N arbiters |
| escrow.refund (return to payer) | the payee's claim | payee — or M-of-N arbiters |
Arbiters must be neither payer nor payee (no self-arbitration), and signatures from DIDs with no role in the escrow are ignored, not trusted.
The limit, stated up front
Zanii never holds funds and cannot force a transfer. What this makes impossible is disputing what was agreed and what happened: the terms are co-signed, the triggering receipt is Merkle-proven, and the authorization to pay is signed. A payer who refuses to settle after a valid release is provably in breach — attributable, not impossible.
