Beersy
BRC-196

STEAK with POTATOES

Buying a digital item through a Bitcoin payment used to leave a gap: the network could confirm your money moved, but nothing tied that payment to actually getting the thing you paid for, like a decryption key for a song. If the seller's server crashed or someone disputed the sale, a buyer had no reliable way to prove they paid and should receive the result, or to recover it later without paying twice.

Ty Everettchanged 1 Oct 202611 min read

Reference for an AI

Everything an assistant needs to answer questions about BRC-196 accurately, including what it depends on.

Summary

Why
A confirmed on-chain payment alone does not prove a buyer is entitled to receive a private digital result, so there was no standard way to link payment to delivery of that result, or to recover it safely after a failure.
What
BRC-196 defines , a signed private result released alongside an 's existing transaction receipt when a purchase's payment and release conditions are both satisfied.
How
A buyer calls prepare to freeze the purchase terms and release policy, submits the funding transaction to get back STEAK plus, once delivered, a signed POTATOES packet containing the secret, and can call recover later using the same acquisition ID if delivery failed or was lost.

What this lets you do

  • Freeze purchase terms and deadlines before funding a transaction
  • Receive a signed secret (POTATOES) only after payment is admitted and a release policy is met
  • Recover a lost or undelivered result without paying again
  • Choose a release policy of local admission, processor acceptance, or mined confirmations
  • Track purchase state through prepared, admission-pending, delivered, or failed outcomes

Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.

basket

This diagram is a placeholder based on the topic. A diagram drawn from this standard has not been generated yet.

The specification

Abstract and status

An submit already returns , the Submitted Transaction Execution AcKnowledgment. A can additionally release a result whose authorization follows from that admitted transaction. For a music purchase, the result is the decryption capability needed to play the purchased song.

means Private Overlay Topical Admittance-linked Transactional Outward Evidentiary Secret. The analogy is deliberate: you get potatoes back with your steak on private-overlay submits. The useful distinction is that STEAK describes topical processing, while POTATOES is a separately authorized private result. Neither is a mining-finality .

This proposed profile is negotiated through BRC-194. It uses BRC-192 encodings, BRC-195 private stores/trusted contexts, and BRC-193 errors. It does not add fields to the legacy STEAK object, whose validators and callers must continue to work unchanged.

1. Preparation before spending

A client first uses free catalogue lookup to select a seller and listing. Before constructing a purchase, it obtains a durable promise of the applicable release/recovery terms from that seller. POST /overlay/v1/purchases/prepare, selected with #steak-potatoes-v1, accepts:

type Prepare = {
  version: 1; requestId: string; topic: string;
  listing: Outpoint; assetId: Hex32; termsDigest: Hex32;
  recipient: Identity; request: Bytes
}
type PurchaseTerms = {
  version: 1; acquisitionId: Hex32; requestDigest: Hex32;
  seller: Identity; recipient: Identity; topic: string;
  listing: Outpoint; assetId: Hex32; termsDigest: Hex32;
  domainProfile: string; domainEvidence: { schema:string; bytes:Bytes };
  releasePolicy: ReleasePolicy;
  purchaseUntil: U64; recoveryUntil: U64
}
type SignedPurchaseTerms = { body:PurchaseTerms; signature:Bytes }
type ReleasePolicy =
  | { kind: 'local-admission' }
  | { kind: 'processor-accepted'; identity: Identity; policy: string }
  | { kind: 'mined'; confirmations: U32 }

The caller equals recipient. Request IDs use BRC-193's constraints. The acquisition ID is digest("purchase", {chain,seller,recipient,topic,requestId}), and request digest is digest("purchase-request", Prepare). The result is a signed packet of type purchase-terms, signed by seller. It freezes private readiness and request semantics before funding; identical retries return the original complete signed terms, while changed bodies conflict. domainProfile names an installed purchase validator. The purchase-domain identifier for BRC-197 is https://bsv.brc.dev/tokens/0197#listing-purchase-v1; it uses the concrete executable https://bsv.brc.dev/tokens/0197#revenue-listing-v1 family defined there. Selecting the domain requires exact family-byte, Script and lineage verification, not only a named predicate. A mined policy requires a positive confirmation count. Processor acceptance needs attributable evidence for the exact transaction under the named policy; a broadcast acknowledgement alone is insufficient.

