Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

TermMeaning in this spec
OperatorThe service that admits traffic and judges abuse
StampNon-refundable payment consumed by one action
Session credentialPrepaid token consumed at admission, re-issued on clean close
Escrowed bondTimelocked deposit with a forfeit path and a refund path
Funding railHow value enters: Lightning, Cashu, Ark, or no money (hashcash, free grants)
CarrierThe 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.

ActorGoalOutcome under Caparra
Slop and spam farmsMass low-value posts, mail, commentsPriced out once the stamp exceeds revenue per action
Sybil swarmsEvade per-account rate limitsSplitting identities does not help: cost is per action
Abusive crawlersBulk readsPriced only where the operator charges for reads
Paying agentsLegitimate automated workAdmitted; they pay the same price as anyone
Capital-rich or state actorsHigh-value influenceNot deterred; out of scope
Volumetric floods (L3/L4)Exhaust bandwidthOut of scope; the Experimental QUIC binding only limits handshake floods
Greedy operatorsProfit from false forfeitsConstrained by objective reason codes and a public forfeit log
Mints and payment intermediariesTheft, censorship, surveillanceKept 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.

  1. Price actions, not actors. Per-action stamps are the primary instrument; bonds are an optional penalty layer.
  2. 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.
  3. 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.
  4. 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.
  5. Unlinkable by default, linkable only per origin. Blind signatures hide which funding event a session came from. Linkable mode never spans origins.
  6. One token format, many carriers. HTTP, SASL, SMTP, Nostr and TLS/QUIC carry identical bytes bound to their channel.
  7. Rail-agnostic spec, Bitcoin reference. Funding rails are plugins; the reference implementation ships Lightning, Cashu and Ark.
  8. 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.

  1. 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.
  2. 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.
  3. 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.
  4. Carriers (L3). The HTTP Caparra auth scheme and the SASL CAPARRA mechanism 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:

  1. Parse the envelope; reject an unknown version with VERSION.
  2. Check the size (at most 4 KiB) and that the proof type and binding profile are accepted.
  3. Recompute ctx from the challenge, or from a timestamp within clock_skew for a pre-informed client; reject a mismatch with BINDING_MISMATCH.
  4. Check each proof’s keyset is listed and not past final_expiry, and that amounts cover the price.
  5. Check the signature by K over ctx.
  6. Check C = k_amount · hash_to_curve(secret) with the operator’s private key.
  7. Atomically test-and-set each proof’s nullifier in the seen-set; reject SPENT if it is already there.
  8. 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 typeStatusVerified againstNullifierUse
op-ecashCore, MUSTOperator’s own keysetY = hash_to_curve(secret)Sessions, stamps, credit
hashcashCore, MUSTSHA-256 over ctx and a counter, d leading zero bitsHash of the solutionNo-money fallback with an operator-set weight
op-grantCore, SHOULDOperator keyset, unit grantYFree tokens issued after an attestation (email, CAPTCHA, invite)
cashu-p2pkOptionalThird-party mint keyset on the operator’s own list, with DLEQYPortable first-visit stamps and small escrowed bonds
ln-preimageOptionalSHA-256 of preimage against an operator macaroon (L402)Payment hashInterop with L402 clients
pp-tokenOptionalPrivacy Pass publicly verifiable token (RFC 9578, blind RSA); a separate token typeToken nonceInterop with Privacy Pass issuers

Funding rails

RailHow credit is boughtRefund pathRole
Lightning BOLT12Client pays the operator’s offer; operator blind-signs unitsNew payment to the client’s offerReference rail
Cashu via LightningClient melts its ecash at its own mint to pay the operator’s invoice; the preimage proves paymentLightning paymentPortable for ecash holders; the operator trusts no mint
Cashu directOperator swaps tokens from mints on its own listFresh ecashOptional; the operator carries that mint’s risk
Ark VTXOOut-of-round transfer into a timelocked script with a presigned forfeit output, at least ark_minCooperative release, or unilateral exit after the timelockThe only fully non-custodial escrowed bond
HashcashWork instead of moneyNoneLast-resort fallback
Operator grantAttestation instead of moneyNoneFree 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 gets VERSION with the supported list.
  • Errors. INVALID, SPENT, KEYSET_EXPIRED, UNDERPAID, QUOTE_EXPIRED, UNSUPPORTED_TYPE, BINDING_MISMATCH, VERSION, RATE_LIMITED. HTTP carries them in Caparra-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 for refund_window and keeps verifying until final_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_key on 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.

InstrumentRefundableWhere the value sitsForfeit enforced by
StampNoConsumed by the actionNot applicable
Session credentialYes, minus the session fee, by re-issueOperator credit (capped)Operator withholds the replacement
Escrowed bond, ArkYes, by timelockClient’s VTXO in a shared scriptPresigned forfeit output; the operator only triggers it
Escrowed bond, CashuYes, by timelockThird-party mintOperator swaps before the locktime

