Why: The Trust Gap in Chapter 10
Chapter 10's "open tab" model is honest but trusting. It carries two asymmetric risks:
Risk 2 — the publisher could over-charge. In Chapter 10 the server holds the voucher-signing key and is authoritative on price. That HMAC key is the publisher's own secret — it proves nothing to the reader. A hostile publisher could, in principle, claim more reads than happened.
A trustless channel makes neither party trust the other. The reader locks a deposit in an on-chain program up front. The publisher can only ever withdraw what the reader cryptographically signed for with their own wallet key. The reader can always reclaim the unspent remainder after a timeout. The Solana program is the referee — not the server, not the publisher.
The On-Chain Program (Anchor / Rust)
A unidirectional payment channel (payer → payee) — simpler than a bidirectional channel and sufficient for reader→publisher. Funds are escrowed in a vault token account owned by a Channel PDA; only the program can move them.
Channel state account (PDA: ["channel", payer, payee, channel_id])
| Field | Type | Meaning |
|---|---|---|
payer | Pubkey | Reader's wallet — the only valid voucher signer |
payee | Pubkey | Publisher's wallet (PayTo) |
mint | Pubkey | USDC mint |
deposit | u64 | Total USDC base units escrowed at open |
claimed | u64 | Amount already withdrawn by payee (starts 0) |
seq | u64 | Highest voucher sequence redeemed (monotonic anti-replay) |
expiry_unix | i64 | After this, payer may reclaim the remainder |
Instructions
open_channel(channel_id, deposit, expiry_unix)— payer signs; creates the Channel PDA + vault ATA; transfersdepositUSDC into the vault.claimed=0, seq=0.close_with_voucher(cumulative, voucher_seq, ed25519_sig)— payee submits the latest voucher. Program: (a) verifies the payer's ed25519 signature over the canonical voucher message; (b) requiresvoucher_seq > seq,cumulative >= claimed,cumulative <= deposit; (c) transferscumulative - claimedfrom vault to payee; (d) setsclaimed = cumulative,seq = voucher_seq. Callable incrementally or once at the end.timeout_refund()— payer signs afterexpiry_unix; returnsdeposit - claimed(the unspent remainder) to the payer; closes the channel. This is the reader's guarantee — funds are never trapped.cancel()(optional, cooperative) — both parties sign for an immediate clean settle before expiry.
The Voucher: Real ed25519 (not HMAC)
This is the core upgrade over Chapter 10. There, the voucher is HMAC-signed by the server (a shared secret meaningful only to the publisher). Here, the voucher is ed25519-signed by the reader's own wallet key — the same key that owns the deposit. The signature is now meaningful to a neutral third party: the on-chain program.
Canonical voucher message (the bytes the wallet signs)
domain_tag || channel_pda || cumulative_u64_le || seq_u64_le
domain_tag— fixed prefix (e.g."x402-channel-v1") to prevent cross-protocol signature reuse.- Commits to which channel (the PDA pins payer + payee + mint + deposit), how much total (cumulative), and which update (seq).
Per read: the reader signs a new voucher with cumulative += price, seq += 1. The publisher serves instantly by verifying the signature off-chain (cheap, no RPC) and checking cumulative <= deposit and monotonic seq. No on-chain transaction during reading — same instant UX and one-fee economics as Chapter 10. The publisher redeems by submitting the latest voucher to close_with_voucher; because the program re-verifies the signature and enforces the bounds, the publisher can never over-claim or replay an old voucher.
Ed25519Program verify instruction in the same transaction and have close_with_voucher introspect the instructions sysvar to confirm a matching verification for (payer pubkey, message, signature). This is fiddly and audit-sensitive — it's where most channel-program bugs live.
Dispute, Timeout & Griefing
- Reader reclaim:
timeout_refundafter expiry returnsdeposit - claimed. The deposit is never trapped. - Publisher liveness: the publisher redeems before expiry. (Post-expiry policy — hard cutoff vs. payee-priority race — is an open design choice; see below.)
- No double-spend: the program enforces monotonic
seqandcumulative <= deposit. - No over-claim: the payee is bounded by the reader's signed cumulative.
- No griefing payoff: a reader who signs then stops reading simply causes the publisher to redeem less — no harm done.
What Changes vs. Chapter 10
| Dimension | Chapter 10 (built) | Chapter 11 (this spec) |
|---|---|---|
| Voucher signer | HMAC — publisher's secret | ed25519 — reader's wallet |
| Deposit | Logical cap in server memory | Real USDC escrowed on-chain |
| Settlement | Open tab — trust reader to pay | Trustless on-chain redemption |
| Reader abandons | Publisher loses content | Publisher redeems signed vouchers |
| Publisher over-charges | Possible (holds the key) | Impossible (bounded by reader's signature) |
| Authority | The server | The on-chain program |
| Effort / risk | Days, no on-chain code | Weeks + mandatory audit; real funds in a program |
Prior Art
This is a focused implementation of a well-known pattern, not new cryptography. Payment / state channels generally follow "deposit → off-chain signed balance updates → settle." Bitcoin's Lightning Network is the famous bidirectional analogy; ours is the simpler unidirectional case (payer→payee), which avoids Lightning's routing and HTLC complexity. Solana has community payment-channel and streaming programs and the standard PDA-escrow + ed25519-sysvar-introspection patterns, but no single canonical channel standard the way Lightning is for Bitcoin — so we would build and audit a focused implementation rather than integrate a named third-party protocol.
Open Design Questions (resolve before build)
open_channel) so the reader isn't prompted on every read. This changes the program: the voucher signer becomes the session key, not the wallet directly. Biggest unknown — warrants a dedicated design spike.close_with_voucher multiple times (better liveness, more fees) or only once at the end (one fee, more abandon exposure before redemption).Design spec authored by the architecture team, 2026-06-02. Not yet implemented. Requires a custom Anchor program, a delegated-session-key signing scheme, devnet hardening, and a mandatory third-party security audit before it holds real funds.