recoveryUntil >= purchaseUntil + 86400. The buyer MUST NOT begin a new purchase at or after purchaseUntil. The seller retains the original obligation through . As in BRC-195, valid first delivery during recovery is accepted without pretending raw transaction bytes contain a reliable creation time. Preparation does not reserve an on-chain against competing buyers and does not charge an HTTP fee.

2. Submission and response envelope

POST /overlay/v1/purchases/submit accepts:

type PurchaseSubmit = {
  version: 1; acquisitionId: Hex32; txid: Hex32; beef: Bytes
}
type PurchaseResult = {
  version: 1; acquisitionId: Hex32; txid?: Hex32;
  status: 'prepared' | 'admission-pending' | 'admission-rejected'
    | 'admitted-delivery-pending' | 'delivery-failed' | 'delivered' | 'expired';
  steak?: STEAK; potatoes?: SignedPotatoes; recoveryUntil: U64;
  decision?: { reason:string; policy:ReleasePolicy; evidence:Bytes;
    decidedAt:U64; globalOutcome:'unknown' };
}
type PotatoesBody = {
  version: 1; acquisitionId: Hex32; requestDigest: Hex32;
  seller: Identity; recipient: Identity; topic: string;
  txid: Hex32; assetId: Hex32; termsDigest: Hex32;
  releasePolicy: ReleasePolicy;
  evidenceDigest: Hex32; schema: string; secret: Bytes;
  issuedAt: U64; recoveryUntil: U64
}
type SignedPotatoes = { body: PotatoesBody; signature: Bytes }
type ReleaseEvidence = {
  chain: Chain; txid: Hex32; policy: ReleasePolicy; acceptedAt: U64;
  processorEvidence?: Bytes;
  blockEvidence?: {
    blockHash: Hex32; height: U64; tipHash: Hex32; tipHeight: U64; beef: Bytes;
    contextId:string; chainPolicyDigest:Hex32
  }
}
type PurchaseEnvelope = {
  result: PurchaseResult; releaseEvidence?: ReleaseEvidence
}

STEAK is the existing unmodified BRC-22 object. A response has no STEAK until durable topical processing succeeds; admitted/delivered responses MUST include it. Only delivered includes POTATOES. evidenceDigest commits to using digest("release-evidence", body). processorEvidence contains the policy-defined signed evidence bytes; fields inconsistent with the selected policy are forbidden. Submit and recovery return PurchaseEnvelope. Missing required evidence leaves delivery pending. Delivered MUST include the exact release evidence committed by POTATOES; its policy and txid match the prepared terms and result. Local admission records acceptedAt and has neither optional field. Mined evidence must verify inclusion and ancestry to the stated , with tipHeight - height + 1 at least the selected confirmations; arithmetic, chain identity and block hashes are checked rather than trusting displayed heights.

POTATOES uses packet signature type potatoes, signed by seller. The secret bytes follow the named installed schema, which MUST define recipient binding, content/key and any licensing evidence. Raw secret schemas require BRC-103/104 over HTTPS; a schema may instead use BRC-78 encryption to recipient. A signature is evidence of what a seller returned, not proof that the key decrypts the intended content. The client verifies that relationship before calling the acquisition usable.

The topic verifies exact purchase conditions and the acquisition/request/recipient binding in the transaction, using the frozen preparation terms. Merely admitting some output in the topic is insufficient. A valid public txid or a copied purchase transaction is not a bearer coupon: only the recorded recipient can recover the result. This profile does not permit a second BRC-105 song charge at submit or recovery.

3. Atomicity and recovery

Before external admission work, persist a unique association of acquisition ID, transaction ID, recipient and frozen terms. One acquisition cannot release against two transactions. The public admission, protected release intent, saved STEAK and replay fence MUST either commit atomically or have a staged durable coordinator that can establish those facts without re-admitting or double-counting the purchase. A transient in-memory callback or a plugin error logged after STEAK is not sufficient.

Only after durable admission and the chosen release policy are satisfied may the result be marked delivered. Failures after public admission yield admitted-delivery-pending, preserving saved STEAK and a recoverable private intent. Repeated submissions merge valid proof variants for the same transaction and return the existing outcome. Public GASP replay and ordinary reindexing MUST NOT invoke private release. Private-byte absence on a replica is delivery-pending/unavailable, not an excuse to charge again.