Session states

  1. PRESENTED. The client sends an envelope of kind session with deposit proofs and blinded outputs for its replacement. Outputs total at most the deposit minus session_fee, MUST name the keyset active at this moment, and duplicate blinded messages are rejected.
  2. 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 published sid alone is useless.
  3. 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_window ends, 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 its detection_window, and a session can be flagged until it is released.
  4. 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-p2pk proof 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.

DestinationMeaning
selfOperator keeps it; wallets flag this
donatePaid to a published BOLT12 offer or address
burnSettled 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
arbiterHeld in 2-of-2 with a named arbiter for disputed reasons
Reason codeObjective test
RATEActions per interval above the published limit
QUOTABytes or actions per session above the published cap
PROTOCOLMalformed or abusive protocol use
REPLAYToken reuse detected
POLICYContent-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

  1. DNS discovery (shared with all carriers). The SVCB/HTTPS caparra SvcParam lists proof types and the manifest hash, next to the existing ech parameter.
  2. 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.
  3. TLS extension, ECH only. The client advertises the caparra extension 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 the tls-hello profile 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.
PathExtra round tripsLinkable
Pre-informed client with ECH0No
Uninformed client, HelloRetryRequest challenge1No
QUIC NEW_TOKEN handshake priority0Yes
No ECHFalls back to HTTP or SASLNo

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 by sid.
  • 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 Authorization already carries application auth, the client sends Caparra-Authorization instead.
  • On admission the server answers Authentication-Info: sid="s_9f…", exp=3600, receipt="…". Later requests send Authorization: Caparra sid="s_9f…", ts="…", sig="…", where sig is the K-signature over (sid, method, target, ts).
  • Native clients and agents use the tls-exporter binding 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 the nonce profile.
  • 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

ActionWire form
Clean closePOST {claim}/{sid}/close with a K-signature, or expiry
Claim replacementPOST {claim}/{sid}/release with a K-signature; idempotent until refund_window ends; answers Caparra-Status: released; sigs="…"; receipt="…"
ForfeitCaparra-Status: forfeited; reason="RATE"; receipt="…" on the 403 or 429 response
Buy creditPOST {fund} with the rail payload; answers with blind signatures
Withdraw creditPOST {fund}/withdraw with tokens and a refund destination
Forfeit logGET {log}/{epoch}
ErrorsCaparra-Error: SPENT (codes listed under Conformance details)

Other carriers

CarrierFormStatus
SASLCAPARRA 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 AUTHNormative
ESMTP (MX to MX)MAIL FROM:<a@b> CAPARRA=<token> extension; inbound MX has no SASLOptional
NostrA NIP adding a ["caparra", token] event tag and a NIP-42 AUTH variantOptional
ActivityPubPlain HTTP binding on the inbox POSTCovered by HTTP
gRPC, WebSocketHTTP binding on the upgrade or callCovered by HTTP

Interop with existing schemes

  • x402: publish caparra as 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 caparra as a payment method and bond as the first entry in the payment-intent registry.
  • L402: accept ln-preimage as 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.

ActionAssumed value to a spammerStampCost to a human
Forum post$0.0028 sat ($0.005)20 posts a day = 160 sat ($0.11)
Signup$0.0164 sat ($0.04)Once
Inbound mail$0.00052 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.

AttackSeverityMitigation in this specResidual
Abuse that never gets flaggedHighStamps priced above value per actionMedium: calibration is the operator’s job
Surge-price manipulationHighLoad pricing on stamps only, ceilings, fair queuingLow
Operator greed, false forfeitsHighReason codes, receipts, forfeit log, declared destination, Ark presigned forfeitsMedium: the self destination stays allowed
Gamed claim ratesMediumSigned admission counts, client-published receiptsMedium: denominators can be padded
Fake bonds from attacker mintsHighOperator-issued credentials; Cashu holders fund by paying the operator’s Lightning invoice, so no mint is trustedLow
Mint rug, censorship, surveillanceMediumMints off the admission path; NUT-28 blinding; batched Tor sweeps; capsLow to medium for cashu-p2pk users
Forfeit race against the locktimeMediumLocktime at least session plus detection window plus 24 h; forfeit earlyLow; NUT-11 post-locktime semantics to confirm
Replay across anycast points of presenceMediumPer-PoP keysets or a central atomic seen-set; ephemeral key signature; nonce or exporter bindingLow
0-RTT and resumption replayMediumNo new tokens in 0-RTT; resumption inherits sidLow
Verifier DoS with junk tokensMediumAddress validation, size and seen-set checks before elliptic-curve work, per-source budgetsLow
Session stuffing (many actions, one session)MediumPer-action stamps; QUOTA caps per sessionLow
Amount, keyset and timing fingerprintsMediumPower-of-two amounts, aged keysets, delayed issuanceMedium
Free-tier farming by AI CAPTCHA solversMediumGrants bound to origin and rate-limited; low weightMedium
Hashcash hardware asymmetryMediumLowest weight, last resort onlyHigh, accepted
Linkable mode becoming a global identityHighLinkability per origin only; cross-origin key reuse forbiddenMedium
Exclusion of people without walletsHighGrant tier and hashcash; reserved human capacityMedium
Legal exposure of operators and mintsHighClosed-loop operator issuers; foundation holds no fundsHigh until counsel confirms
Incumbent x402 wins by defaultHighBridge as an x402 scheme rather than competeHigh

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.

