One channel per node you buy from
EachopenChannel creates a channel between your address and one node’s address. Downloading from three different nodes in a session = three channels.
Opening a channel
Streaming and vouchers
A voucher is a signed off-chain message:amount=5 MB_USDC supersedes a voucher with amount=3 MB_USDC on the same channel. The node only needs to keep the highest-amount voucher; you can discard older ones too.
Voucher cadence
Default: sign a voucher every 1 MB. Configurable per stream:- Small cadence (1 MB): more signatures, tighter bound on worst-case unpaid delivery if the node misbehaves. Baseline choice.
- Large cadence (10 MB): fewer signatures, faster. Worst-case unpaid delivery is one cadence worth. For a 10 GiB download at 10 MB, that’s 1,024 signatures instead of 10,240 — meaningful CPU saving for hardware-signed workflows.
StreamResponse.max_bytes_per_voucher. Pick the larger of your configured cadence and the node’s offer.
Closing a channel
Either party can initiate:- Happy path: node submits the latest voucher you signed. 48 h dispute window. No disputes → settles at voucher amount. Unspent deposit refunds to you.
- If the node closes stale: you have the dispute window to submit a higher-nonce voucher via
disputeChannel(voucher). - If you go offline: watchtowers can dispute for the node; for you, re-opening your client within the window lets your local dispute monitor handle it.
Dispute window
Default: 48 hours. Set by governance within the bounded range (12 h – 72 h). Why 48 h specifically: Arbitrum’s forced-inclusion path guarantees inclusion within ~24 h. 48 h gives you at least 24 h of effective response time even if the sequencer censors your dispute transaction for the worst-case period.What could go wrong, and how it’s bounded
| Threat | Bound |
|---|---|
| Node serves corrupted bytes | BLAKE3 verification rejects them before you pay for that chunk |
| Node streams and vanishes (no voucher accepted) | At most one cadence worth of unpaid bytes delivered |
| Node closes with stale voucher | You submit the newer voucher within 48 h |
| You go offline during dispute window | Local dispute monitor auto-handles while client is running; you lose only if you’re offline the entire window |
| Arbitrum sequencer censors you | 24 h forced-inclusion path; 48 h window absorbs the worst case |
The local dispute monitor
Every client runs a background dispute monitor that watches on-chain events for its channels. Default config:disputeChannel(voucher) if it detects a stale close. No user interaction required if the client is running.
Running out of deposit mid-stream
If your cumulative voucher amount approaches the channel deposit:- The node refuses new vouchers with
StreamError::ChannelInsufficient. - You close the current channel happy-path.
- You open a new channel with a larger deposit.
- You re-issue
StreamRequestwithoffset_bytes = last_verified_byte.
Token semantics
Multiple allowlisted ERC-20s are supported. Choose per-channel:token_rates. Channels in delisted tokens can be force-closed by any party via forceCloseChannel — a safety net that ensures funds aren’t trapped.
Settlement invariants
- Voucher signatures are the only settlement authority. Not your client, not the watchtower, not the counterparty.
- Amounts are cumulative. Higher nonce + higher amount wins.
- Tokens are fixed per channel. A voucher’s
tokenfield must match the channel’s token; cross-token disputes fail signature check. - Bounded settlement. A channel’s settlement can never exceed its deposit — over-signing vouchers is a client error, not a loss, because the on-chain function caps payout at deposit.