If the exact prepared purchase was already publicly admitted through another path, an authorized recipient submission MAY establish the private association from the retained verified admission and preparation records, without admitting or accounting for it twice. The same binding and release checks still apply. An empty newly-admitted list in a duplicate STEAK is not proof that the earlier purchase failed. Conversely, discovering a public transaction in history cannot create a preparation or authorize its caller retroactively.

POST /overlay/v1/purchases/recover accepts {version:1, acquisitionId:Hex32} and returns the same wrapper to the authenticated recipient without payment. If admission has not begun, return prepared (or expired at the unpinned recovery deadline), with no claim that a submitted transaction failed. Wrong recipients receive the same not-found behavior. The provider retains the result or sufficient protected material to reproduce it through recoveryUntil. Equivalent regenerated encrypted envelopes are allowed, but asset, rights, recipient, accepted transaction and secret capability cannot change.

When two listeners spend the same listing, at most one chain spend can survive. A client MUST resolve its exact first transaction's outcome before authorizing another purchase/payment. A rejection containing only “input spent” does not identify the winner. Check exact txid, evidence and the provider's recovery state; uncertainty remains pending. A retained local rejection permits discussing a replacement; it does not prove that the first signed transaction cannot later settle. Any replacement uses a new request ID and renewed explicit buyer authorization acknowledging unresolved or later-settlement risk. An unconstructed prepared request can be abandoned after its private-fulfillment reservation expires; this is never an exclusive reservation of the UTXO.

An unconfirmed admission or processor acceptance can later conflict or be reorganized. A key already delivered cannot be recalled. The seller chooses and discloses this risk through ; a client must not display local admission as mined settlement. Reassess chain evidence and public projections without deleting the acquisition history. The covenant constrains payment; it cannot force an off-chain seller to deliver a key, so this is not an atomic fair-exchange claim.

4. Adapters, compatibility and conformance

Use an optional admission-result companion around the existing engine and an explicit selected-host client API. Do not append potatoes to an ordinary STEAK, silently change /submit response parsing, or drop per-host private results inside an aggregate broadcaster. Legacy clients remain on legacy routes. Private publication and recovery obligations from BRC-195 remain necessary even when payment moves from HTTP lookup to a covenant.

Required tests cover preparation replay; body conflict; no-key readiness rejection; exact recipient/acquisition binding; rejection before admission; crash before and after admission commit; already-public admission with an empty duplicate STEAK; release-policy delay; lost response; repeated evidence variants; unauthorized recovery; GASP replay with no secret release; selected-host routing; races with an uncertain earlier purchase; wrong decryption capability; and a later chain conflict. A real reference implementation must demonstrate both immediate local release and a delayed release policy without fabricating confirmation evidence.

5. Reservation, release and recovery details

Preparation validates selected seller/topic/profile/chain, recipient=caller, exact listing , asset/terms/request schema, domainEvidence and private result capacity before committing a promise. Its complete Prepare digest, original signed PurchaseTerms and all deadlines are immutable on retry. The domainEvidence schema is an installed immutable IRI; for BRC-197 it is https://bsv.brc.dev/tokens/0197#lineage-package-v1 and bytes are UTF-8 JCS LineagePackage. It is returned during preparation, not inferred from a listing ID. RecoverySeconds and an applicable Offer's longer promise have exactly BRC-195's deadline/late-first-delivery precedence. Retain sufficient protected material to deliver after catalogue turnover; preparation never locks a Bitcoin output against other buyers.

A submit is processed in this order: authenticate current recipient; resolve original capability/terms; verify complete bounded target /raw txid; check configured chain, exact listing consumption, installed family/domain, asset, terms, receipt acquisition ID, request digest and recipient; then atomically reserve (acquisitionId,txid) and durable admission work before any external admission. A malformed or conclusively domain-invalid first candidate returns invalid without reserving txid. Incomplete verification is pending bounded candidate work and cannot invoke admission; once any external-effect reservation is committed the acquisition cannot switch txid. Alternate proofs for the same raw transaction merge. One acquisition/txid may recover already committed topic processing; neither a duplicate STEAK nor a public reindex job creates a new private release.

The state transition table is:

