Worked example · agentic commerce · mesh-llm
Paid inference between strangers: two books, one payment.
On mesh-llm, a node can pay another node it has never met to run a model, over Lightning, with no one in the middle. This page shows how evidence and payment fit together in that exchange. Every claim below says what it rests on, and links it where the source is public.
payments.v1 plugin and the payer's payment.lifecycle.v1 events (#2035, 25 Sep 2026). Not yet in a tagged release.The problem
One ledger cannot say why a payment is missing.
A provider serves a request and the payment never shows up in its wallet. From the provider's own records, three different stories look the same from its own records: an expired, unpaid invoice.
| What happened | What the payer's half shows | What the provider's half would show |
|---|---|---|
| Never paid | Terms accepted and an invoice received, then no settlement from its wallet. | The invoice lapsed, or a debt was recorded. |
| Paid late | Its wallet reported the payment, sealed after the invoice's expiry. | The invoice lapsed, then a late receipt arrived. |
| Noticed late | Its wallet reported the payment in time. | Its own wallet saw the payment only after its deadline check. |
Only the two halves together, joined on the payment_hash both wallets already share, separate the three. Today the payer's events are merged upstream; the provider's are proposed in #2017. The times come from each side's sealed record, and recording the payment events into those records is still in design.
The architecture
Each node keeps its own book. Nothing sits in the middle.
The requester pays and the provider serves. Each runs mesh-llm with two plugins: payments.v1, which holds the wallet and does the paying or invoicing, and the evidence plugin, which seals that node's own half of the exchange where the host serves it. Each side's half commits to digests of the exact request and response, and never to the text itself.
1 · Serve
The requester sends the request with the provider's advertised prices. The provider prefills, then sends an input invoice with the terms frozen. It may decode ahead, but it pauses delivery at 512 tokens until its own wallet sees the payment.
2 · Pay
The payer pays the input invoice under its own spending cap. Output streams, and the provider invoices the output it actually sent. The payer settles that under the original authorization. Each moment appears on the payer's payment.lifecycle.v1 stream.
3 · Seal and close
At the end of the stream, each evidence plugin seals its node's half. On our working branch the two halves are swapped, and the exchange reads CLOSED when both sides recorded the same request and response digests. Checkpoints cover the new records, and a witness registers them if you turned one on.
The lifecycle
Six phases, and who asserts each one.
Every event carries a source, so a reader can see who is making the assertion. A signature on an event never upgrades it: a provider's claim stays a provider's claim.
| Phase | What it records | Who asserts it | Status |
|---|---|---|---|
| terms_accepted | The payer accepted the frozen terms; the amount is its approved cap. | payer_asserted | merged · #2035 |
| input_invoice_issued | The provider's input invoice, as the payer received it (not independently verified issuance). | provider_asserted | merged · #2035 |
| input_settlement_observed | The payer's wallet reported the input payment succeeded. | wallet_reported | merged · #2035 |
| output_invoice_issued | The provider's invoice for the output it sent, as the payer received it. | provider_asserted | merged · #2035 |
| output_settlement_observed | The payer's wallet reported the output payment succeeded. | wallet_reported | merged · #2035 |
| final_accounted | What the payer actually paid, fees included. | payer_asserted | merged · #2035 |
The provider's half, proposed in #2017, uses the same event shape plus role: provider. It covers the invoice as actually issued (provider_asserted) and its own wallet seeing the payment arrive (wallet_reported). It also records when delivery paused and resumed at the token gate (host_asserted), a lapsed invoice, the output delivered, and a debt recorded or forgiven. A forgiveness is an operator action, so it goes on the record. proposed
What each party can show
Checkable by each side, without trusting the other's wallet.
The requester can show
- What it agreed to: a digest over the eight public fields of the terms, and nothing private.
- Whether and what its own wallet reported paying, keyed by
payment_hash. - Its sealed half of the exchange: digests of the exact request and response, and the token counts.
The provider can show
- Its sealed half of the exchange, including what it served and on which model file.
- With #2017, the invoices it actually issued, what its own wallet received, and when delivery was paused for payment.
- Which debts it recorded, and which an operator forgave.
Nobody takes on trust
- That the other side's record is the same: the halves match by digest, or they don't.
- That a record wasn't rewritten later: checkpoints commit to it, and a witness holds a copy of the checkpoint.
- Which side a claim came from: every event names its
source.
What no one can show from this, yet
- That the inference was correct, or that the prefill was honest. Upstream states that prices and token counts are the provider's claims, and that neither is checked.
- That a paid wallet belongs to any particular person or organisation.
- That every event was delivered. The stream is best-effort by design (an eight-event queue and a one-second timeout, and settlement never waits for it), so a missing event means incomplete evidence, not a failed payment.
What stays local
Prompt and response text, the invoice string, the payment preimage, wallet transaction IDs, and the private recovery ID never leave the node in these events or records. What travels is digests, amounts and payment_hash. Upstream notes that payment hashes are linkable metadata, so treat them that way.
For builders
Run it on two nodes
- Build mesh-llm from
mainon both machines.payments.v1merged on 25 Sep 2026 and is not in a tagged release yet (the latest release, v0.76.2, is from 14 Sep 2026). - Set up a wallet on each node as the upstream payments spec describes. Paid mode moves real money over Lightning. A paid provider must run a single-node text model.
- Install the evidence plugin on both:
mesh-llm plugins install action-state-group/capsule-emit-mesh@0.1.0
Each node now seals its own half of every exchange and checkpoints locally. Nothing leaves the machine unless you set a witness URL.
Not released yet: the provider-side events (#2017, proposed), swapping halves and the CLOSED state (on a public branch, not yet released), and the Evidence tab (our fork, not published). Recording the payment events into the book is also not released; it is in design.
For enterprises
Why this matters for agent payments
When your agents buy and sell work from parties you don't know, a dispute is settled by records, and one side's ledger is not enough to settle it. Here each party seals its own view at the moment of the exchange, and the two views join on an identifier both wallets already share, so "never paid", "paid late" and "noticed late" become checkable facts rather than accusations. No platform sits in the middle holding everyone's data: each party keeps its own book and shows only what a dispute needs.
When a dispute needs a second signature from someone who is neither party, that is what Countersign is for.
Claims table
Every claim on this page, and what it rests on.
Upstream links point at the mesh-llm pull request, issue or merge commit. Rows marked “not yet released” link a commit on a public branch that is not in a release yet. Rows marked “not yet public” cite a commit that hasn't been published, and the row says so.
| # | Claim | Status | Rests on |
|---|---|---|---|
| 1 | The mesh-llm host tells out-of-process plugins about each OpenAI exchange, including on the node that routed the request. | merged | #1437 · 6929c702887343af1b73296d7bd7f4aaa2db642f #1668 · 85b142df02fbb4c2555becd589ad7324296a8114 |
| 2 | That event carries digests of the exact request and response, plus serving provenance, so a half commits to digests and not text. | merged | #1841 · 6ffb2b711f081ca03bde7dc5bc9f8facec03da51 #1946 · 3060f3762aa92841be048113660eba07f2eab251 |
| 3 | The payments engine runs as the in-process payments.v1 plugin. It can be turned off, and with no engine installed the node runs free-only. | merged | #2035 · d06600ea0433a3271d0b51686ddb195f77a2336f |
| 4 | The payer emits payment.lifecycle.v1: six phases, each with a source of payer_asserted, provider_asserted or wallet_reported. | merged | paid_events.rs @ d06600ea0433a3271d0b51686ddb195f77a2336f |
| 5 | The terms digest covers eight public fields; the private recovery ID and the local peer are excluded. | merged | paid_events.rs @ d06600ea0433a3271d0b51686ddb195f77a2336f (terms_digest) |
| 6 | Events carry no invoice string, preimage, wallet transaction ID, prompt or response text, and payment hashes are linkable metadata. Delivery is best-effort: an eight-event queue, a one-second timeout, and settlement never waits for it. | merged | docs/specs/lightning-payments.md @ d06600ea0433a3271d0b51686ddb195f77a2336f |
| 7 | Two payments, input then output. Delivery pauses at 512 tokens until the provider's wallet sees the input payment. | merged | lifetimes.rs @ d06600ea0433a3271d0b51686ddb195f77a2336f (PRE_PAYMENT_OUTPUT_TOKENS) |
| 8 | An unpaid input invoice with nothing delivered lapses and does not block the peer. Delivered output left unpaid blocks the peer until it is paid or an operator forgives it. | merged | receivables.rs @ d06600ea0433a3271d0b51686ddb195f77a2336f |
| 9 | Prices and token counts are the provider's claims. Correct inference and honest prefill are not checked. | merged (stated) | docs/specs/lightning-payments.md @ d06600ea0433a3271d0b51686ddb195f77a2336f (Payment flow) |
| 10 | payments.v1 is not in a tagged release: the latest is v0.76.2 (14 Sep 2026), and #2035 merged on 25 Sep 2026. | fact | mesh-llm releases · #2035 |
| 11 | The provider's matching events use the same shape plus role: provider, including host_asserted delivery pause and resume, lapse, and debt recorded or forgiven. They join on payment_hash. | proposed | issue #2017 (open) |
| 12 | The evidence plugin installs with one command and seals each node's own half at the serving boundary. Local checkpoints are on by default, and the witness is off unless you set its URL. | released · ours | capsule-emit-mesh v0.1.0 · 55a3656b6b7eb9a296cc4f3e3110d894d2b35a7e |
| 13 | The halves are swapped at completion, and the exchange reads CLOSED when both sides' digests match. | public branch · not yet released | capsule-emit-mesh 08c63607d62cef434e6f1b33144a17177b94755b (on a public branch, not in a release) |
| 14 | The Evidence tab in mesh-llm shows each exchange's two halves and its CLOSED state. | our fork · not yet public | mesh-llm fork d30ee1f3a8959d09309059f0fc0edc7a21dbd6cd (unpublished) |
| 15 | The evidence plugin will read payment.lifecycle.v1 and record each payment moment with its source. The three missing-payment stories are read from those records. | designed | DESIGN-paid-inference-evidence.md @ v0.1.0 |
More worked examples, with reports you can open and check: Demos →