v0.2 archive: Caparra Protocol draft (superseded)
2026-10-09 · Dominic
This tab keeps version 0.2 of 9 October 2026 unchanged, as source material for the CPR extension documents (deposits, pricing guidance, wallet guidance, SASL, TLS/QUIC). The first tab, version 0.3, supersedes it. Here the protocol still carries its old working name, Caparra; read CPR.
Abstract and status
Caparra is an open protocol that makes every costly network action carry a small Bitcoin-denominated price, and lets operators hold refundable deposits that are returned by default and kept only on published evidence of abuse. It is a layered stack: one core token format, normative bindings for HTTP and SASL, an Experimental TLS 1.3/QUIC binding, and pluggable funding rails with Bitcoin (ecash and Lightning) as the reference.
What the debate changed. Three owner choices survive in modified form:
- Refundable bond, demoted. A refundable bond costs an attacker almost nothing unless the operator catches them, so non-refundable per-action stamps are the primary price. Bonds remain as a first-class but optional, capped penalty instrument.
- Custody. Operators issue their own blind tokens (the operator is its own mint). Bitcoin rails only fund those tokens. This removes global mint allowlists, the fake-bond attack and the shared-mint surveillance point.
- Layer. The application binding (HTTP, SASL) is normative. The TLS/QUIC binding is a separate Experimental document, because forfeit is an application judgement and the reference TLS library will not merge a custom extension.
Status: pre-draft, version 0.2. Contested design choices were argued by opposing agents before being fixed here; the debate record is in Appendix A. An independent review of version 0.1 found five blockers and seven major issues; all are fixed in this revision. The key words MUST, SHOULD, RECOMMENDED and MAY are to be read as in BCP 14 (RFC 2119, RFC 8174).
Fixed inputs from the project owner:
- Scope: protocol level (TLS/QUIC/HTTP), full vision, no MVP cut in this document
- Rail: spec is rail-agnostic; reference implementation is Bitcoin-only
- Mechanism: refundable bond; on forfeit the operator chooses where funds go, and claim rates are public
- Unit: one session bond per connection (now a session credential with a small fee; see Lifecycle), plus optional per-action stamps for costly actions
- Privacy: unlinkable by default; operators may opt into linkable tokens
- Fallbacks: pluggable proof types (ecash, Lightning, hashcash, operator-issued tokens)
- Agents: welcome if they pay; the protocol prices actions, not actors
- Implementation: Rust, solo maintainer at start, MIT/Apache-2.0 dual license
- Standards path: Cashu NUT first, IETF Internet-Draft later
Name. “Stamp” collides with the well-known Bitcoin Stamps (SRC-20) project, and the stamp crate is taken. The debate recommends Caparra, the Italian word for a refundable earnest-money deposit: exactly the semantics, and the caparra crate was free on crates.io. Alternatives with free crate names: Arra, Vadium, Handsel. GitHub collisions are not yet checked.
| Term | Meaning in this spec |
|---|---|
| Operator | The service that admits traffic and judges abuse |
| Stamp | Non-refundable payment consumed by one action |
| Session credential | Prepaid token consumed at admission, re-issued on clean close |
| Escrowed bond | Timelocked deposit with a forfeit path and a refund path |
| Funding rail | How value enters: Lightning, Cashu, Ark, or no money (hashcash, free grants) |
| Carrier | The protocol slot that transports a token: HTTP, SASL, SMTP, Nostr, TLS/QUIC |
Motivation and threat model
Caparra prices out actions whose value to the actor is lower than their price: slop posts, spam mail, bulk fake signups, Sybil swarms. It does not stop attackers who earn more per action than they pay, and it does not touch packet floods.
The economic test is one inequality. An automated action is deterred when the stamp price plus the expected forfeit exceeds the value of the action:
p_{stamp} + q \cdot B > v_{action}
Here q is the probability the operator flags the action and B is the bond at risk. With no reliable abuse classifier, q is near zero and only the stamp does any work. That is why stamps, not bonds, carry the price.
| Actor | Goal | Outcome under Caparra |
|---|---|---|
| Slop and spam farms | Mass low-value posts, mail, comments | Priced out once the stamp exceeds revenue per action |
| Sybil swarms | Evade per-account rate limits | Splitting identities does not help: cost is per action |
| Abusive crawlers | Bulk reads | Priced only where the operator charges for reads |
| Paying agents | Legitimate automated work | Admitted; they pay the same price as anyone |
| Capital-rich or state actors | High-value influence | Not deterred; out of scope |
| Volumetric floods (L3/L4) | Exhaust bandwidth | Out of scope; the Experimental QUIC binding only limits handshake floods |
| Greedy operators | Profit from false forfeits | Constrained by objective reason codes and a public forfeit log |
| Mints and payment intermediaries | Theft, censorship, surveillance | Kept off the admission path by operator-issued tokens |
Corrections to the original thesis:
- A price is charged per action, not per actor. “Negligible at human speed, prohibitive at machine speed” only holds if each actor’s volume is capped, and a cap is an identity system. Caparra instead targets the value per action.
- Paying sats is a transfer of money, not proof of work. Its advantage over hashcash is fairness: money costs everyone the same, while hashcash favours attackers with GPUs over humans with phones (Laurie and Clayton, 2004).
- Superintelligence framing is unfalsifiable and deters serious operators. The spec argues from spam economics only.
First principles, goals and non-goals
Eight principles decide every trade-off in this spec; each one came out of a debate the losing side conceded.
- Price actions, not actors. Per-action stamps are the primary instrument; bonds are an optional penalty layer.
- The relying party is its own issuer. An operator accepts credentials it signed itself. No global mint allowlist exists, so no third party can mint fake admission and no new gatekeeper appears.
- Admit at transport, judge at application. Admission can happen in any carrier; abuse is judged where the semantics live, and all carriers share one session state machine.
- Refund by default, forfeit by evidence. Silence or operator failure returns value to the client. Every forfeit carries a signed receipt with an objective reason code and lands in a public log.
- Unlinkable by default, linkable only per origin. Blind signatures hide which funding event a session came from. Linkable mode never spans origins.
- One token format, many carriers. HTTP, SASL, SMTP, Nostr and TLS/QUIC carry identical bytes bound to their channel.
- Rail-agnostic spec, Bitcoin reference. Funding rails are plugins; the reference implementation ships Lightning, Cashu and Ark.
- No protocol party holds funds it does not already own. The project and its foundation never custody anything.
Goals:
- Make low-value automated actions unprofitable at a price a human barely notices
- Work for anonymous visitors with no account and no identity provider
- Verify admission offline in under 1 ms with no network call on the hot path
- Deploy today behind a reverse proxy, with no browser or TLS library changes
- Interoperate with x402, L402, MPP and Privacy Pass in the same 402 response
Non-goals:
- Proving that a client is human (personhood credentials are a separate layer)
- Stopping well-funded attackers or volumetric DDoS
- Replacing the operator’s abuse classifier; Caparra makes its verdicts costly to the abuser
- Being a payment network or a token; Caparra has no coin of its own
Architecture overview
Caparra has four layers. Value enters once through a funding rail, becomes operator-issued blind credentials, travels in a channel-bound envelope, and reaches the operator through whichever carrier the protocol has.
- Funding rails (L0). Lightning BOLT12 offers, Cashu ecash melted to pay the operator’s Lightning invoice, Ark VTXOs for escrowed bonds, or no money at all (hashcash, operator free grants). Rails are only touched when buying credit or settling, never during admission.
- Credentials (L1). Each operator runs its own issuer: a Cashu-compatible blind-signature keyset with an operator-specific unit. Tokens are verified offline with the operator’s own key. Third-party Cashu proofs locked to the operator are an optional extra for portable first-visit stamps.
- Envelope (L2). One CBOR structure carries any proof type, its kind (stamp, session, bond) and a context hash that binds it to the challenge and, where the client can read it, to the TLS exporter of the current connection.
- Carriers (L3). The HTTP
Caparraauth scheme and the SASLCAPARRAmechanism are normative. ESMTP, Nostr and MQTT profiles are optional. The TLS/QUIC binding is Experimental.
A single session state machine with signed receipts and a public forfeit log sits beside the stack. It is the same for every carrier, so a session admitted at a TLS terminator and one admitted by an HTTP header are the same object with the same session id.
Figure: Caparra architecture · 4 layers and a shared state machine. The source tab shows this as an interactive diagram (widget
node/6a453bca-f864); it is not part of this export. See the source doc.
Value flows upward once: a rail funds operator credentials, and every carrier then presents the same channel-bound envelope with no payment on the admission path.
Core objects and wire formats
Four objects make up the protocol: the operator manifest, the operator credential, the token envelope and the receipt. All are encoded deterministically (CBOR on the wire, JSON for the manifest) and signed with secp256k1 BIP-340 Schnorr keys so they reuse Bitcoin and Cashu tooling.
Operator manifest
Published at /.well-known/caparra and summarised in a DNS SVCB/HTTPS caparra SvcParam (proof types plus manifest hash) so informed clients can attach a token on the first request.
{
"v": 1,
"versions": [1],
"origin": "forum.example",
"op_key": "02ab…",
"keysets": [
{ "id": "00ad…", "unit": "cpr", "unit_price_msat": 1000, "denominations": [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024], "active": true, "final_expiry": 1798761600 }
],
"funding": ["bolt12:lno1…", "ark:…"],
"proof_types": ["op-ecash", "hashcash", "op-grant", "cashu-p2pk"],
"binding_profiles": ["tls-exporter", "nonce"],
"prices": { "session_deposit": 4, "session_fee": 1, "post": 8, "signup": 64 },
"price_ceiling": { "session_deposit": 16, "post": 64, "signup": 256 },
"bond": { "cashu_tiers": [256, 1024], "ark_min": 50000, "detection_window": "P2D" },
"forfeit": { "destination": "donate", "target": "lno1…", "reasons": ["RATE", "QUOTA", "PROTOCOL", "REPLAY", "POLICY"] },
"credit_cap": 10000,
"refund_window": "P7D",
"clock_skew": "PT60S",
"endpoints": { "fund": "/.well-known/caparra/fund", "claim": "/.well-known/caparra/session", "log": "/.well-known/caparra/log" },
"sig": "…"
}
Prices are in units of the operator’s own credential. The manifest pegs a unit to millisatoshis and MAY add a fiat reference that is re-quoted in every challenge.
Operator credential
An operator credential is a Cashu proof (NUT-00) from the operator’s own keyset: amount, keyset id, secret, unblinded signature C and a DLEQ proof (NUT-12). The client builds the secret before blinding as a NUT-10 well-known secret that P2PK-locks the proof to a fresh ephemeral client key K. The operator therefore never sees K at issuance, and a stolen proof is useless without K.
Verification of an op-ecash envelope runs in this order, cheapest checks first:
- Parse the envelope; reject an unknown version with
VERSION. - Check the size (at most 4 KiB) and that the proof type and binding profile are accepted.
- Recompute ctx from the challenge, or from a timestamp within
clock_skewfor a pre-informed client; reject a mismatch withBINDING_MISMATCH. - Check each proof’s keyset is listed and not past
final_expiry, and that amounts cover the price. - Check the signature by K over ctx.
- Check
C = k_amount · hash_to_curve(secret)with the operator’s private key. - Atomically test-and-set each proof’s nullifier in the seen-set; reject
SPENTif it is already there. - Record the stamp or session and sign the receipt.
The client MUST strip the DLEQ proof before presenting to the operator that issued the token. A NUT-12 proof DLEQ carries the blinding factor, which would let the issuer link presentation to issuance. DLEQ is checked only by the client at issuance and by verifiers of third-party cashu-p2pk proofs.
Seen-set. Entries are inserted only after verification and kept until the keyset’s final_expiry. Operators with several points of presence pick one model and state it in the manifest: per-PoP keysets (default; each PoP accepts only credentials it signed, fully offline), a central store with atomic test-and-set (one network call), or eventual sync with a stated maximum double-spend window.
Token envelope
CaparraToken = {
1: uint, ; version = 1
2: kind, ; 0 stamp, 1 session, 2 bond
3: tstr, ; proof type, e.g. "op-ecash"
4: [+ ProofItem], ; one or more proofs
5: bstr .size 32, ; ctx
6: binding_type, ; 0 nonce, 1 tls-exporter, 2 tls-hello
? 7: tstr, ; action label for stamps, e.g. "post"
? 8: bstr .size 32, ; sid, for a stamp spent inside a session
? 9: [* BlindedMessage], ; replacement (session) or change (stamp) outputs
? 10: BondTerms ; locktime, refund key, forfeit script (bonds only)
}
ProofItem = [proof: bstr, sig: bstr / null] ; sig by the proof's client key over ctx, if the type has one
ctx = SHA-256(CBOR(["caparra/v1", origin, nonce_or_ts, kind, action, amount,
proof_type, binding_type, binding]))
binding:
nonce empty; the challenge nonce and quote-exp carry the binding
tls-exporter TLS-Exporter("EXPORTER-Caparra", "", 32)
tls-hello SHA-256(ClientHello.random || cookie_nonce_or_ts) ; Experimental TLS binding only
Binding profiles. Native clients, agents and SASL -PLUS use tls-exporter, so a captured token fails on any other connection. Browsers and extensions cannot read the TLS exporter, so they use nonce: the token is valid for one challenge within quote-exp, and an operator MAY price or refuse that weaker profile. A CDN that terminates TLS computes the exporter and forwards it to the origin. A typical envelope is 300 to 450 bytes per proof.
Proof types
| Proof type | Status | Verified against | Nullifier | Use |
|---|---|---|---|---|
op-ecash | Core, MUST | Operator’s own keyset | Y = hash_to_curve(secret) | Sessions, stamps, credit |
hashcash | Core, MUST | SHA-256 over ctx and a counter, d leading zero bits | Hash of the solution | No-money fallback with an operator-set weight |
op-grant | Core, SHOULD | Operator keyset, unit grant | Y | Free tokens issued after an attestation (email, CAPTCHA, invite) |
cashu-p2pk | Optional | Third-party mint keyset on the operator’s own list, with DLEQ | Y | Portable first-visit stamps and small escrowed bonds |
ln-preimage | Optional | SHA-256 of preimage against an operator macaroon (L402) | Payment hash | Interop with L402 clients |
pp-token | Optional | Privacy Pass publicly verifiable token (RFC 9578, blind RSA); a separate token type | Token nonce | Interop with Privacy Pass issuers |
Funding rails
| Rail | How credit is bought | Refund path | Role |
|---|---|---|---|
| Lightning BOLT12 | Client pays the operator’s offer; operator blind-signs units | New payment to the client’s offer | Reference rail |
| Cashu via Lightning | Client melts its ecash at its own mint to pay the operator’s invoice; the preimage proves payment | Lightning payment | Portable for ecash holders; the operator trusts no mint |
| Cashu direct | Operator swaps tokens from mints on its own list | Fresh ecash | Optional; the operator carries that mint’s risk |
| Ark VTXO | Out-of-round transfer into a timelocked script with a presigned forfeit output, at least ark_min | Cooperative release, or unilateral exit after the timelock | The only fully non-custodial escrowed bond |
| Hashcash | Work instead of money | None | Last-resort fallback |
| Operator grant | Attestation instead of money | None | Free tier, onboarding |
Receipts
Every state transition yields a receipt signed by op_key: {sid, transition, reason, amount, epoch, ts}. Each proof type defines a nullifier (table above), and sid = SHA-256("caparra/sid" || proof_type || nullifier of the first proof), so it is identical whichever carrier delivered the token.
Conformance details
- Versioning. Envelope field 1 and the manifest’s
versions; an unknown version getsVERSIONwith the supported list. - Errors.
INVALID,SPENT,KEYSET_EXPIRED,UNDERPAID,QUOTE_EXPIRED,UNSUPPORTED_TYPE,BINDING_MISMATCH,VERSION,RATE_LIMITED. HTTP carries them inCaparra-Error; SASL in the failure message. - Time. All times are UTC. An epoch is one UTC day. Clock skew is bounded by the manifest’s
clock_skew(default 60 seconds). - Keyset rotation. The manifest lists keysets with
final_expiry. A retired keyset keeps signing replacements forrefund_windowand keeps verifying untilfinal_expiry. - Failed actions. If the operator fails (for example HTTP 5xx) after consuming a stamp, it MUST return a replacement for it.
- Manifest authenticity without WebPKI (SMTP, SASL, Nostr): pin
op_keyon first use, or check the manifest hash in a DNSSEC-signed SVCB record.
Lifecycle and settlement
An honest session costs one non-refundable session fee (default 1 unit) and moves no money: the deposit token is consumed, and on release the operator blind-signs a replacement worth the deposit minus the fee. Money only moves when credit is bought, withdrawn, or forfeited.
| Instrument | Refundable | Where the value sits | Forfeit enforced by |
|---|---|---|---|
| Stamp | No | Consumed by the action | Not applicable |
| Session credential | Yes, minus the session fee, by re-issue | Operator credit (capped) | Operator withholds the replacement |
| Escrowed bond, Ark | Yes, by timelock | Client’s VTXO in a shared script | Presigned forfeit output; the operator only triggers it |
| Escrowed bond, Cashu | Yes, by timelock | Third-party mint | Operator swaps before the locktime |
Session states
- PRESENTED. The client sends an envelope of kind
sessionwith deposit proofs and blinded outputs for its replacement. Outputs total at most the deposit minussession_fee, MUST name the keyset active at this moment, and duplicate blinded messages are rejected. - ADMITTED(sid). Checks pass; the operator stores
sid, the nullifiers and the blinded outputs, and returns an ADMITTED receipt. Every later request in the session carries a signature by K over(sid, method, target, ts), so a publishedsidalone is useless. - RELEASED. On clean close or on expiry without a flag, the client claims its replacement at the manifest’s claim endpoint with a K-signature. The claim is idempotent and repeatable until
refund_windowends, so a lost response loses nothing. SASL logout and QUIC close need no in-band release for the same reason. The operator MAY defer release up to itsdetection_window, and a session can be flagged until it is released. - FLAGGED → FORFEITED. The operator withholds the replacement and signs a FORFEITED receipt with a reason code and an evidence hash. The receipt enters the forfeit log.
What the session credential is for. It does not price an unflagged abuser beyond the session fee. Its jobs are a concurrency limit (holding 1M concurrent sessions locks up 4M units, about $2,700 at 1 unit per sat) and a forfeitable deposit until release. The per-action stamp remains the main price.
Expiry without a release counts as RELEASED: if the operator goes silent, the client can claim its replacement for the whole refund_window.
Figure: Session state machine · shared by every carrier. The source tab shows this as an interactive diagram (widget
node/a5d34f30-c6a2); it is not part of this export. See the source doc.
The default path ends in a fresh credential; reaching a forfeit takes an explicit flag with an objective reason code and a signed receipt.
Escrowed bonds
Bonds are for higher-risk actions (first post on a new account, bulk mail, relay writes) and come in fixed tiers. Bond amounts never scale with load, so a capital-rich attacker cannot raise them to price out humans.
- Ark bond. The client transfers a VTXO into a script with three paths: operator plus Ark server spending to a destination presigned by the client before time T (forfeit); client plus operator spending anywhere (release); client alone after T (refund, with unilateral on-chain exit). The operator cannot redirect a forfeit to itself. This script is the debate’s construction, not a shipped Ark feature. Ark bonds MUST be at least the manifest’s ark_min (RECOMMENDED 50,000 sat) so a unilateral exit stays economical, and T MUST fall before the VTXO’s batch expiry.
- Cashu bond. A
cashu-p2pkproof locked to the operator’s NUT-28-blinded key, with locktime T and a client refund key (NUT-11). Release is an operator signature that lets the client swap at once; forfeit is an operator swap before T. The destination cannot be enforced, so the log is the only check. RECOMMENDED cap: 10,000 sat.
The locktime MUST satisfy:
T \geq t_{session} + w_{detection} + g
where w is the operator’s declared detection window and g is a grace period of at least 24 hours for mint or Ark-server outages. Operators SHOULD forfeit early rather than near T.
Forfeit destination and reasons
The owner’s rule stands: the operator chooses the destination. The manifest MUST declare it, wallets MUST show it with the claim rate before paying, and the spec RECOMMENDS a destination other than the operator.
| Destination | Meaning |
|---|---|
self | Operator keeps it; wallets flag this |
donate | Paid to a published BOLT12 offer or address |
burn | Settled to the operator’s own node, then destroyed in a batched on-chain OP_RETURN transaction with a published txid; never by abandoning ecash, because unspent ecash is a windfall for the mint |
arbiter | Held in 2-of-2 with a named arbiter for disputed reasons |
| Reason code | Objective test |
|---|---|
RATE | Actions per interval above the published limit |
QUOTA | Bytes or actions per session above the published cap |
PROTOCOL | Malformed or abusive protocol use |
REPLAY | Token reuse detected |
POLICY | Content-policy breach; requires the policy hash and an evidence hash |
Forfeit log
The forfeit log is an append-only Merkle log in the style of Certificate Transparency (RFC 9162). Each entry holds (sid, reason, amount, evidence_hash). Every epoch the operator publishes a signed tree head and a signed admission count. Clients verify that their own receipts are included and that tree heads are consistent, and MAY publish receipts so third parties compute value-weighted claim rates. Admission counts can still be padded, so a claim rate is a signal, not a guarantee.
Credit and withdrawal
Operator credit is called capped prepaid credit, never escrow. Funding that would push a balance above credit_cap is refused, unused credit can be redeemed through the funding rail within refund_window, and idle credit is refunded automatically.
Transport binding (Experimental)
The TLS 1.3/QUIC binding is a separate Experimental document that admits sessions before application code runs; it never judges abuse and is never required by the core. Its one capability the application bindings lack is pricing handshake floods on unauthenticated endpoints such as SMTP MX servers and Nostr relays.
Mechanisms, in build order
- DNS discovery (shared with all carriers). The SVCB/HTTPS
caparraSvcParam lists proof types and the manifest hash, next to the existingechparameter. - QUIC handshake priority with NEW_TOKEN (first prototype). After a session admitted by any carrier, the server issues a one-time NEW_TOKEN containing an encrypted
{sid, standing, expiry}, and a fresh one on every connection, because the token is visible on the path and RFC 9000 tells clients not to reuse tokens. Presenting it gives the next connection handshake priority at the cost of one AEAD decryption; it never grants application admission. It needs only a small patch to quinn’s token payload, and it is linkable, so it is available only in linkable mode. - TLS extension, ECH only. The client advertises the
caparraextension in its ClientHello. The server replies with a HelloRetryRequest carrying the challenge under that same codepoint (a server may only answer with extensions the client offered) plus a cookie, so it stays stateless. The second ClientHello carries the token inside the encrypted inner ClientHello, bound with thetls-helloprofile to its own ClientHello.random and the cookie nonce, because the exporter of an unfinished handshake does not exist yet; Finished then authenticates the token through the transcript. The HelloRetryRequest travels in clear and reveals that the server runs Caparra. A pre-informed client (from DNS) skips it and binds to a timestamp instead. Without ECH no token goes in the handshake and the client falls back to an application binding.
| Path | Extra round trips | Linkable |
|---|---|---|
| Pre-informed client with ECH | 0 | No |
| Uninformed client, HelloRetryRequest challenge | 1 | No |
| QUIC NEW_TOKEN handshake priority | 0 | Yes |
| No ECH | Falls back to HTTP or SASL | No |
Rules
- Check order. Address validation (QUIC Retry or HRR cookie) first, then size and keyset id, then the seen-set, and only then elliptic-curve verification. Each source address gets a verification budget.
- No new tokens in 0-RTT. A resumed PSK session inherits its
sid; 0-RTT data is accepted only for idempotent, cheap operations. - Terminators. A CDN or load balancer that terminates TLS acts for the operator. It passes the admission verdict to the origin as a PROXY v2 TLV
{sid, verdict}and receives forfeits back keyed bysid. - No transport forfeit signal. Forfeits use the core receipt on the application carrier; there is no TLS alert or QUIC error code for them.
- Codepoints. The extension aligns codepoint-for-codepoint with any revived Privacy Pass-in-TLS draft, so Caparra becomes a token type in that slot rather than a rival extension. The draft found by the debate (draft-pauly-privacypass-for-tls-00) has expired.
Advancement criteria. The binding leaves Experimental only after at least two independent HTTP or SASL deployments and an unforked rustls path, since rustls refuses arbitrary extension APIs and points implementers to standardisation.
Application bindings: HTTP, SASL and others
The HTTP Caparra authentication scheme and the SASL CAPARRA mechanism are the normative carriers; they deploy today behind a reverse proxy and reach every protocol that has an auth slot.
HTTP challenge
One 402 response offers Caparra next to other payment schemes, so clients use whichever they support. Privacy Pass PrivateToken challenges are defined for 401 responses (RFC 9577), so an operator that also accepts Privacy Pass sends that challenge on a 401.
HTTP/1.1 402 Payment Required
WWW-Authenticate: Caparra realm="forum", kind="stamp", action="post", amount="8", unit="cpr",
proof-types="op-ecash hashcash cashu-p2pk", bindings="tls-exporter nonce", hc-bits="22",
nonce="q8Zr…", quote-exp="60", manifest="/.well-known/caparra"
WWW-Authenticate: Caparra realm="forum", kind="session", amount="4", fee="1", unit="cpr", ttl="3600", nonce="…"
WWW-Authenticate: Payment id="…", method="lightning", intent="charge", request="…"
PAYMENT-REQUIRED: eyJ4NDAy…
HTTP credential and session
Authorization: Caparra token="<base64url CBOR envelope>"
- If
Authorizationalready carries application auth, the client sendsCaparra-Authorizationinstead. - On admission the server answers
Authentication-Info: sid="s_9f…", exp=3600, receipt="…". Later requests sendAuthorization: Caparra sid="s_9f…", ts="…", sig="…", where sig is the K-signature over(sid, method, target, ts). - Native clients and agents use the
tls-exporterbinding profile; a terminating CDN computes the exporter and forwards it to the origin, as RFC 9729 does. Browsers and extensions, which cannot read the exporter, use thenonceprofile. - The default session scope is the application session, not the connection, because HTTP/2 and HTTP/3 pool, coalesce and migrate connections.
Release, forfeit and credit
| Action | Wire form |
|---|---|
| Clean close | POST {claim}/{sid}/close with a K-signature, or expiry |
| Claim replacement | POST {claim}/{sid}/release with a K-signature; idempotent until refund_window ends; answers Caparra-Status: released; sigs="…"; receipt="…" |
| Forfeit | Caparra-Status: forfeited; reason="RATE"; receipt="…" on the 403 or 429 response |
| Buy credit | POST {fund} with the rail payload; answers with blind signatures |
| Withdraw credit | POST {fund}/withdraw with tokens and a refund destination |
| Forfeit log | GET {log}/{epoch} |
| Errors | Caparra-Error: SPENT (codes listed under Conformance details) |
Other carriers
| Carrier | Form | Status |
|---|---|---|
| SASL | CAPARRA and CAPARRA-PLUS (RFC 9266 tls-exporter channel binding, as SCRAM-PLUS), both starting with a server-first nonce, for SMTP submission, IMAP, XMPP, LDAP and MQTT 5 AUTH | Normative |
| ESMTP (MX to MX) | MAIL FROM:<a@b> CAPARRA=<token> extension; inbound MX has no SASL | Optional |
| Nostr | A NIP adding a ["caparra", token] event tag and a NIP-42 AUTH variant | Optional |
| ActivityPub | Plain HTTP binding on the inbox POST | Covered by HTTP |
| gRPC, WebSocket | HTTP binding on the upgrade or call | Covered by HTTP |
Interop with existing schemes
- x402: publish
caparraas an x402 payment scheme so x402 clients can pay Caparra stamps; privacy and refundability are the differentiators. Agents may pay in x402 while humans use Caparra credit. - MPP / draft-httpauth-payment: register
caparraas a payment method andbondas the first entry in the payment-intent registry. - L402: accept
ln-preimageas an optional proof type. - Privacy Pass: a Caparra payment can serve as the attestation step in the RFC 9576 architecture, after which a separate Privacy Pass issuer issues tokens; pp-token is accepted as a proof type.
Pricing and admission control
Only stamps follow load; bonds come in fixed tiers and every price has a published ceiling. At the ceiling the operator queues instead of raising the price, so nobody can buy an advantage over humans with capital they get back.
Load-based stamp price
p(u) = \min\left(p_{ceiling},\; p_{base} \cdot \max\left(1, (u / u_{target})^{k}\right)\right)
u is the utilisation of the priced resource (posts per minute, open sessions, CPU), u_target the comfortable level and k the steepness. A quote is valid for quote-exp seconds. An attacker who drives the price up pays it in non-refundable stamps, so surge pricing costs the attacker real money.
Admission under saturation
- Requests at the ceiling are served by fair queuing per credential class, never by highest bid.
- Operators SHOULD reserve a share of capacity for a human tier (grant tokens and low-volume credit) at or below the ceiling.
- The core never escalates price per identity, because identities are free to split. Per-origin escalation is possible only in linkable mode.
Proof-type weights
Each proof type counts as a number of units set by the operator. Hashcash difficulty SHOULD be set so a phone solves it in 1 to 3 seconds; GPUs and botnets solve it far faster, so its weight stays low and it is a last resort.
Calibration example
At about $67,000 per BTC (June 2026), 1 sat is about $0.00067. These figures are illustrative.
| Action | Assumed value to a spammer | Stamp | Cost to a human |
|---|---|---|---|
| Forum post | $0.002 | 8 sat ($0.005) | 20 posts a day = 160 sat ($0.11) |
| Signup | $0.01 | 64 sat ($0.04) | Once |
| Inbound mail | $0.0005 | 2 sat ($0.0013) | 50 mails a day = 100 sat ($0.07) |
Prices in sats move with BTC. The manifest MAY peg the unit to a fiat reference, re-quoted in every challenge.
Privacy
Blind signatures make sessions unlinkable to each other and to the funding event, but the spec does not promise unlinkability against a small operator, who can already link visitors by IP address, TLS fingerprint and timing.
What the protocol guarantees
- Session to session. Each replacement credential is blind-signed, so the operator cannot connect two sessions through their tokens.
- Funding to session. The operator sees a payment at funding time but cannot tell which tokens it produced.
- Theft resistance. Tokens are P2PK-locked to an ephemeral client key and bound to the challenge nonce or the TLS exporter, so an observer cannot reuse them.
Rules that protect it
- Fixed denominations. Amounts are powers of two units, because odd amounts are fingerprints. Clients also strip DLEQ proofs before presenting to the issuing operator, because a DLEQ reveals the blinding factor.
- Per-origin keys. Clients MUST use fresh ephemeral keys per token and MUST NOT reuse refund keys across origins.
- Linkable mode is per origin only. An operator may require linkable credentials (for example to give returning users standing, or for QUIC NEW_TOKEN handshake priority). The spec forbids credentials that are linkable across origins, so Caparra cannot become a global payment identity.
- Delayed issuance. Operators SHOULD batch and delay blind signing after funding, and clients SHOULD pay through BOLT12 blinded paths.
- Third-party mints. For
cashu-p2pk, the operator key is blinded with NUT-28 so the mint cannot see which operator a proof is locked to. Clients accept only aged keysets used by many wallets, and operators sweep in batches through Tor or Oblivious HTTP. Swap-on-receive inside the handshake is not allowed because it leaks per-session timing to the mint.
Regulatory context
From July 2027 the EU AMLR bars licensed crypto-asset service providers from anonymous accounts, and the Transfer of Funds Regulation (2023/1113) has required checks on self-hosted-wallet transfers above €1,000 since December 2024. Caparra’s model is that each operator runs a closed-loop issuer for its own service only, which looks closer to a merchant taking a deposit than to a custodian. That reading needs EU counsel before anyone relies on it.
Security analysis
With the review fixes in this revision, the residual risks are mainly economic and legal. The red team’s three potentially fatal attacks are each answered by a structural change above, and the table records what remains.
Fatal attack 1: unflagged abuse is free. A refundable bond costs an attacker only interest on locked capital: 1M sessions a day at 100 sat with a 1-hour lock tie up about 4.2M sat (about $2,800), roughly $0.77 a day at 10% cost of capital. Answer: stamps are the primary price, and bonds are a penalty layer.
Fatal attack 2: buying surge prices with refundable capital. If bonds scaled with load, a funded attacker could lock out humans for the cost of interest. Answer: only non-refundable stamps follow load, every price has a ceiling, and saturation is handled by fair queuing.
Fatal attack 3: trust collapse. Unprovable forfeits, a gameable claim rate and a few dominant mints would make bonds look like confiscation. Answer: operator-issued credentials remove mints from admission, forfeits need objective reason codes and signed receipts in a Merkle log, and destinations are declared up front.
| Attack | Severity | Mitigation in this spec | Residual |
|---|---|---|---|
| Abuse that never gets flagged | High | Stamps priced above value per action | Medium: calibration is the operator’s job |
| Surge-price manipulation | High | Load pricing on stamps only, ceilings, fair queuing | Low |
| Operator greed, false forfeits | High | Reason codes, receipts, forfeit log, declared destination, Ark presigned forfeits | Medium: the self destination stays allowed |
| Gamed claim rates | Medium | Signed admission counts, client-published receipts | Medium: denominators can be padded |
| Fake bonds from attacker mints | High | Operator-issued credentials; Cashu holders fund by paying the operator’s Lightning invoice, so no mint is trusted | Low |
| Mint rug, censorship, surveillance | Medium | Mints off the admission path; NUT-28 blinding; batched Tor sweeps; caps | Low to medium for cashu-p2pk users |
| Forfeit race against the locktime | Medium | Locktime at least session plus detection window plus 24 h; forfeit early | Low; NUT-11 post-locktime semantics to confirm |
| Replay across anycast points of presence | Medium | Per-PoP keysets or a central atomic seen-set; ephemeral key signature; nonce or exporter binding | Low |
| 0-RTT and resumption replay | Medium | No new tokens in 0-RTT; resumption inherits sid | Low |
| Verifier DoS with junk tokens | Medium | Address validation, size and seen-set checks before elliptic-curve work, per-source budgets | Low |
| Session stuffing (many actions, one session) | Medium | Per-action stamps; QUOTA caps per session | Low |
| Amount, keyset and timing fingerprints | Medium | Power-of-two amounts, aged keysets, delayed issuance | Medium |
| Free-tier farming by AI CAPTCHA solvers | Medium | Grants bound to origin and rate-limited; low weight | Medium |
| Hashcash hardware asymmetry | Medium | Lowest weight, last resort only | High, accepted |
| Linkable mode becoming a global identity | High | Linkability per origin only; cross-origin key reuse forbidden | Medium |
| Exclusion of people without wallets | High | Grant tier and hashcash; reserved human capacity | Medium |
| Legal exposure of operators and mints | High | Closed-loop operator issuers; foundation holds no funds | High until counsel confirms |
| Incumbent x402 wins by default | High | Bridge as an x402 scheme rather than compete | High |
Added after independent review: session hijack through a published sid (every request and release now carries a K-signature); issuer linking through the DLEQ blinding factor (DLEQ is stripped before presenting to the issuer); replay of QUIC NEW_TOKEN copied from the path (one-time tokens that grant handshake priority only); free unflagged sessions (a non-refundable session fee and release deferral up to the detection window).
Wallets, humans and agents
A human should never see a per-action payment prompt: credit is bought rarely and spent silently under a budget, which makes Caparra feel like a flat rate. Thirty years of failed micropayment schemes (Szabo’s mental transaction costs, Odlyzko’s flat-rate preference) say this is the make-or-break UX rule.
Human wallet (browser extension and mobile)
- Budget policy. Auto-approve stamps up to N units per action and M units per month per origin; ask only above that.
- Top-up, not pay-per-click. One Lightning or Cashu top-up per origin funds weeks of activity; the wallet tops up quietly within the budget. Extensions cannot read the TLS exporter, so the wallet uses the nonce binding profile.
- Trust display. Before the first payment to an origin, show its forfeit destination, its claim rate from the log, and whether deposits are escrowed (Ark) or prepaid credit.
- Free path first. If the origin offers grants, use them before spending money.
- Receipts. Store every receipt; verify forfeit-log inclusion; optionally publish receipts for independent claim-rate audits.
Agent SDK
- A Rust library with TypeScript and Python bindings that handles the 402 challenge, picks a proof type, funds and caches credit, and retries, using the tls-exporter binding profile.
- A command-line client,
cpr get <url>, in the style of Lightning Labs’lnget, so any agent with a shell can pay. - An MCP server exposing “fetch with Caparra” and “check budget” tools.
- Hard spending caps per task and per origin, enforced in the library rather than trusted to the model.
People without wallets
Operators SHOULD offer an op-grant tier funded by attestation (an email, an invite, a Privacy Pass token from an existing attester) and accept hashcash at a low weight. Reserved human capacity under load keeps this tier usable. The design accepts that the free tier is weaker against farming; its job is inclusion, not defence.
Rust reference implementation
The reference implementation is a Cargo workspace that reuses existing Bitcoin and networking crates and keeps every experimental piece behind its own crate, so a solo maintainer never carries a fork on the main path.
| Crate | Responsibility | Builds on |
|---|---|---|
caparra-core | Envelope, manifest, receipts, ctx hashing, session state machine; no_std-friendly | ciborium, secp256k1 |
caparra-issuer | Operator keyset, blind signing, funding and withdrawal flows | CDK (Cashu Dev Kit) |
caparra-verify | Offline verification, seen-set (memory, Redis, sled), check ordering, per-source budgets | caparra-core |
caparra-log | Forfeit log epochs, Merkle roots, inclusion proofs | rs_merkle or equivalent |
caparra-http | Tower middleware for axum and hyper, plus the caparra-gate reverse proxy binary | tower, hyper, rustls exporter API |
caparra-sasl | CAPARRA and CAPARRA-PLUS mechanisms | caparra-core |
caparra-fund-lightning | BOLT12 offers, refunds | LDK |
caparra-fund-cashu | Online swap of incoming Cashu, cashu-p2pk proofs | CDK |
caparra-fund-ark | Escrowed bond scripts | Rust Ark client (beta) |
caparra-wallet | Client wallet, budgets, receipts, cpr CLI, MCP server | All of the above |
caparra-quic | NEW_TOKEN handshake priority (Experimental) | quinn, small upstream patch |
caparra-tls | ECH-only TLS extension (Experimental) | rustls fork behind a feature flag |
Build order
caparra-core,caparra-verify,caparra-issuerand the test vectors that the NUT will cite.caparra-httpandcaparra-gate, so any site can deploy behind a proxy.caparra-walletwith thecprCLI and MCP server, so agents can pay from day one.caparra-fund-lightningandcaparra-fund-cashu; thencaparra-log.caparra-sasland the Nostr relay plugin.caparra-fund-ark,caparra-quic, thencaparra-tls.
Performance targets: offline verification under 1 ms per token on one core; seen-set lookups under 50 µs; no network call on the admission path. These are targets to benchmark, not measured results.
Standardization and governance
The spec ships first as Cashu NUTs, where the cryptography already lives and reviewers already exist, then as IETF documents split so the HTTP scheme never waits on the Experimental TLS work.
Documents
| Document | Venue | Status target |
|---|---|---|
| Caparra envelope, operator credentials, receipts | Cashu NUT | Accepted NUT |
| NUT-24 extension: kinds, ctx binding, release and forfeit headers | Cashu NUT | Accepted NUT |
HTTP Caparra authentication scheme | IETF individual draft (HTTP area) | Standards Track candidate |
SASL CAPARRA and CAPARRA-PLUS | IETF individual draft, IANA SASL registry | Standards Track candidate |
SVCB caparra SvcParam | IANA registration | Registered |
caparra method and bond intent | draft-httpauth-payment registries | Registered |
caparra x402 scheme | x402 Foundation | Accepted scheme |
| Nostr NIP, ESMTP extension, MQTT profile | NIPs repo, IETF | Optional |
| TLS 1.3/QUIC binding | IETF individual draft | Experimental |
IANA considerations and test vectors. Registrations needed: the Caparra HTTP authentication scheme, the Caparra-Status, Caparra-Error and Caparra-Authorization header fields, the caparra well-known URI, the SASL mechanisms, the SVCB SvcParam, and an Experimental TLS extension codepoint. Every document ships test vectors (envelopes, ctx hashes, receipts, log entries) generated by caparra-core.
Licensing
Code is dual-licensed MIT and Apache-2.0. NUT text follows the Cashu repository’s terms. Internet-Drafts are published under the IETF Trust’s legal provisions, which license code components in them under the Revised BSD License, so test vectors and examples stay reusable.
Governance
- Now: single maintainer, design changes through public proposal issues in the repository with a two-week comment window.
- After two independent implementations: move the spec to a small editors’ group, keep the code with its maintainers.
- Always: no foundation, company or maintainer holds user funds, runs a default mint, or ships a default allowlist.
Appendix A: Debate record
Six agents argued opposing positions in an opening round and a rebuttal round. The table records each contested decision, the two strongest positions and the verdict this spec adopts.
| Decision | Position A | Position B | Verdict |
|---|---|---|---|
| Bond custody | Cashu ecash: P2PK-locked proofs, offline-verifiable, native locktime refund | Non-custodial: operator-issued blind tokens, funded by Lightning or Ark | Operator-issued credentials in Cashu format; Cashu, Lightning and Ark are funding rails; third-party Cashu proofs optional for portable stamps |
| Economic core | Refundable bond (owner’s choice) | Red team: bonds are nearly free for unflagged attackers | Stamps are primary; bonds are an optional, capped penalty layer |
| Mint allowlists | Mandatory, to stop attacker mints | A new gatekeeper; avoid entirely | No global or default allowlist; Cashu holders fund by melting to the operator’s own Lightning invoice, so no mint is trusted |
| Protocol layer | Transport: admit in the TLS/QUIC handshake, universal across protocols | HTTP and SASL: abuse is judged where the semantics live | HTTP and SASL normative; TLS/QUIC a separate Experimental document |
| Transport forfeit signal | TLS alert and QUIC error code | Application receipt only | Application receipt only; transport admits, application judges |
| TLS path without ECH | Bond as a client certificate type | Collides with mTLS; no handshake carriage without ECH | Dropped; clients without ECH use the application binding |
| Forfeit destination | Operator keeps it (owner’s choice) | Red team: default to burn or donate | Operator chooses, must declare it; spec recommends non-operator destinations |
| Unlinkability promise | Unlinkable by construction | Small operators link by IP and timing anyway | Guaranteed between sessions and funding; not promised against small operators |
| Relationship to x402 | Compete with a Bitcoin-native standard | Skeptic: bridge, don’t compete | Bridge: publish caparra as an x402 scheme |
| Thesis framing | Superintelligence and the human high ground | Skeptic: unfalsifiable, deters operators | Spam economics only |
| Name | Stamp | Collides with Bitcoin Stamps and the taken crate | Caparra, pending a GitHub check |
The skeptic’s narrowest surviving version, a “bonded Privacy Pass” for Nostr relays, fediverse signups, Tor onion services and self-hosted API free tiers, is the recommended first deployment target even though this document specifies the full vision.
Independent review of version 0.1. A reviewer who had not seen the debate found five blockers: DLEQ proofs letting the issuer link sessions, browsers being unable to read the TLS exporter, a circular TLS handshake binding, session ids usable as bearer tokens, and a session credential that cost an abuser nothing. It also found seven major issues, including a false claim that fake Cashu mints fail an online swap. All are fixed in version 0.2.
Open questions and risks
The biggest unknowns are legal and adoption risks, not technical ones; each item below needs an answer before the spec leaves draft.
- EU legal reading. Does a closed-loop operator issuer count as a merchant deposit or as crypto custody under MiCA and AMLR (from July 2027)? Needs EU counsel; Consob required existing Italian providers to apply for MiCA authorisation by 30 December 2025.
- NUT-11 after the locktime. Confirm whether both the operator key and the refund key can spend after T, which would create a race; the spec currently tells operators to forfeit early.
- Ark escrow script. The presigned-forfeit bond is the debate’s construction, not a shipped feature, and Ark implementations are beta. Prototype it and get review from an Ark team.
- Privacy Pass in TLS. The draft the transport binding wants to align with has expired. Track whether it, or the TLS client-puzzles draft, revives.
- Classifier dependence. Bonds only bite when the operator flags abuse. Publish guidance on objective reason codes so small operators can use bonds without a classifier.
- Claim-rate integrity. Find a way to commit admission counts that resists padding, or drop claim rates as a trust signal.
- Wallet adoption. Lightning public capacity is about 4,900 BTC and Cashu has little consumer reach. Decide which wallets to partner with for the browser extension.
- Unit volatility. Decide whether the reference manifest pegs units to sats or to a fiat reference by default.
- Name. Check GitHub, domains and EU trademarks for Caparra before the first public repository.
- Benchmarks. Measure verification and seen-set performance against the targets in the implementation section.
Sources
- Lightning Labs: AI agent tools and lnget
- The Block: Lightning agent tools and Aperture
- Forkast: x402 infrastructure adoption
- Cashu NUT specifications
- IACR ePrint 2026/2174: Cashu system-level analysis
- Laurie and Clayton: Proof-of-work proves not to work
- draft-pauly-privacypass-for-tls-00
- draft-venhoek-tls-client-puzzles-00
- RFC 9729: Concealed HTTP authentication
- draft-httpauth-payment-01
- rustls feature manual (custom extensions)
- quinn-proto token source
- Cloudflare: TLS 1.3 middlebox failures
- Arkade hashlock contracts
- Consob press release on MiCA deadlines
- EU AMLR crypto rules
- Spark: Lightning network state 2026 (published by a competing protocol)