FromTrigger and durable effectTo
absentvalidated preparation and protected capacity committedprepared
preparedvalidated candidate and unique admission intent committedadmission-pending
preparedrecovery deadline, no pinned candidate/effectexpired
admission-pendingretained definitive local rejectionadmission-rejected
admission-pendingtopic processing/STEAK linked exactly onceadmitted-delivery-pending
admitted-delivery-pendingselected release evidence and private envelope committeddelivered
admitted-delivery-pendingretained irrecoverable delivery failuredelivery-failed
deliveredauthorized recovery/reissue of same rightdelivered

Uncertainty after an external call stays pending and is reconciled by its saved operation/txid. Definitive local rejection stores decision reason, selected policy, bounded canonical evidence, time and globalOutcome=unknown. All post-reservation statuses carry txid; prepared/expired do not. STEAK is required only in admitted-delivery-pending/delivery-failed/delivered. Potatoes and releaseEvidence are required exactly in delivered; decision is required exactly in admission-rejected/delivery-failed and forbidden otherwise. No negative service response establishes that a transaction is globally unable to mine. A failed candidate that was pinned before deadline remains recoverable to a retained decision; an admitted purchase awaiting delivery survives deadline as an outstanding obligation under BRC-195's rules.

A local-admission ReleaseEvidence has neither processorEvidence nor blockEvidence. Its acceptedAt is the provider's durable topical acceptance time and relies on that provider's signed response. A processor-accepted policy requires processorEvidence and forbids blockEvidence. This BRC registers policy https://bsv.brc.dev/overlays/0196#processor-attestation-v1: processorEvidence is UTF-8 JCS of a BRC-192 signed packet of type processor-acceptance, body {version:1,chain:Chain,txid:Hex32,policy:string,acceptedAt:U64}. The signature signer equals ReleasePolicy.identity, policy equals this IRI, and chain/txid/acceptedAt equal ReleaseEvidence. The processor asserts it accepted that exact transaction under its published policy; it does not assert mining or universal availability. A client must have explicitly trusted that identity for this weaker acceptance mode. Future processor formats use different IRIs.

A mined policy requires blockEvidence and forbids processorEvidence. Its BEEF proves the target's inclusion in . Heights and tipHash are not sufficient evidence of a best chain. The verifier must resolve both against its configured genesis and a locally selected immutable chain-tracker context identified by contextId/chainPolicyDigest. The tracker supplies authenticated headers/ancestry, difficulty/work validation under the configured chain policy, and inclusion on its selected best chain; the caller bounds bytes, header count, work and deadline before retrieval. Unknown headers, budget limits or a changed view are unresolved/limited/context-changed, never invented confirmation depth. Release requires a positive selected confirmation count, contiguous verified ancestry, checked tipHeight-height+1, and a final same-context check at commit. acceptedAt is when this assessment first satisfied release policy; it is not claimed block time. A reorganization invalidates current assessments but not the historical delivery event. Claiming this release mode requires real inclusion and reorganization qualification; a mock tip string is insufficient.

The buyer separately tracks received, validating, usable, and unusable(reason). Delivered is the seller's durable issuance statement, not an assertion that decryption or licensing succeeded. Wrong key, missing/invalid license, overbroad key grant, unavailable ciphertext or invalid settlement remains unusable/pending as appropriate; uncharged recovery can repair transport/encryption/signature packaging, not buy again implicitly. Reissue may change signature randomness, encryption , wrapper issuedAt and an equivalent License's non-rights issuance fields. It MUST retain acquisition/request, asset/version, exact recipient, paid transaction, accepted rights/key periods/key commitments, original releaseEvidence and its digest, original deadlines (or an explicit extension), and settlement identity. A changed accepted right is not equivalent recovery. Clients keep both the historical release evidence and today's separately assessed currentness. Once released, recovery does not re-gate the old grant on today's confirmation depth. No rule here reverses a disclosed key or promises refunds/fair exchange.

BRC-192 spend reconciliation determines the client's currently observed compatible history, not this seller's release authorization. A first-seen selection or non-final replacement is not ReleaseEvidence. Reorg/conflict updates may change present currentness and projections; they neither create a second acquisition nor erase the original delivery/recovery obligation. The BRC-197 domain remains final-sequence/zero-locktime only.

Was this helpful?

Search Beersy

Search standards by number, title, author or topic