Lightning Stream · TestsTests and numbers
What was run, on what, and what each run does not show. Every figure on this page was measured on a regtest, a private test network, between our own nodes, unless it says it was computed or simulated, or is in the two sections on XBT mainnet: the first rehearsal, and the released app between our own nodes.
This page accompanies Draft 11 of the Lightning Stream whitepaper, 27 October 2026. Back to the whitepaper.
1Evidence anyone can check
The outside review pointed out that the measurements could not be checked by anyone outside the project. Three things have changed. Public CI builds the tree with its locked dependencies and runs its unit and integration tests on every push and pull request, on GitHub; those tests need no Lightning node, and the lab scripts are not part of it. It passed at the commit Draft 9 described, aaad6da. The reports the lab scripts wrote on their latest runs, on 14 October 2026, are in the tree's reports/ folder, as they were written: every check with what it saw. One of them has a failed check, published with it: in the wallet's end-to-end script the part that kills each process in turn timed out before it reached the seller's node. They are our own runs on one machine, and the figures on this page were mostly written from earlier runs of the same scripts, so a time or a lottery's wins differ between the two. And the tree has a guide for an outside tester, docs/TESTING.md, written from a first run from a fresh clone on a clean Ubuntu 24.04 machine, made as a stranger to the project would make it: the workspace's 707 tests passed, and so did every check of the three lab scripts the guide asks for (the wallet paying a gateway with tickets, 35 of 35; the lottery, 30 of 30; the film demo, 22 of 22). The Lightning lab scripts need Docker and python3 and fetch the rest themselves, so anyone can run them; the scripts for pay with work still need parts that are not published.
2Tickets in the lab
The first table was measured on a three-node regtest lab, one hop: a buyer, one routing node and a seller, all running Lightning Fork v0.21.3-beta-blake2b.17 [61] with default settings, in the regtest lab its author publishes (lightning-fork-lab at commit a5cadc5, on a Bitcoin Knots 29.4.1 BLAKE2b regtest [60]), on one machine, with two channels of 5,000,000 sat. The nodes were driven by scripts, not by the wallet, and all traffic was on loopback, so no figure includes real network latency. A script in the public tree, scripts/ln_ticket_regtest.sh, now reruns these cases on four nodes. Its figures differ a little from the ones below and are in the tree's docs/TICKETS.md.
| Question | Measured on a three-node regtest lab, one hop |
|---|---|
| Does a stock Lightning Fork node accept tickets through a routing node? | Yes, with no change to it. Collecting a ticket returned the buyer's own secret as the payment's proof. |
| Per request, on the seller | Hash the secret and look up the ticket: 0.93 ms median. Collect: 3.5 ms. The buyer's node showed the payment complete 11 ms after the request arrived. |
| Time to lock tickets of 1,000 sat | 1 ticket: 0.27 s. 10: 0.59 s. 100: 3.0 s. About 45 tickets a second after the first quarter second. |
| Smallest payment, and smallest held ticket | The smallest payment a node carried was 1 sat; a smaller one found no route. The smallest ticket the design holds is 546 sat, by the dust rule in the next table. |
| Routing fee | 1 sat plus one part per million for each collected ticket, per hop, at the default policy. Nothing for a cancelled ticket: with 10 locked and 5 collected the buyer paid 5,005 sat and the seller received 5,000. |
| How many at once, between Lightning Fork nodes | 483 in one channel, the most the protocol allows and Lightning Fork's default. Of 520 offered at once, 483 were accepted and 37 failed cleanly on the buyer's node. A channel with a Core Lightning node at either end carries 30; see the next table. |
| Cost of many tickets in a channel | Each ticket above the dust limit (354 sat on these Lightning Fork nodes) adds an output to the channel's commitment transaction. The fee its opener would pay on a force close rose from 285 sat with no tickets to 21,304 sat with 483, at 1 sat/vB. |
| How long a ticket can be held | A ticket's life must be more than 24 blocks: 20 was refused and 30 accepted. The seller's Lightning Fork node cancels a held ticket 18 blocks before its expiry. With a life of 40: locked at height 159, expiry at 202, cancelled at 184. |
| A restart with 4 tickets locked | Seller, routing node and buyer in turn: each was back in 1 to 1.5 s with its tickets still locked. Two were then collected and two cancelled, correctly. |
| A routing node that is down | The seller's collect returned in 4 ms and its invoice read settled. The buyer's node showed the payment complete as soon as the routing node was back. |
| A seller that neither collects nor cancels | Its node cancelled the tickets 25 blocks after the lock (40-block tickets). Every channel stayed open. |
| A seller that goes offline with tickets locked | The routing node force-closed its channel to the seller 43 blocks after the lock. The buyer's money came back 8 blocks later, 51 blocks in all. The buyer's own channel stayed open. |
| For comparison: one Lightning payment per piece | Direct channel: 110 ms median. Through the routing node: 210 ms median, with a fee of 1,001 msat on 1,000 sat. |
Not measured: more than one hop; real network latency; the fees and routes of mainnet; tickets on a direct channel with no routing node; a force close with hundreds of tickets in the channel (in this lab it was run with 3); and a seller claiming a ticket on the chain because the routing node stayed down. The four-node script has since run a force close with 100 tickets, which is the figure in the table below. The demo has since paid tickets through two routing nodes.
With Core Lightning: slots and dust
These come from a later run, on 12 October 2026, on regtest, on one machine, in the same lab: three Lightning Fork nodes of the version above and three nodes of the Core Lightning fork [63] at its release v26.06.9-blake2b.7, the released binaries. Two of the Core Lightning nodes were started with the values that binary has by default on mainnet (30 slots among them), because on regtest the same binary defaults to 483; the third kept 483, for the dust cap. The channels were of 5,000,000 sat, one of 2,000,000, all of the anchor type. A Core Lightning seller held tickets through a small plugin of our own. The driver of this run is not in the public tree, so treat the figures as our own claim; the tree's docs/TICKETS.md, section 11, has them too.
| Question | Result |
|---|---|
| 40 tickets of 1,000 sat to a Core Lightning seller on one channel | 30 locked and 10 failed. The buyer's node reported its balance as short, though its side of the channel had 3,998,056 sat. |
| An ordinary payment of 5,000 sat over that channel while the 30 were held | Failed the same way. Left free to choose a route, it went round through another node for a fee of 1,005 msat. |
| 40 more tickets, the buyer free to choose a route | All 40 locked, over two more of the seller's channels. The seller then held 70 tickets on three channels, two of them full. |
| 40 tickets through a Core Lightning routing node | 30 locked and 10 failed. |
| Two buyers through one Core Lightning routing node to one seller | The first held 20. The second offered 20: 10 locked and 10 found no route. |
| A Core Lightning node as the buyer's node | It paid a Lightning Fork seller's ticket through one routing node for a fee of 1,001 msat, and the proof it got back was its own secret. At its own limit its node names the cause: "Too many HTLCs". |
| Time to lock 1, 8 and 30 tickets through one routing node, invoices made first | To a Lightning Fork seller: 0.21 s, 0.28 to 0.29 s, 0.56 to 0.64 s. To a Core Lightning seller: 0.22 s, 0.25 to 0.31 s, 0.54 to 0.57 s. |
| Which held tickets have an output on the chain | On a Lightning Fork node, those of 354 sat and above. With tickets of 400, 600 and 1,000 sat held, a Core Lightning node's own force close had outputs for 600 and 1,000 only: its limit is 546 sat. |
| Core Lightning's cap on dust in one channel, on a channel it opened with 483 slots | Tickets of 500 sat: 100 locked (50,000 sat) and 30 failed. Tickets of 546 sat: 130 of 130 locked. |
| The next ticket to that seller after a refusal at the cap | A 1,000 sat ticket found no route: the buyer's node had stopped trying that seller. |
| Lightning Fork's cap on dust | Not reached: 483 tickets of 300 sat locked, 144,900 sat below the dust limit. |
| 200 payments of 64 sat on a 30-slot channel, nothing held | Sent 64 at a time: 61 succeeded and 139 failed, in 3.0 s. Sent 8 at a time: all 200 succeeded in 7.1 s, 28 a second. |
| A Core Lightning seller that neither collects nor cancels (40-block tickets, direct channel) | Nothing cancelled the tickets. At their expiry height the buyer's node began to force-close its own channel to the seller, and the close was in the chain 3 blocks later. |
Not measured here: what XBT's nodes on mainnet are set to (an operator can raise Core Lightning's 30 or lower Lightning Fork's 483), the channel types in use there and the fee rates; channels without anchors, where the dust limit also rises with the fee rate (computed under Fee rates, not run); two or more Core Lightning routing nodes in a row; and the two published hold plugins beyond a first try of one and a reading of the other. Who bears the loss of a ticket below the dust limit in a force close is reasoned from the protocol's rule, not run.
3The lottery as built
These come from a run on 13 October 2026 of scripts/lottery_regtest.sh, which is in the public tree and makes 30 checks. It ran on a regtest lab, our own nodes: the wallet's signer, its tool server and the gateway as the tree builds them, an ordinary file server, and three Lightning Fork nodes of the version above (a buyer, one routing node, a seller) on one machine, on loopback. The buyer's channel and the seller's inbound channel were opened with 30 slots. There was no Core Lightning node. The settings were the ones the wallet and the gateway ship with, with pieces of 64 KiB. These are one run's figures, and a roll is random, so the wins differ from run to run.
| Question | Measured on a regtest lab, our own nodes |
|---|---|
| A 100 MB file at a quarter of a sat a piece (1,526 pieces, 381.5 sat), bought five times by an agent's one call to the wallet | Whole each time, in 1.2 to 2.3 s. 8, 12, 6, 4 and 5 wins, where 6 were expected each time: 520, 780, 390, 260 and 325 sat paid, fees included. |
| What the buyer's node paid, what the seller's node received, and what the wallet booked | 2,240,000 msat on both nodes: 35 wins of 64,000 msat, each paid once. The wallet's ledger: 2,275 sat, each win and its fee of 1 sat. |
| A win, from the start of its payment to its end, one at a time | 221 ms median, 267 ms at the longest (35 payments, one routing node). |
| What was pending on the buyer's channel during the downloads, looked at every 20 ms | Nothing in 47% of the looks, never more than 3 payments, and every one of them a win on its way. Nothing when the downloads were over, and nothing held on the seller's node. |
| A page priced at 2.5 sat a request, bought 200 times | One roll each, 7 wins where 7.8 were expected. 3.0 ms a call, against 1.5 ms for a free page through the same wallet (medians). |
| 20 sellers paid at once, three files of 64 one-sat pieces from each | 3,840 rolls, 68 wins where 60 were expected, every file whole, no payment failed, 4.8 s. At most 8 wins on the channel at once. An ordinary payment of 5,000 sat from someone else over the same channel went through meanwhile in 0.5 s. |
| 100 sellers paid at once, the same | 19,200 rolls, 288 wins where 300 were expected, every file whole, no payment failed, 20 s: 14 wins a second. At most 8 wins on the channel at once; the ordinary payment went through in 0.5 s. A win was on its way for 0.44 s at the median and 1.2 s at the longest. |
| 100 sellers paid at once on a busy machine: 16 busy loops beside the lab, on its 16 cores (other runs of the same day) | With wins paid strictly in the order found, a seller cut a file short in 12 of 15 runs, each time with every win paid afterwards; on an idle machine, in none of 13. With the wallet paying first the wins a seller's answer stands still for: every file whole in 5 runs of 5. |
| A buyer that sends no halves of its own | Served 8 pieces. Then its answer was cut and its next request refused. |
| A buyer that rolls and never pays, pieces of a quarter of a sat | Served 1.4 wins' worth on average before it was stopped (ten sessions; 1.2 at the median, 3.3 at the most; 0.8 to 1.4 on average over four runs). |
| The same, pieces of a quarter of a win | 3.9 wins' worth on average (ten sessions; 3.9 to 4.9 over four runs): the gateway bears two unpaid wins and serves 8 pieces ahead of the buyer. |
| A second program for the roll, written separately, playing that buyer | The wins it worked out were exactly the seller's, in every session. |
| A seller that claims a win for a roll that lost, with a real invoice | Refused by the wallet's own working of the roll. Nothing paid, and the session ended. |
| A seller that sends, for a real win, an invoice that pays another node, or one for 1 sat more | Refused by the checks every invoice passes. Nothing paid, and the session ended after the third such invoice. |
| A seller that sends two invoices for every win | 64 wins, each paid once. None of the 64 second invoices was paid. |
| The wallet's signer, the gateway, the buyer's node and the seller's node, each killed between two calls and again in the middle of a download | In all eight cases: no win paid twice, nothing in the ledger that the node did not pay, nothing paid that is not in the ledger, and a later call was served. |
Not measured for the built lottery: sellers on separate machines and nodes (the 100 sellers were 100 gateways beside one seller node); more than 100 sellers; more than one hop, and a direct channel with no routing node; real network latency and mainnet fees; a Core Lightning node; and anything on a small computer. When the buyer's node was killed in the middle of a download, one to three of its payments stayed marked as in flight on that node for good: the wallet keeps those wins booked as spent and does not pay them again, so the seller is not paid for them. The tree's docs/LIMITS.md states this and the other limits under "The lottery".
Building it changed the design, and running it found faults that the tests had not. The plainest: the wallet's limit of 8 payments at a time first let a win go as soon as it was sent, as is right for a ticket. With 100 sellers, 24 wins were then pending at once on the 30-slot channel, and 47 of 313 payments failed. A win now stays inside the limit until it has been paid. The tree's docs/TICKETS.md, section 9, lists what building it changed and what the lab found.
A file cut short with 100 sellers. A later fault showed only when the machine was busy. The gateway bears two unpaid wins from a buyer. At a third, the answer it is sending stands still for the payment, for 10 s at the most, and is then cut short. A file of 64 pieces at one win expected draws three or more wins 8 times in 100, and its answer then stands still at the third until the first is paid. The wallet paid wins strictly in the order it found them, all sellers together, so the first of those three waited its turn behind the other sellers' wins: about 7 s when the machine did nothing else (100 wins at 14 a second), which is inside the 10 s, and more than 10 s when it was busy. The seller then cut the file, and every one of its wins was paid a moment later. With 16 busy loops beside the lab, on its 16 cores, a file was cut in 12 of 15 runs of the 100-seller check; on an idle machine, 13 runs cut none. The fault was not new: it was also there in the tree the lottery was first merged in. The wallet now pays first the wins a seller's answer stands still for. It knows which they are by its own count against the seller's terms, and takes no seller's word for it. Under the same load, every file was whole in 5 runs of 5, and the whole lottery script then passed ten runs out of ten. It is a change of order only: which payments are made, the limit of 8 at a time and every check of an invoice are as before. These are the numbers of one lab on one day (regtest, one machine, our own nodes, one routing node), and the load measures nothing but itself. What the two waits bound beyond that is reasoned from the lab's rate and was not run past 100 sellers: a win no answer waits for would reach the seller's 30 s of grace with about 420 wins waiting, and some 140 wins of the urgent kind at once would use up the 10 s. A slower machine or a slower route lowers the rate, and those numbers shrink with it.
In the design's lab a roll of the first form cost about two millionths of a second for both sides together. As built, a win was paid 221 ms after its payment began (median of 35 payments through one routing node), and each win paid that node 1 sat, which is 1.6% of a 64 sat win. A page bought by a roll took the wallet 3.0 ms a call, against 1.5 ms for a free page: a roll is one more request (on a regtest lab, our own nodes). A roll of the second form cost 16 millionths of a second for each side and 200 to 600 bytes; in 20 runs out of 20 the seller collected its win alone, and the buyer's node showed the payment complete 13 ms after the win. Numbers has the rest.
4The design's measurements: the lottery and many sellers
These are older than the table above, and were made before anything of the lottery was built. They come from three runs, all on regtest, on one machine, on loopback, with Lightning Fork nodes of the same version in the same lab. Tickets to many sellers and the pay-after-win lottery ran on five nodes: a buyer, one routing node and three seller nodes. Up to 200 seller sessions were spread over those three nodes, and a request and its answer were calls inside one program; every invoice, payment, lock, collect and cancel was a real call on a node. The win that pays itself ran on three nodes, with its roll in a prototype of our own. The scripts for these runs are not in the public tree, so treat the figures as our own claim. Rows marked simulated involved no node.
| Question | Result |
|---|---|
| Tickets in flight from one channel, every channel with 483 slots | 400 tickets, 2 each to 200 seller sessions on three nodes, locked in 11.0 s. Offered 600 over two channels: all 600 locked, 483 on one and 117 on the other, in 9.2 s. This is what one Lightning Fork channel and its router accept, not a number of sellers a route carries. Within half of a 30-slot channel the figures are 7 sellers at 2 tickets each, or 15 at 1. |
| A new seller appears: until it holds a ticket | 248 ms median through one routing node (30 runs); 0.11 s over a direct channel. |
| One request that pays many sellers | Every seller's collect had returned after 28 ms for 10 sellers, 149 ms for 50 and 809 ms for 200. The buyer's node showed every payment complete after 0.2 s, 0.8 s and 2.9 s. |
| A force close with 100 tickets of 1,000 sat | The routing node paid 4,635 sat in fee for the closing transaction and about 29,000 sat in all to get its funds back, at 1 sat/vB. With 3 tickets: 1,530 sat. |
| One roll, pay after a win | 2.3 millionths of a second for both sides together, in Python. |
| A win, until the seller is paid (pay after a win, one routing node) | With 24 sellers at 400 pieces a second, odds of 1 in 640 and wins of 64 sat: 218 ms median, 255 ms at the longest, 30 wins. With 96 sellers and odds of 1 in 32: 284 ms median, 509 ms at the longest, 520 wins. Every payment succeeded. |
| Many sellers win in the same instant | All paid after 0.76 s for 24 sellers, 2.3 s for 100 and 4.6 s for 200. |
| Routing fee for a win | 1 sat for each routing node: 1.56% of a 64 sat win. |
| A buyer that never pays (simulated) | It is served 1.00 win's worth before its first win on average, 2.98 one time in twenty. |
| One roll, a win that pays itself | 16 to 19 millionths of a second for each side and 197 to 581 bytes, for odds from 1 in 16 to 1 in 65,536 (a prototype in Rust, one processor core). |
| A buyer that offers rolls with no key in them | Caught on 49.9%, 50.0% and 50.2% of 200,000 rolls at odds of 1 in 16, 64 and 1,024. No win got through. |
| A win that pays itself, on three nodes | A 64 sat ticket locked through one routing node (209 ms), rolls at 1 in 64 until the seller won: the seller collected in 20 runs out of 20. This run was made before the dust rule; as designed now, that ticket is at least 546 sat. Its collect call took 2.4 ms and the buyer's node showed the payment complete 13 ms after the win. Paying the same win afterwards with an ordinary payment took 217 ms. |
| The proof that ties a ticket's lock to its rolls | 254 ms to make on one processor core, 1.35 ms to check, 192 bytes (a Groth16 proof [54] made with arkworks [57]). |
Not measured in these design runs: sellers that are really separate machines; more than one hop; real network latency and mainnet fees; requests over HTTP, and a buyer that does not pay on real nodes (both are in the table of the built lottery above); anything on a small computer; the hardened form of one building block that the self-paying win needs, where the prototype uses the simple form; and a proof that needs no trusted setup.
5A demo: a film paid for as it plays
This paper opens with one buyer and one seller and a streamed film. That case now runs, as a demo, on a regtest. A node runner's media server, Jellyfin as it comes, stands behind the seller's gateway, and a film is bought from it piece by piece through the wallet while a player reads it. Nothing is installed into Jellyfin and none of its settings change. The same file is then bought in pieces from 20 gateways at once, paid by the lottery. A script in the public tree, scripts/demo_film.sh, runs all of it, and the tree's docs/DEMO.md is the guide for a node runner, with the seller's whole setup.
The conditions. Every figure in this section is from one run on 13 October 2026: on a regtest, on one machine, on loopback, between our own Lightning Fork nodes, with channels that carry 30 pending payments. Nothing ran on mainnet or between two people. On one machine everything is a loopback away, so the times say what the software adds and nothing about a real network. Jellyfin was version 10.11.0. The film is Big Buck Bunny, (c) 2008 Blender Foundation, www.bigbuckbunny.org, licensed under Creative Commons Attribution 3.0: 276 MB, 10:34 long, 1080p, 3.5 Mbit/s. It was priced at 100 sat a MiB and paid in tickets of 1,000 sat. That price is a number chosen so that a ticket lasts about half a minute of this film. It is not a market price. The rules of tickets and of the lottery were the shipped ones, but the demo's driver changes some other settings: the owner names every seller, so nothing in the demo is paid as a stranger; caps on rates and sessions are raised for a lab where every buyer comes from one address; and the rule that keeps small calls off the lottery is turned off for the 20 sellers, so that their rolls can be shown. docs/DEMO.md lists each change, and says where the script gives no reason for one.
On the buyer's side stood the wallet and a small paying proxy, a demonstration script that a player reads from in place of the seller. Each request the player makes becomes one call of the wallet's pay tool, so the wallet pays as the film plays and stops when the player stops asking. The proxy holds no key and no ticket secret. The film was played as HLS, in segments of about six seconds, and priced by the byte.
| What | Through one routing node | Through two |
|---|---|---|
| First frame in ffmpeg, from a wallet with no session open: the terms, 2 tickets locked along the route, the playlist, the first segment | 0.46 s (median of 3) | 0.55 s |
| The same with a session open | 0.17 s | 0.12 s |
| Played in real time, the player keeping 12 s of film ahead | 90 s with no stall; 4 tickets released, 2 held | 45 s with no stall |
| What it cost | 2,213 sat a minute of film (22.1 MiB a minute) | The same. |
| The whole film, fetched as fast as it came | 106 segments, 274.7 MiB, 27,466 sat by the price; 28 tickets paid, 28,000 sat | The same 28 tickets. |
| Routing fees for the whole film | 28 sat (1.001 sat a ticket) | 56 sat (2.002 sat a ticket) |
| Held ahead while it played | 2 tickets (2,000 sat), and never more pending on the buyer's channel | 2 tickets |
| What happens when | Measured, through one routing node |
|---|---|
| The player is paused | It stops asking once its 12 s are ahead. In the 20 s after that nothing was paid and no ticket was released. The 2 held tickets stayed held and were not spent. |
| The viewer seeks to 5:00 | Playing from the new place after 0.03 s. Jellyfin had those segments ready. |
| The connection drops in the middle of a segment | One request failed. The player asked again and had the segment whole 0.5 s later, and the film did not stall. The 0.28 MiB that had arrived were paid for (30 sat) and were bought again. |
| The seller's gateway is killed and started 8 s later | One request was refused. The film stood still for 19 s and then went on in the same session, with the balance it had. Why it was 19 s and not 8 was not traced. |
| The viewer stops | The seller cancelled the 2 held tickets, and they were back on the buyer's node 0.24 s later. 458 sat, what was left of the last released ticket, stayed with the seller. |
| An agent's one call that may use 300 sat at the most | 3.0 MiB, the first bytes of the film's file, in 0.37 s. One ticket was released, and the wallet stopped the answer where 300 sat were used by its own count. |
After every part, what the buyer's node paid, what the sellers' nodes received and what the wallet's ledger held agreed to the millisatoshi.
The same file from 20 sellers. The film's file, 263.3 MiB, was cut into 1,054 pieces of 256 KiB at 1 sat a piece and bought from 20 gateways at once. A piece costs far less than a ticket, and 20 sellers are more than tickets can be held with from one 30-slot channel, so the wallet paid by rolls: one roll in 64 wins, and a win is an ordinary payment of 64 sat. There were 1,054 rolls and 14 wins, where 16.5 were expected. The buyer paid 896 sat for pieces priced at 1,053 sat; the run before it paid 576 sat. Nine of the 20 sellers were paid nothing: each had sold about 52 sat of pieces, which is less than one win. Routing fees were 14 sat. Never more than 4 payments were on the buyer's channel at once, nothing was held between wins, and no payment failed. Every piece was checked against its hash, and the file was whole in 3 s. That is the lottery as it is: exact on average and uneven in one run, for the buyer and for each seller. The 20 gateways stood beside one seller node (on a regtest, one machine, our own nodes, one routing node).
What the demo does not show
- A person watching
- Nobody watched this film on a screen. The players of the run were ffmpeg, for the first frame, and a script that asks for segments as an HLS player does: it keeps 12 s ahead, pauses and seeks. mpv, VLC and a browser were not tried.
- A stock Jellyfin app paying
- It cannot yet. The player here is handed the film's address. Jellyfin's own web and TV apps also log in, browse and report progress, with headers the gateway does not pass on by default, and they play the file directly with range requests, which the wallet's pay tool cannot send yet. A paying proxy that carries those is not built.
- An AI model as the agent
- The agent of the demo is a script that speaks MCP with the wallet's tools.
- Real coins, or two people
- It is a regtest, on one machine, between our own nodes. Letting people stream from a seller needs a shared test network or mainnet, a gateway reachable from outside behind TLS, and somebody's decision to expose a home server.
- Seeking inside what was paid for
- A viewer who seeks back pays for those segments again.
- What the limits already say
- A seller that takes a ticket and serves nothing keeps at most one ticket, what is left of the last ticket stays with the seller, and a buyer that never pays a win was served about one win's worth.
For a seller: what a buyer can reach is decided by the gateway's routes, one film each, so the service's own port must never be published, only the gateway's. In the demo a second film on the same server, and the server's own pages, answered 404 through the gateway.
6On mainnet: the first rehearsal
The conditions. Two runs on XBT mainnet, on 23 and 24 October 2026, with real coins in amounts we could lose. The buyer and the seller were our own Lightning Fork nodes, version 0.21.4-beta-blake2b.21 (the version Umbrel ships), on one machine, with one direct private channel of the anchor type between them: 100,000 sat, opened for 329 sat at 2 sat/vB. The seller's gateway stood in front of a test file server, and the agent wallet paid through its own pay tool; everything listened on loopback only. The wallet's policy named this one seller, with a task budget of 10,000 sat and at most 3,000 sat a payment. Tickets were 1,000 sat, with 2 held at a time.
| What | First run, 23 October | Second run, 24 October, after the fixes |
|---|---|---|
| A page (100 sat) and a 5 MB file (1,000 sat), with tickets | One prepaid ticket, then held ones. Before the prepaid one the wallet offered 2 held tickets that the seller refused, and booked them as failed (nothing was paid for them). | One prepaid ticket first, then held ones; no ticket refused or failed. |
| Paid for the tickets | 2,000 sat for 1,420 sat served: 580 sat, what was left of the last ticket, stayed with the seller | 2,000 sat for 1,100 sat served: 900 sat stayed with the seller, and the wallet now reports it when the session stops |
| Unused held tickets when the session stopped | 2, cancelled by the seller and back on the buyer's node | 2, the same |
| 20 pieces by lottery, 320 sat expected | 3 wins of 64 sat: 192 sat, all settled | 3 wins of 64 sat: 192 sat, all settled |
| The gateway's count of paid wins | 0 while the session was open; it counted them only when the session was swept | 3 within 10 s, with the session still open |
The wallet's spending cap (ln.exposure_cap_sats) | Had to be set at 140,000 sat, since it counted the node's on-chain coins too | 100,000 sat, counting the channel only (96,864 sat; 33,599 sat on chain shown and not counted) |
| Bodies and fees | Every body matched its file. No routing fees: the channel is direct. Over both runs the buyer's side of the channel went from 99,056 to 94,672 sat, and the seller's from 0 to 4,384 sat: what the two runs paid. No payment failed. | |
The four faults and their fixes. The cap counted coins on chain, which a payment cannot spend, so any cap below the node's whole balance refused every payment; it now counts the channels and what is in flight. The gateway did not say in its 402 that it holds tickets only after a prepaid one, so the wallet tried held tickets first; the terms now say so, and the wallet buys the prepaid ticket first. The gateway looked at a win's invoice only when the buyer next asked for something; it now looks on every round of its janitor. And what is left of the last ticket stays with the seller when a session ends: that is by design and less than one ticket, and the wallet now reports it. The fixes are in the public tree, with tests.
What the rehearsal does not show
- Anyone else's node
- Both nodes are ours, on one machine, on one direct channel: no route, no routing node, no fee policy but our own, no network latency.
- Anyone outside the project
- No outside buyer or seller has used it, and the gateway was not reachable from outside.
- A force close
- The channel is still open, and no channel was closed with tickets in it on mainnet.
- Load
- One buyer, one session at a time, a few calls.
7On mainnet: the released app between our own nodes
The conditions. From 26 to 27 October 2026, on XBT mainnet, with real coins in small amounts. The app was installed from the published packages, ran the safe starter rules (approval above 1,000 sat), and paid through Lightning Fork nodes of our own. The seller was our test shop, now at https://stream.lazarus-xbt.xyz, and in one run a second shop of ours. Each charge below was checked against the seller's meter and its signed receipt.
| What | What was paid and checked |
|---|---|
| A fresh Linux install buys from our shop | One 900 sat prepaid ticket, 904 sat with its routing fee. A film range came back as exactly 2,097,152 bytes, and the credit went down by exactly 40,000 msat (2 MiB at 20 sat per MiB), plus 40 msat for an AI answer of 4 tokens. |
| Our wallet buys films and AI answers from our shop | 1,003 sat in all, with the AI model billed exactly 10 msat per token. |
| A node box of ours buys from a second shop of ours | A 5 MiB file charged exactly 100 sat, and an AI answer of 8 tokens exactly 80 msat, from one 900 sat ticket. |
| Our shop at its new address | A 2 MiB range, exactly 2,097,152 bytes, charged exactly 40,000 msat. The seller's signed receipt matched the request. |
| Hashrate | A wallet of ours rented a miner from our shop: 691 sat, paid for slices already delivered. |
| StartOS: the node key | With Lightning Fork's Grant app access, the app took its key within 60 s and could read and pay but not make addresses. After a revoke the app said so plainly; a new grant was taken within 5 s. |
Faults these runs found, all fixed. Credit left with a seller was not used after a restart, or under the shop's new address, and a new ticket was bought instead (fixed in 0.1.15). A film stream stopped early could be charged past the limit set for it (0.1.15). The real-money step was easy to miss, because Home said ready before it (0.1.15). And a fresh install could not allow our shop after it moved address, because its listing still named the old one (0.1.16). The changelog has each.
What these runs do not show
- Anyone outside the project
- Every buyer and seller was ours. No outside tester has used the app yet.
- A paid buy on StartOS
- On StartOS the node key was tested on mainnet; a paid buy there has not run yet.
- Strangers' routing nodes
- Payments went over channels between our own nodes.
- Load
- One buyer at a time, a few calls each.
Back to the whitepaper · Draft 11, 27 October 2026.