CrateResponsibilityBuilds on
caparra-coreEnvelope, manifest, receipts, ctx hashing, session state machine; no_std-friendlyciborium, secp256k1
caparra-issuerOperator keyset, blind signing, funding and withdrawal flowsCDK (Cashu Dev Kit)
caparra-verifyOffline verification, seen-set (memory, Redis, sled), check ordering, per-source budgetscaparra-core
caparra-logForfeit log epochs, Merkle roots, inclusion proofsrs_merkle or equivalent
caparra-httpTower middleware for axum and hyper, plus the caparra-gate reverse proxy binarytower, hyper, rustls exporter API
caparra-saslCAPARRA and CAPARRA-PLUS mechanismscaparra-core
caparra-fund-lightningBOLT12 offers, refundsLDK
caparra-fund-cashuOnline swap of incoming Cashu, cashu-p2pk proofsCDK
caparra-fund-arkEscrowed bond scriptsRust Ark client (beta)
caparra-walletClient wallet, budgets, receipts, cpr CLI, MCP serverAll of the above
caparra-quicNEW_TOKEN handshake priority (Experimental)quinn, small upstream patch
caparra-tlsECH-only TLS extension (Experimental)rustls fork behind a feature flag

Build order

  1. caparra-core, caparra-verify, caparra-issuer and the test vectors that the NUT will cite.
  2. caparra-http and caparra-gate, so any site can deploy behind a proxy.
  3. caparra-wallet with the cpr CLI and MCP server, so agents can pay from day one.
  4. caparra-fund-lightning and caparra-fund-cashu; then caparra-log.
  5. caparra-sasl and the Nostr relay plugin.
  6. caparra-fund-ark, caparra-quic, then caparra-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

DocumentVenueStatus target
Caparra envelope, operator credentials, receiptsCashu NUTAccepted NUT
NUT-24 extension: kinds, ctx binding, release and forfeit headersCashu NUTAccepted NUT
HTTP Caparra authentication schemeIETF individual draft (HTTP area)Standards Track candidate
SASL CAPARRA and CAPARRA-PLUSIETF individual draft, IANA SASL registryStandards Track candidate
SVCB caparra SvcParamIANA registrationRegistered
caparra method and bond intentdraft-httpauth-payment registriesRegistered
caparra x402 schemex402 FoundationAccepted scheme
Nostr NIP, ESMTP extension, MQTT profileNIPs repo, IETFOptional
TLS 1.3/QUIC bindingIETF individual draftExperimental

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.

DecisionPosition APosition BVerdict
Bond custodyCashu ecash: P2PK-locked proofs, offline-verifiable, native locktime refundNon-custodial: operator-issued blind tokens, funded by Lightning or ArkOperator-issued credentials in Cashu format; Cashu, Lightning and Ark are funding rails; third-party Cashu proofs optional for portable stamps
Economic coreRefundable bond (owner’s choice)Red team: bonds are nearly free for unflagged attackersStamps are primary; bonds are an optional, capped penalty layer
Mint allowlistsMandatory, to stop attacker mintsA new gatekeeper; avoid entirelyNo global or default allowlist; Cashu holders fund by melting to the operator’s own Lightning invoice, so no mint is trusted
Protocol layerTransport: admit in the TLS/QUIC handshake, universal across protocolsHTTP and SASL: abuse is judged where the semantics liveHTTP and SASL normative; TLS/QUIC a separate Experimental document
Transport forfeit signalTLS alert and QUIC error codeApplication receipt onlyApplication receipt only; transport admits, application judges
TLS path without ECHBond as a client certificate typeCollides with mTLS; no handshake carriage without ECHDropped; clients without ECH use the application binding
Forfeit destinationOperator keeps it (owner’s choice)Red team: default to burn or donateOperator chooses, must declare it; spec recommends non-operator destinations
Unlinkability promiseUnlinkable by constructionSmall operators link by IP and timing anywayGuaranteed between sessions and funding; not promised against small operators
Relationship to x402Compete with a Bitcoin-native standardSkeptic: bridge, don’t competeBridge: publish caparra as an x402 scheme
Thesis framingSuperintelligence and the human high groundSkeptic: unfalsifiable, deters operatorsSpam economics only
NameStampCollides with Bitcoin Stamps and the taken crateCaparra, 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