Sealed streams
Pay by the hour. The chain never learns the rate.
A salary by the hour, rent by the month, a subscription by the day. You open the stream once: the rate is sealed to your key and to theirs, you prove that rate times duration fits in your balance, and that total is set aside. From then on the money accrues by itself. The recipient, or anyone, releases what has accrued, any time, with no signature from you and no proof from anyone: the chain multiplies the sealed rate by the periods elapsed. Cancel, and what is left comes back to you, still sealed.
- One proof at opening, then none: a release is two curve multiplications
- The chain holds the rate as a ciphertext and multiplies it by a public number of periods
- No figure, no rate, no total is ever on chain; the recipient reads what accrued with their key
- The payer's copy of the rate is what a cancel refunds, so neither side can read the other's
- Works with payroll, the board and agents: a stream is a transfer that pays itself
period hour · count 160
rate two ciphertexts · to the payer, to the recipient
opened one proof, 1,792 bytes · total set aside, sealed
+ 37 h release · the chain adds 37 × rate to atlas
+ 91 h release · 54 × rate more · still no figure
+ 120 h cancel by gwen · 29 h paid out, 40 h back to gwen, sealed
what the chain saw: who, when, how many periods. Never how much.
On paper, by the second
Open a stream. Watch it accrue. Release it.
A salary runs by the hour and rent by the month; here the period is one second so you can see it move. Open a stream of 0.10 a second for a minute to atlas: one proof, 6.00 set aside. The counter below is what the chain would count. Release, and atlas reads the figure with their key. Cancel, and the seconds left come back to you, sealed.
- One proof at opening, none after: releases are arithmetic on ciphertexts
- Anyone may release; only you may cancel; nobody learns the rate
- What accrued is paid out at cancel, the rest returns under your own key
Open one and the record appears here: the sealed rate as two ciphertexts, the period, the count, what has accrued, what was released.
How a stream works
You seal the rate twice
Enc(rate) under your key and Enc(rate) under theirs. Then one ordinary transfer proof over count × rate: the chain derives those deltas itself by multiplying your two ciphertexts by count, so the proof is bound to the rate you sealed.
The chain counts periods
Elapsed seconds over the period, capped at the count, minus what was released. That number is public. The rate is not.
Releases are arithmetic
periods × Enc(rate) is Enc(periods × rate) under the same key: ElGamal is homomorphic. The chain adds it to the recipient's balance. No proof is needed because the total was proven once and the count is enforced.
On chain
function openStream( address to, Ciphertext rateS, // the rate, sealed to the payer Ciphertext rateR, // the rate, sealed to the recipient uint32 period, // seconds uint32 count, // periods bytes proof // one transfer proof over count × rate ) returns (uint256 id); releaseStream(id) // anyone: credits periods × rateR to the recipient cancelStream(id) // the payer: pays what accrued, refunds the rest × rateS accruedPeriods(id) // what a release would move, in periods
Where to open one
# terminal
bankhaus stream open atlas 42 per hour for 160
bankhaus stream release 1 · bankhaus stream cancel 1
# SDK
const id = await bank.streams.open('atlas', '42', 'hour', 160);
await bank.streams.release(id);
# bankhaus-mcp
open_stream { recipient, rate, period, count }
release_stream { id } · cancel_stream { id } · stream { id }- Payroll
An hourly salary that needs no payroll run at all.
- Rent and subscriptions
Twelve months on the first of the month, opened once.
- Agents
A budget that arrives by the hour instead of a vault funded in one go.