Abstract
This document specifies what happens when a person with no wallet arrives at a Metanet application and is invited to connect one.
It defines three things. The connect surface: the control a site offers, the vocabulary it uses, why that vocabulary is not interchangeable with the vocabulary of signing in, the manifest entry by which a site declares whether an auction runs in front of its users at all, and the obligation to put the person back where they were once the connecting is over. The chooser: the dialogue that opens when no wallet is reachable, which MUST present at least two wallets from at least two vendors, because a list of one is a product recommendation and a list of two is a demonstration that the account is not the site's to keep. And the onboarding auction: sealed-bid, first-price, decided in under 250 milliseconds, in which wallet vendors bid for placement by committing to fund the newcomer's wallet.
The bid is the bounty. A vendor wins on the amount the person receives and on nothing else; what it separately pays the auction house is a billing matter that MUST NOT influence which campaign wins. The amount is never transmitted, only a commitment to it, so a client that is never given a number cannot rank by it and a scraper that reads every field of every offer learns nothing it could sort. Every bid is published and only the winner's is opened, which lets any losing sponsor establish from its own key that its bid was in the auction and that the winner outbid it.
Targeting is a client-evaluated predicate: a campaign declares its audience, the chooser checks it locally against Bitcoin Attestation Protocol attestations or BRC-52 certificates the person already holds, and discards what does not match. Nothing goes back. A bounty MUST be locked before it is bid, as a BRC-227 voucher batch derived from the sponsor's own key, and the commitments come from the same key and the same reference, so commitment i and voucher i are the same slot and the sponsor stores nothing.
Motivation
BRC-137 established that an application meeting a wallet-less visitor MUST NOT show them an error, MUST detect their operating system, and MUST resolve install recommendations from a catalog. That solved the dead end. It left three things open, and each is where the Metanet's central claim is either made or quietly dropped.
One wallet is not a choice, and a choice is the whole argument. What a Metanet application offers a newcomer is not better sign-in; it is that the account is theirs and travels. That claim is unfalsifiable in the abstract and immediate in a list: two wallets, from two companies, either of which opens the same identity. BRC-137's catalog can return one entry and conform. This document requires two, from two vendors.
Nobody is paying for the funnel, so the funnel does not exist. A vendor's most expensive problem is a person who has heard of self-custody and will not install anything to try it. A site's most expensive problem is a visitor who bounces at the wallet requirement. The two are complementary and unconnected. Advertising solved the analogous problem with a real-time auction and then spent twenty years demonstrating every way that mechanism can be turned against the person it is shown to. The mechanism is not the problem. The direction of the payment is.
A bounty that can be seen is a bounty that will be farmed. If the offer carries the amount, the amount reaches the device, and whoever wants to harvest it reads it out of the network tab, sorts, installs the top one, and repeats. Asking an interface not to render a number it possesses is not a control. So the offer carries a commitment instead, which the vendor cannot later reduce and the recipient cannot read.
What is actually being specified
Wallet onboarding is the occasion for this document rather than the whole of its subject, and a reader who takes it for a standard about install prompts will misjudge which rules matter.
What is specified is an attention market whose ranking variable is the benefit to the person whose attention is bought, made structural rather than promised. Three inversions produce it, each enforced by what the wire carries:
- The payment points at the person. A slot is won with the amount delivered to the newcomer, and section 4.3 forbids ranking on anything else. What the buyer pays the intermediary is a separate number that no ranking may consult.
- No profile is assembled, because the description travels outward. The audience is declared by the buyer and evaluated on the person's device. Section 4.2 forbids an identifier in the request, and at the moment it is issued there is not one in existence to forbid.
- The sortable number is never transmitted. Only a commitment to it. A client not given a number cannot rank by it, so the property survives a careless implementation and a hostile one alike.
Nothing in the three is specific to wallets. What is specific to wallets is section 3, and section 3 is there because a market that recommends one product to everybody is not a market.
Prior Art
A status code reserved for exactly this, and left unspecified. HTTP has carried a response for the case where a request has several valid answers and a person has to pick one since HTTP/1.1 in 1997. MDN describes it:
In agent-driven content negotiation, the request has more than one possible response and the user agent or user should choose one of them. There is no standardized way for clients to automatically choose one of the responses, so this is rarely used.
Read that as a summary of the problem this document addresses and every clause lands. There is more than one possible response: section 3.1 requires at least two wallets from at least two vendors. The user agent or user should choose one of them: the agent detects the platform and the person picks the wallet. And the reason the code went unused is the reason the wallet chooser barely exists today, which is that nobody wrote down how the choosing works. Sections 3.3 and 3.4 are that missing part, and they are why only order and inclusion are for sale.
The parallel is not decorative and the number is claimed for it. What differs is that a content negotiation offers alternative representations of one thing, whereas here the alternatives are rival products from rival companies, so the mechanism needs rules about who may pay for position and what they may not buy with it. HTTP never needed those. A market does.
A choice screen sold by auction, and what went wrong with it. After the European Commission's 2018 Android decision, Google introduced a search choice screen in the EEA and allocated its rival slots by sealed-bid auction: providers bid a per-installation price payable to Google. Ecosia refused to participate, DuckDuckGo and Qwant objected that the format converted a remedy into a revenue stream, and in 2021 Google abandoned the auction and made the screen free. The failure was not the auction. It was that the ranking variable was what the bidder paid the intermediary, so the screen sorted by who could most afford to be there. This document is the same mechanism with that variable replaced, and section 4.6 is where the replacement is argued.
Real-time bidding. OpenRTB, standardised by the IAB in 2010, supplies the shape and the timing discipline of section 4: a bid request describing a slot, fanned out, resolved against a deadline in the low hundreds of milliseconds. What is refused is its contents. An OpenRTB request carries a user identifier and whatever segment data the exchange has accumulated, because the bidder is buying a person. Here the bidder buys a placement in front of an audience it described in advance, the description is evaluated on the person's own device, and the request carries no identifier because none exists yet.
What actually matters in auction design. Klemperer's 2002 survey argues that the fine points of pricing rules matter far less than collusion, entry deterrence and predation, and that auctions fail in practice for those reasons rather than for theoretical ones. It is cited here as a warning against this document's own preoccupations: sections 4.5 to 4.7 spend their length on commitments and pricing, while the two failures most likely to matter are named in the Security Considerations and are exactly Klemperer's, a house colluding with a sponsor and a capital requirement that keeps small vendors out.
Onboarding by gift. BRC-227 established the payer/owner decoupling that makes cold-start onboarding possible: a publisher pre-funds a voucher, a newcomer's freshly generated key takes ownership of what it buys, and the newcomer needs no prior coins. Section 7.1 requires that construction, and section 4.5 borrows the same derivation a second time for the commitment salts, so one key and one reference generate both batches and the sponsor keeps no state.
Specification
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in RFC 2119.
Two words are reserved. "Bounty" always means the amount a sponsor commits to deliver to a newcomer, never a fee paid to anyone else. "Sponsored" always means placement was determined by the auction of section 4, and is the only claim a surface may make about the commercial arrangement behind an entry.
This document refines BRC-137. Its four login buckets, operating-system detection, catalog contract and connect decision flow apply unchanged and are not restated. This document specifies the Metanet bucket's control (section 2), the contents and composition of BRC-137's no-wallet view (section 3), and a fourth ordering mode for its ordering policy (section 4). Where the two could be read to conflict, the narrower requirement here governs the surfaces this document names.
Versioning. Every object here carries a version field, and 1 is the version this document defines.
- A party MUST reject an object whose
versionhas a major component it does not implement, and MUST treat the sender as non-conforming rather than guessing. - A party MUST ignore fields it does not recognise and MUST NOT fail on a minor version it does not implement. New fields arrive in minor revisions; the meaning of an existing field does not change within a major version.
- A chooser rejecting an offer on version grounds MUST treat the slot as unfilled, per section 4.9, and MUST NOT surface an error.
1. Terminology
- Connect surface: the part of an interface through which a user brings a wallet, as distinct from any part through which the application issues a credential of its own.
- Chooser: the dialogue presented when a user asks to connect, per the two entry points of section 3.1.
- Slot: one position in the chooser's list, rendered as one card.
- Task reference: what the user was doing when they operated the connect control, recorded so they can be put back. Section 2.7.
- Sponsor: the party funding a campaign, identified by an identity key. In practice a wallet vendor, though nothing requires it.
- Campaign: a signed object declaring a wallet entry, an audience, a bounty and a budget. Section 4.3.
- Campaign reference: the 32-byte value from which a campaign's commitment batch and voucher batch are both derived. Sections 4.5 and 7.1.
- Commitment batch: the ordered list of bounty commitments a sponsor pre-computes and hands to an auction house. Section 4.5.
- Auction house: the party that receives bid requests, ranks campaigns and returns offers. The only party that sees more than one sponsor's bounty amounts before settlement.
- Offer: a signed object naming one wallet, one sponsor and one bounty commitment, valid for one auction. Section 4.4.
- Bounty: satoshis a sponsor commits to deliver to a newcomer's identity key. Never a placement fee.
- Newcomer: the person at the connect surface, whether or not they already hold a wallet.
- Bid set: every eligible campaign's commitment for one auction, published together and opened only for winners. Section 4.7.
- Settled record: the auction house's public account of a decided auction. Section 4.7.
- Billing statement: what a house charged one sponsor, addressed to that sponsor and never published. Section 4.8.
- Pending record: what a site stores before a newcomer leaves to install, and matches when they return. Section 6.1.
- Connect receipt: a certificate, issued by the site, binding an auction to an identity key. Section 6.2.
- Funding note: the wallet's record of a claim it made without asking. Section 6.4.
- Maturity: blocks that MUST pass after a receipt before a bounty is claimable. Section 6.6.
- Refusal: a sponsor holding a locked voucher and declining to release it against a valid claim. Section 7.4.
- Retention proof: the newcomer's signed single-bit assertion on which a site's share depends. Section 7.5.
2. The Connect Surface
2.1 The control
An application that speaks BRC-100 MUST offer a control that connects a wallet, labelled with connect vocabulary.
- The label SHOULD be "Connect wallet", or a localised equivalent whose verb means to attach something the user already has. BRC-137 section 1 states the corresponding rule as a SHOULD and this document does not overrule it on wording it did not introduce.
- The label MUST NOT be, or contain, "Sign in", "Log in", "Sign up", "Register", "Create account", or a localised equivalent. Those verbs describe an application issuing a credential and keeping the record behind it, which is the opposite of what the control does. The prohibition is the MUST and the preferred wording the SHOULD, because the harm is the wrong word rather than the absence of one particular right one.
- Protocol identifiers MUST NOT appear on it, per BRC-137 section 1.
Almost nobody arriving at a Metanet application will read an explanation of what is different about it, and for most of them the label on the button is the entire explanation they will ever get. "Sign in with your wallet" teaches that the wallet is a credential the site accepts, which is what an OAuth button is. "Connect wallet" teaches that the wallet is a thing the user brought. One of those is true.
2.2 Connect is not sign-in, and the surface MUST say so once
Where the connect control appears alongside any control that issues an application credential, including the custodial and multi-chain options of BRC-137 sections 7 and 11:
- The connect control MUST be distinguishable by more than position: a separator, a heading, or a caption marking it as a different kind of act.
- The surface MUST carry a plain statement of what the difference is, saying that the account is the user's and that the application does not hold it. It SHOULD be at most two lines. It MUST NOT use "self-custody", "non-custodial", "sovereign" or "decentralized" without saying what they mean alongside, and SHOULD avoid them entirely.
- That statement MUST be visible without navigation, hover or expansion. A disclosure requiring a click has been read by nobody.
- It MUST NOT be a barrier: any other login method MUST remain completable without dismissing it, per BRC-137 section 9.
The two-line bound is from BRC-110 section 6, for the reason that document gives: an explanation of a benefit is retained where the benefit is offered, and not otherwise.
2.3 Disconnect
An application offering connect MUST offer disconnect, reachable in no more steps than connect was.
- Disconnecting MUST end the application's access and MUST NOT delete, deactivate or anonymise anything the user owns.
- It MUST NOT be presented as account deletion, or confirmed with warnings framed as loss.
- Where the application holds derived data, disconnecting SHOULD state what it keeps and for how long, per BRC-151.
A connection the user cannot withdraw as easily as they granted it is a credential wearing a connection's label, and the second sentence of section 2.2 becomes false.
2.4 What the surface MUST NOT do
- It MUST NOT gate content or functionality that does not require a wallet, per BRC-110 section 2 and BRC-137 section 7.1.
- It MUST NOT open the chooser automatically on arrival. A modal shown to somebody who has not asked for it is dismissed unread, which spends the one moment this document exists to use well.
- It MUST NOT state an amount, a range, or a comparison of what any wallet pays, and MUST NOT use the vocabulary of reward, bonus, prize or free money. It MAY describe how the market works, within section 3.5.5. The distinction is between explaining an arrangement and making an offer.
- It MUST NOT claim the user's wallet will work with every application, only that the wallets shown work with this one and with each other.
2.5 Declaring an auction house
A site running the auction of section 4 MUST declare where, in the metanet namespace of its /manifest.json, alongside the metanet.trust object of BRC-68 and the metanet.groupPermissions object of BRC-116.
{
"metanet": {
"connect": {
"version": "1.0",
"auction": "https://house.example/.well-known/metanet-connect/bid",
"catalog": "https://bsvradar.com/api/apps/filter",
"chooserSlots": 2,
"certifier": "0295bf1c7842d14babf60daf2c733956c331f9dcb2c79e41f85fd1dda6a3fa4549"
}
}
}
| Field | Requirement | Meaning |
|---|---|---|
version |
MUST | The version of this document implemented. "1.0" here. |
auction |
MAY | Absolute HTTPS URL of the auction house's bid endpoint. Absent means the site runs no auction and the chooser is entirely catalog-filled. |
catalog |
SHOULD | Absolute HTTPS URL of the wallet catalog, per BRC-137 section 3. Absent means BRC-137's hardcoded shortlist. |
chooserSlots |
SHOULD | Total slots the chooser presents, n in section 3.4. Minimum 2, default 2. Distinct from the bid request's sponsoredSlots, which is floor(n / 2). |
certifier |
MUST when auction is present |
The identity key with which this site signs connect receipts, per section 6.2. |
- A site publishing no
metanet.connectobject runs no auction. A chooser MUST NOT probe a well-known auction path and MUST NOT fall back to a house of its own choosing. A site that has not named a house has not consented to one bidding in front of its users. certifierMUST equal thecertifierof every receipt the site issues, and MUST NOT equal the key with which the site authenticates users.- A sponsor verifying a receipt MUST resolve the certifier by fetching
https://<fields.origin>/manifest.jsonand comparingmetanet.connect.certifier. This binds a receipt to a domain; without it, "the certifier is a site" in section 6.5 names nothing checkable. - An auction house MUST attribute every bid request to a site that declares it, per section 4.2.5, and
certifieris how. auctionandcatalogMAY point at the same operator. They are separate fields because they answer separate questions, and conflating them gives one party both the sponsored and the unsponsored slots.
The manifest is the right home because the site is the party consenting: it chooses whether an auction runs in front of its users, which house runs it, and how many slots are offered. In a file the site publishes, that answer is inspectable without running the page.
2.6 The browser's permission comes between this control and the wallet
Operating the control begins a BRC-185 discovery round, and since Chrome 142 the first loopback request in that round is gated by a browser permission. The dialog it raises is not this application's, grants once per origin, and a refusal ends loopback discovery for that origin until the user reverses it in browser settings.
- An application MUST NOT treat a refusal of that dialog, or a discovery round blocked by it, as the absence of a wallet. BRC-185 section 4.1 already states that a policy-blocked scan is
blockedand not evidence of absence. The chooser of section 3 is reached on coverage, and coverage that was never gathered is not an empty one. - The ordering of surfaces between this control and the first probe is specified separately, in the User-Initiated Browser Permission Requests for Wallet Discovery proposal. This document does not restate it, and the two compose in the obvious order: this document governs the control and the chooser, that one governs what happens in between.
2.7 Returning the user to what they were doing
Almost nobody operates the connect control as the thing they came to do. They were reading an article, part of the way through a form, or at the end of a purchase, and connecting is an interruption they accepted in order to finish. Between the control and a connected wallet the person may cross a browser permission dialog, a wallet application, an application store, and on a phone a tab the operating system discarded while they were away. Each of those is a place the task can be dropped, and an application that drops it has charged the person twice for one decision.
- Before any surface this document specifies is shown, the application MUST record a task reference: the location the user is at, together with whatever state that view holds which the location does not encode. It MUST be sufficient to reconstruct what the person was doing without asking them.
- The application MUST restore the task on every exit from the connect flow, not only on a successful connection. The exits are: a wallet connected; the chooser dismissed without a choice; a discovery round blocked by the permission of section 2.6, which BRC-185 section 4.1 requires be reported as
blocked; a wallet permission prompt denied, which BRC-219 requires be completed with an error rather than left pending; and the install detour of section 6.1. A refusal is a decision the person made, and the thing they were doing before they made it is still there. - A screen whose only content is that a wallet is connected MUST NOT be the end of the flow. Confirmation MAY be shown, as something laid over the restored task rather than in place of it.
- Restoration MUST NOT assume the document survived. On a phone the tab is routinely discarded while the user is in the wallet application or the application store, so a task reference held only in the page's memory is gone before it is needed. It MUST be stored where it outlives the document, keyed to the browsing session by the same means as the pending record of section 6.1.
- Where the return is by the same-device link of BRC-185 section 8.4, the application MUST resolve the returned state value to the task reference, and MUST NOT rely on the returned URL alone to carry the task. That value is a correlator with no task identity of its own, and this section is where it acquires one. The manual return path that the same section requires when automatic return fails MUST arrive at the task rather than at the site's front page.
- Across the install detour of section 6.1 the task reference travels in the pending record and MUST NOT outlive it. It SHOULD expire far sooner. An article is worth returning to an hour later; a half-completed form is a liability by then, and an application that restores a stale one has restored the wrong thing.
- Where the task cannot be restored, the application MUST place the user at the nearest point they will recognise and MUST say what was not kept. Substituting a default landing page in silence makes a loss look like a destination.
- Restoring a task MUST NOT re-open the chooser, which section 2.4.2 forbids in any case, and MUST NOT re-run an action the person had not yet confirmed.
BRC-137 section 9 states the matching rule for the code: every successful login resolves to one application session of the same shape, so application code never branches on how the user logged in. This section is that rule applied to the person rather than to the program. The session does not branch, and neither should they. Whatever route they took through the control, the chooser, a permission dialog and an application store, they come back to the paragraph they were reading.
3. The Chooser
3.1 At least two wallets, from at least two vendors
A chooser opens on either of two entry points. It MUST open when a user activates the connect control and no wallet is reachable over any BRC-100 substrate, per BRC-137 section 4. It MAY also open on explicit request from a user who already has a wallet connected, and an application SHOULD offer that control wherever the connected wallet is shown.
The second entry point is not an afterthought. A person acquiring a second wallet is the only person in this document who can demonstrate portability rather than be told about it, and it is the only circumstance in which the holdsWallet term of section 5.2 can be satisfied.
- The chooser MUST present at least two wallet entries.
- They MUST be published by at least two distinct vendors. Two builds of one vendor's wallet, or two skins of one codebase, count as one.
- Every entry MUST be installable on the platform detected per BRC-137 section 2; one that is not MUST be omitted, not greyed out. At the second entry point the wallet the user already holds MUST also be omitted, since a list offering somebody what they are holding has not offered them a choice.
- Where fewer than two conforming entries exist for the platform, the application MUST say so, name what it has, and offer the fallbacks of BRC-137 section 7. It MUST NOT pad with an incompatible entry, a placeholder, or a second entry from the same vendor.
- The chooser SHOULD present between two and five entries. Above five it is a directory, and somebody deciding whether to install their first wallet is not helped by a directory.
Rule 2 is load-bearing, and it is worth saying why a specification requires a competitor's presence. Every other statement an application makes about portability is a promise about the future made by the party who benefits from it being believed. Two vendors in a list, either of which opens the same identity, is the same claim in a form the reader can act on in five seconds. It is also the claim most likely to be dropped first, because the vendor with the largest install base has an obvious interest in a list of one. Requiring two is the cheapest possible enforcement of the thing the ecosystem says it is for.
3.2 The wallet card
Every field on every card comes from the catalog. A sponsored entry and an unsponsored one are rendered from the same source, and an offer selects which catalogued wallet occupies a slot rather than supplying what the slot says. Section 4.4 carries no wallet object for that reason: there is no field in which a sponsor could ship its own icon, its own description, or its own install target. A chooser that renders any card field from a bid response is non-conforming.
Every entry MUST be rendered as a card carrying exactly these fields:
| Field | Requirement | Content |
|---|---|---|
icon |
MUST | Square, rendered edge to edge. |
name |
MUST | The wallet's name as its vendor publishes it. 1 to 32 characters. |
vendor |
MUST | The entity publishing the wallet, where it differs from name. 1 to 48 characters. |
description |
MUST | What this wallet is particularly good at. 1 to 140 characters. |
installUrl |
MUST | The install location for the detected platform. |
sponsored |
MUST when true | The disclosure of section 3.5. |
selfCustody |
MUST | Whether the wallet holds the user's keys. Rendered only when false. |
- Every field above MUST be read from the catalog entry the offer names, per section 4.4.2. A chooser MUST discard an offer naming a wallet it cannot find in the catalog it is already using for the unsponsored slots of section 3.4.2, and MUST NOT render a partial or reconstructed card in its place.
vendorMUST be present and MUST NOT be inferred from the wallet's name. Choosing between two wallets is choosing between two companies, and the company is the fact a person is likeliest to already have an opinion about. Where the two names coincide the field MAY be rendered once.descriptionSHOULD describe a capability or an audience rather than a category. "Fast payments and a built-in browser" and "Built for people who have never used Bitcoin" are what the field is for. "A BSV wallet", "Secure and easy" and "The best wallet" are not: the first distinguishes nothing, the others are claims the surface cannot check. This is a SHOULD because whether a sentence distinguishes anything is a judgement, and a MUST no implementation can evaluate is decoration.descriptionMUST NOT contain a superlative ranking the entry against another in the same chooser, a price, or a call to action.descriptionMUST NOT mention a bounty, an amount, or the sponsorship arrangement, per section 2.4.3. Explaining the arrangement is the surface's job, in the words of section 3.5.5, and not something a vendor writes its own copy for.selfCustody: falseMUST be disclosed on the card. A custodial wallet reached through a control labelled "Connect wallet" is a contradiction the user is entitled to see before, not after.- Where the wallet is itself a browser, the card MUST carry the return instruction of BRC-137 section 5.4.
3.3 Only order and inclusion are for sale
Every card MUST be rendered identically in every respect other than its own content.
- Same size, same layout, same fields in the same positions.
- No entry may carry a badge, highlight, border treatment, colour, animation, "recommended" flag, star rating, rank number or pre-selected state that another does not.
- A sponsor MUST NOT be able to purchase any of the above and an auction house MUST NOT offer them. The auction sells position and presence and nothing else.
- The chooser MUST NOT reorder, animate or re-render its list after first paint. See section 3.6.
This decides whether the chooser stays a chooser. The moment a slot can be made visually louder than its neighbours, the top slot stops being a suggestion and becomes an advertisement, the others become the control group, and rule 3.1.2 is satisfied in letter while its purpose is gone.
Rule 3 is not left to conscience. The offer schema of section 4.4 is closed, carries no card content at all, and contains no priority, weight, badge, colour, label or ordering hint, so there is no field in which purchased emphasis could travel and nothing for a house to sell. What remains a bare rule is a site styling its own cards unevenly, which no wire format can reach.
3.4 Slot composition
Where a chooser presents n slots and the auction is in use:
- At most
floor(n / 2)slots MAY be sponsored. - The remainder MUST be filled by the catalog's own policy, per BRC-137 section 3, with no reference to any bid.
- Sponsored and unsponsored slots MUST be interleaved rather than grouped, and the first slot MAY be either.
- No sponsor may occupy more than one slot in one chooser.
With the minimum of two slots this yields exactly one sponsored and one unsponsored entry: the person always sees at least one wallet that is there because a catalog thinks it is good, and at least one that is there because a vendor is willing to fund them. Both are worth having; neither is worth having alone.
3.5 Disclosure
- A sponsored entry MUST be disclosed as sponsored, in text, on the card, in the user's language.
- The disclosure MUST be one word or short phrase and MUST NOT be an icon alone.
- It MUST NOT state, imply or hint at an amount, a rank or a comparison. "Sponsored" conforms. "Top sponsor", "Featured", "Premium partner" and "Sponsored, pays the most" do not.
- It MUST expand into a short explanation, reachable by tap, by pointer and by keyboard focus. Hover alone does not conform: an affordance revealed only to a pointer is unreachable on the devices most of these people are holding.
- The expansion MUST say, in plain language: that placement here was paid for; that vendors compete for it by funding the wallets of the people who install them, rather than by paying this site more; that paying for a place in the list is not the site vouching for the wallet; and, where section 7.5 applies, that this site is paid when somebody who installs here is still using the wallet a month later. It SHOULD be at most five lines.
- The expansion MUST NOT state an amount or a range, MUST NOT say or imply that this entry funds more than another, MUST NOT promise the reader will be funded, and MUST NOT use the vocabulary of reward, bonus, prize or free money. It describes how the market works. It does not make an offer.
- The chooser MUST offer, in one interaction, a view of the unsponsored list, without requiring an account, a preference or a dismissal.
- The chooser MUST NOT require the user to accept or opt into sponsorship in order to install any entry.
Rules 4 to 6 draw the line the rest of the document depends on, and it is not where a first reading puts it. The existence of the arrangement is disclosable: it is common knowledge a week after any real deployment, a reader who suspects an ad and is told nothing will assume the worst and be right to, and somebody who understands that vendors compete by funding users has understood something true and favourable. The number is not disclosable, at any time, in any form, by any party, because a number is the only thing a farmer can sort by.
Rule 7 exists because the honest answer to "how do I know this is not just an ad" is to show the person the list without the ads.
3.6 Deadline, and the rule against reordering
- The chooser MUST render within the flow bound of BRC-110 section 1, and MUST NOT block first paint on the auction.
- The auction MUST be resolved, or abandoned, within 250 milliseconds of the bid request being issued. The budget covers the whole of it: the round trip, signature verification per section 4.5.4, and the local predicate evaluation of section 5.1. Anything that cannot complete inside it MUST be treated as a failure of that offer rather than a reason to extend the budget, which is why section 5.4.2 permits only cached revocation state and why no step in section 5 may make a network call.
- An offer arriving after the deadline MUST be discarded. It MUST NOT be held for the next chooser and MUST NOT be applied to a list that has painted.
- If nothing returns in time, the chooser MUST render the unsponsored list, without an error, a spinner or an empty slot.
- Once the list has painted, its contents and order MUST NOT change until the chooser closes.
- The deadline governs the auction and nothing else. It MUST NOT be applied to a wallet request, which may take as long as the user takes, per BRC-219.
Rule 5 is a safety requirement, not an aesthetic one. A list that reorders under a finger already in motion causes the person to install a wallet they did not choose, at exactly the moment they are least equipped to notice. The 250 millisecond deadline exists to make rule 5 satisfiable, and implementations MUST treat it as a ceiling rather than a target.
4. The Auction
4.1 Roles
| Role | Holds | Sees bounty amounts |
|---|---|---|
| Newcomer | Nothing yet | Never, before settlement |
| Relying site | The connect surface, the manifest of section 2.5, and the pending record of section 6.1 | No |
| Chooser | The offers, and the predicates of section 5 | No |
| Auction house | Campaigns, bids, and the settled record | Yes |
| Sponsor | Its own campaign and budget | Its own only |
The auction house is the only party with a complete view before settlement, and it is the seller's agent rather than the buyer's. Section 9 states what that implies and what it does not.
4.2 The bid request
The chooser, or a service acting for it, issues one bid request per chooser opening.
{
"protocol": "metanet-connect-bid",
"version": 1,
"auctionId": "<32 bytes, base64>",
"sponsoredSlots": 1,
"platform": "android",
"site": "example.com",
"locale": "nl-NL",
"height": 892140,
"predicatesSupported": ["country", "language", "platform", "over18", "holdsWallet"]
}
| Field | Requirement | Meaning |
|---|---|---|
auctionId |
MUST | A fresh 32-byte random value, identifying this auction everywhere it is referenced. |
sponsoredSlots |
MUST | Sponsored slots available, floor(n / 2) per section 3.4. Distinct from the manifest's chooserSlots, which is n. |
platform |
MUST | The detected platform, per BRC-137 section 2. |
site |
MUST | The origin hosting the connect surface. |
locale |
MAY | The client's language tag. |
height |
MUST | The chain height the chooser believes current, per BRC-100 getHeight. |
predicatesSupported |
MUST | The predicate names this chooser can evaluate, per section 5.2. |
- The request MUST NOT contain an identity key, a handle, a certificate, a device identifier, an advertising identifier, a cookie identifier, a fingerprint, a referrer, or any value derived from one. At the instant the chooser opens the person has no wallet, no identity key and no relationship with anybody here. There is nothing to send, and a specification leaving room for a device identifier would be inviting one to be manufactured.
- It MUST NOT contain the result of evaluating any predicate. Predicates are evaluated after offers arrive, per section 5.1.
siteis the origin and nothing more: no path, no query, no page title. It is present because a sponsor is entitled to know what kind of application its wallet is being suggested for, and because a house needs it to exclude sites that farm.- A house MUST NOT retain a request beyond what section 4.7 requires, and MUST NOT join requests across auctions by any field other than
site.
Attribution, and why it is required before anything is consumed.
- An auction house MUST attribute a request to the
siteit claims before consuming any commitment. For a browser client this is the user-agent-suppliedOriginheader, which a page cannot forge; for a server-side caller it is mutual authentication per BRC-104 against thecertifierkey that origin publishes under section 2.5. A request that cannot be attributed MUST be answered with no offers. - A house MUST rate-limit requests per attributed origin, and
paceBlocks(section 4.3.6) MUST bound how fast a campaign's commitment batch may be drawn as well as how fast its budgets may be spent.
Rules 5 and 6 exist because section 4.7.6 requires the whole bid set to be published, so a commitment index is consumed by entering an auction rather than by winning one. Without attribution, anyone could post forged requests naming a site they do not control and drain every eligible campaign's batch until section 4.5.5 takes those campaigns dark. The attacker spends nothing and the sponsors pay in consumed entries. An unauthenticated bid request was merely wasteful before the bid set existed; with it, it is a denial of service against every advertiser on a site at once.
4.3 The campaign object
A sponsor authors one campaign per wallet entry it wishes to place.
{
"protocol": "metanet-connect-campaign",
"version": 1,
"sponsor": "02c1a4...",
"wallet": {
"name": "Nexus",
"vendor": "Nexus Labs",
"icon": "https://nexus.example/icon.png",
"description": "Built for people who have never used Bitcoin",
"selfCustody": true,
"install": { "android": "https://play.google.com/store/apps/details?id=app.nexus" }
},
"predicate": { "all": [ { "platform": ["android", "ios"] } ] },
"campaignRef": "81c3d1d52d38fcb148c4aabef688f43ddc3039b701e18afdf5f9d540e7c6b1ba",
"bountySats": 21000,
"maxBidSats": 5000,
"siteShareSats": 7000,
"retentionBlocks": 4320,
"budget": {
"bountyTotalSats": 2100000000,
"placementTotalSats": 500000000,
"paceBlocks": 144
},
"claim": "https://nexus.example/.well-known/metanet-connect/claim",
"maturityBlocks": 144,
"startHeight": 892000,
"endHeight": 898000,
"signature": "<DER, hex>"
}
| Field | Requirement | Meaning |
|---|---|---|
sponsor |
MUST | Compressed identity key, 66 hex characters. MUST equal metanet.trust.publicKey published at the vendor's own domain per BRC-68. An anonymous sponsor cannot bid. |
wallet |
MUST | The card content of section 3.2, plus per-platform install targets. |
predicate |
MUST | The audience, per section 5. An empty predicate matches everyone and MUST be written explicitly. |
campaignRef |
MUST | A 32-byte value, unique to this campaign under this sponsor, from which the commitment batch and the voucher batch are both derived. Sections 4.5 and 7.1. |
bountySats |
MUST | What the newcomer receives. This is the bid, and the only quantity a slot is won with. |
maxBidSats |
MUST | The most the sponsor will pay the house for one placement. Billing only. |
siteShareSats |
MUST | What the sponsor will pay the site for one retained user, per section 7.5. Zero is valid and explicit. |
retentionBlocks |
MUST when siteShareSats is non-zero |
Blocks after the receipt at which retention is tested. Minimum 1008. |
budget |
MUST | Separate totals for bounties and for placement, plus pacing. The site share is drawn from the placement total. |
claim |
MUST | HTTPS endpoint accepting the claim of section 6.3. |
maturityBlocks |
MUST | Blocks between receipt and claimability, per section 6.6. Minimum 6. |
startHeight, endHeight |
MUST | The validity window, in block heights. |
signature |
MUST | BRC-3 signature by sponsor over the RFC 8785 canonical form with signature removed, protocol ID [2, "wallet choice"], key ID campaign, counterparty anyone. |
bountySatsis the bid. A house MUST rank eligible campaigns by it and MUST NOT rank bymaxBidSats, by any function of it, or by any other quantity. Ties break per section 4.6.maxBidSatsandsiteShareSatsare what the sponsor pays the house and the site, not the person. Neither MAY appear in an offer, influence which campaign wins, or be deducted frombountySats.siteShareSatsMUST NOT exceedbountySats. The person whose installation produced the arrangement is entitled to at least as much as the site that showed them the list, and a document letting an intermediary out-earn the beneficiary would have reproduced the thing it was written against.- A campaign MUST express its window in block heights rather than dates. A height is a fact every party already holds; a clock is something they must be persuaded to agree about.
budget.bountyTotalSatsMUST be at leastbountySats, andbudget.placementTotalSatsat leastmaxBidSats + siteShareSats. A house MUST NOT admit a campaign whose remaining bounty budget is below its bounty, and MUST stop returning offers the moment either budget is exhausted.paceBlocksbounds how fast either budget may be spent and how fast the commitment batch may be drawn, per section 4.2.6. Pacing is pro rata over the campaign window: within any sliding window ofpaceBlocksblocks a house MUST NOT reduce either budget by more thanceil(total * paceBlocks / (endHeight - startHeight)), nor draw more thanceil(N * paceBlocks / (endHeight - startHeight))commitments. Every quantity in that expression is already in the campaign, so pacing needs no field of its own and two houses serving the same campaign compute the same ceiling. Without it a campaign is drained in one block by whoever finds it first, which is the failure mode of every unpaced faucet ever deployed.- A campaign MAY be revised by publishing a new object with a later
startHeight. It MUST NOT be revised in place, because section 4.7 references it and an object that changes underneath a record makes that record unverifiable. - Eligibility, house side. A house MUST treat a campaign as ineligible for an auction, and MUST NOT draw a commitment for it, when any of the following holds. Section 6.5 gives the sponsor's equivalent list at claim time; this is the one the house runs at bid time, and the two are deliberately separate because they protect different parties.
- the request cannot be attributed to a declaring site, per section 4.2.5;
- the height is outside
[startHeight, endHeight]; - the commitment batch is exhausted, per rule 6;
- the remaining bounty budget is below
bountySats, or the remaining placement budget is belowmaxBidSats + siteShareSats, per rule 5; - the pacing ceiling of rule 6 is already reached for the current window;
- the observed unspent balance of the voucher batch is below
bountySats, per section 7.1.6; wallet.installhas no target for the requested platform;- the campaign has an open refusal, per section 7.4.2; or
- the house's own published floor exceeds the campaign's
maxBidSats. A campaign that cannot meet the floor is ineligible and MUST NOT be entered, rather than entered and charged its cap; - the wallet the campaign names is not listed in the catalog serving that site's unsponsored slots, or the catalog entry disagrees with the campaign's
walletobject on vendor, name or platform support; or - the
sponsorkey is not attested at the vendor's own domain per BRC-68, or the house has stopped serving that sponsor under section 8.3.5.
Conditions 10 and 11 are the ones that keep this an auction rather than a door. Money buys position among wallets somebody already vetted. It never buys entry. A sponsor that cannot get its wallet catalogued cannot bid at any price, and the bar for the sponsored half of a chooser is exactly the bar for the unsponsored half. The Security Considerations set out what happens when that principle is relaxed, and why the auction's own ranking rule makes relaxing it worse than it looks.
Keeping the amounts separate gives the house and the site revenue models that do not come out of the newcomer's pocket, which is what makes anyone willing to run one or carry one. Ranking on the bounty alone is what stops the separation collapsing back into ordinary ad tech: an auction ranked on maxBidSats lets every sponsor set bountySats to zero and still win, and within one funding round it would.
4.4 The offer
The auction house returns at most sponsoredSlots offers, one per winning campaign.
{
"protocol": "metanet-connect-offer",
"version": 1,
"auctionId": "<32 bytes, base64>",
"slot": 0,
"house": "03fe12...",
"sponsor": "02c1a4...",
"vendorKey": "02c1a4...",
"walletName": "Nexus",
"predicate": { "...": "the campaign predicate, verbatim" },
"bountyCommitment": "e13a43f9a5c5d90a63947e66a8bd4a8159de40580dde2bb0e65b64e22429060d",
"commitmentIndex": 0,
"claim": "https://nexus.example/.well-known/metanet-connect/claim",
"maturityBlocks": 144,
"expiresHeight": 892150,
"signature": "<DER, hex>"
}
- The offer MUST NOT contain
bountySats,maxBidSats,siteShareSats,budget, a bid rank, a price, a clearing value, a count of competing bids, or any field from which any of those amounts could be recovered or bounded.maxBidSatsis excluded alongside the bounty because a sponsor's placement price correlates with its bounty;siteShareSatsis excluded because section 4.3 rule 3 caps it at the bounty, so publishing it would bound the bounty from below. A leaked correlate is a leak. - The offer names a wallet; it does not describe one.
vendorKeyis the vendor's identity key andwalletNameits published name, and together they select one entry in the catalog the chooser is already using. Every field the card shows is read from that entry, per section 3.2.1. The offer MUST NOT carry an icon, a name, a description, an install target, aselfCustodyflag, or any other card content, and a chooser MUST ignore such a field if one appears. bountyCommitmentandcommitmentIndexare drawn from the batch of section 4.5.predicateMUST be carried verbatim from the campaign, so that the chooser can evaluate it locally per section 5.1.signatureis a BRC-3 signature byhouseover the RFC 8785 canonical form withsignatureremoved, protocol ID[2, "wallet choice"], key IDoffer, counterpartyanyone. A chooser MUST verify it and MUST discard an offer that fails, without substituting another.- A chooser MUST discard an offer whose
auctionIddoes not match the request it issued, whoseexpiresHeightis below the height it sent, or whose named wallet the catalog does not offer for the detected platform. expiresHeightMUST NOT exceed the request'sheightplus 144, and a chooser MUST discard an offer that exceeds it. An offer is a bid in one auction resolved inside 250 milliseconds, so it has no use beyond the chooser that asked for it; an unbounded expiry turns a bid into a standing quote the sponsor never agreed to make.- An auction house MUST return at most
sponsoredSlotsoffers, and MUST NOT return an overflow, a reserve list, or a ranked set of candidates. An offer the chooser discards leaves its slot to the catalog, per section 5.1.1. This costs the house inventory and is required anyway: a list longer than the slots available is a list the client must choose from, and any order the house could put it in is an ordering by bounty, which is the one signal this document exists to withhold. - The offer schema is closed. The fields above are the whole of it. An auction house MUST NOT add a field, and a chooser MUST ignore any field it does not recognise and MUST NOT render one, per the versioning rules of the Specification preamble. New fields arrive by revision of this document and by no other route.
Rule 1 is where the anti-farming property actually lives, and it is a rule about what is transmitted rather than what is rendered. A chooser cannot be trusted to hide a number it holds: the person can open the network tab. It can be trusted with a hash, because there is nothing in a hash to sort by. Everything section 8 says about farming rests on this rule and on rule 8.
Rule 9 is what makes section 3.3 more than an appeal to good conduct. Read the schema again and notice what is absent: there is no priority, no weight, no badge, no colour, no label, no ordering hint, and no free-text slot a house could repurpose as one. A sponsor cannot buy emphasis because the format gives the house nothing to sell. That does not stop a site from styling its own cards badly, which remains a rule rather than a mechanism, but it does close the route by which emphasis would have become a purchasable product, which is the route that matters.
4.5 The commitment batch
A sponsor is not online for any individual auction: auctions resolve inside 250 milliseconds, thousands of times over, from a campaign authored once. So commitments are pre-computed in a batch, derived from the sponsor's own key, and consumed one per auction by the house.
salt_i = SHA-256( "wallet choice salt" || sponsorPrivKey (32 bytes) || campaignRef (32 bytes) || uint32LE(i) )
commitment_i = SHA-256( uint64BE(bountySats) || salt_i )
The tag is the 18 ASCII bytes wallet choice salt, with no separator, because every field after it is fixed length.
- The derivation follows BRC-227 section 2's construction, under a distinct tag, for the same reason: it consumes the sponsor's private key, so nobody else can reproduce a salt, and the sponsor regenerates every opening from
(sponsorPrivKey, campaignRef)alone with no stored state and no backup. - The tag is load-bearing and MUST NOT be omitted. Section 7.1 derives a voucher private key from the same key, the same reference and the same index, and section 4.5.6 publishes the salt. Without the tag the two values are byte-identical, so publishing an opening would publish the private key controlling that bounty, and anyone reading a settled record could spend a voucher whose newcomer had not yet claimed it. The tag is the whole of what keeps a published opening from being a published key.
- The sponsor computes
commitment_iforiin[0, N)and delivers the ordered list with the campaign. It MUST NOT deliver any salt. - The house MUST consume commitments in ascending
i, use each at most once, and carry the value and its index into the offer unmodified. Reusing an index makes both offers recognisable as the same campaign to anybody who later learns one opening. - A commitment is consumed by entering an auction, not by winning one, because section 4.7.6 publishes the whole bid set. A campaign that bids often and wins rarely depletes its batch at the rate it bids, so
NMUST be sized against bids. Deriving an entry costs one SHA-256 and stores nothing, so a largeNis cheap. - The house MUST stop returning offers when a batch is exhausted, and MUST NOT compute a commitment itself. A house computing commitments could open them to any value it chose, which is the one thing this construction prevents.
- The opening
(bountySats, salt_i)MUST be published for a winning campaign, no earlier than the delay of section 8.4, and MUST NOT be published for a losing one. campaignRefMUST NOT be reused across campaigns under one sponsor. Reuse repeats the salt sequence, so two campaigns with the same bounty produce identical commitments and are trivially linkable.
campaignRef also derives the voucher batch of section 7.1, so commitment i and voucher i are the same slot. One reference, one key, two batches that stay in step without a database, and two tags so that opening one never opens the other.
Committing to a value with a hash and opening it later is the ordinary construction, used here for the ordinary reason. What is unusual is which party is kept in the dark, since the commitment also hides the winning bid from its beneficiary for as long as they could act on it. It does three jobs at once. Against the newcomer it hides a number they could otherwise sort by. Against the sponsor it prevents a bid being quietly reduced after it has won. Against the house it prevents a bid being reported as something other than what was made.
4.6 First price on the bounty, and why the house's fee is different
The winning campaign is the eligible campaign with the highest bountySats, and the winner delivers exactly that. Ties break by the lowest auctionId-keyed hash of the sponsor key, which is deterministic, unpredictable in advance and cheap to verify.
Display advertising settled on second-price auctions for a good reason: when the winner pays a price to the seller, charging the runner-up's bid removes the incentive to shade, and the seller loses little. That reasoning is sound and it depends entirely on the money going to the party running the auction.
The bounty is not that. It is a transfer to a third party who is not a participant and has nothing to withhold. Charging the winner the second-highest bid would mean the person receives less than the sponsor had already agreed to part with, and the difference would return to the sponsor. There is no efficiency argument for that, only a transfer away from the person this mechanism exists to serve. This is also the precise fault of the Android choice screen auction described in the Prior Art: the slots went to whoever paid the intermediary most, so a rival's willingness to spend on the intermediary, rather than anything about the user's interest, decided what the user saw.
- A house MUST NOT run a second-price, reserve-price or soft-floor variant on
bountySats, and MUST NOT retain, rebate, discount or fail to deliver any part of a winning bounty. - A house MAY apply second-price or any other pricing it publishes to its own charge against
maxBidSats, and SHOULD, because there Vickrey's argument applies exactly as it does anywhere else. - The two pricings are independent. The winner is decided by the highest bounty; what the winner is then billed is a matter between the sponsor and the house, bounded by
maxBidSatsand stated in the billing statement of section 4.8.
The document runs two auctions at once: a first-price auction for the person's benefit, and downstream of it an ordinary priced service for the house's.
4.7 The settled record, and the bid set
A house MUST publish a settled record for every auction in which an offer was returned. It is public and contains only what everybody may see.
{
"protocol": "metanet-connect-settlement",
"version": 1,
"auctionId": "<32 bytes, base64>",
"height": 892140,
"site": "example.com",
"platform": "android",
"bidSet": [
"14c8c9a850de3b9bf16e7c9f7d0ec6d22ce7da673acffbaec471f77957d70f82",
"e13a43f9a5c5d90a63947e66a8bd4a8159de40580dde2bb0e65b64e22429060d"
],
"slots": [
{
"slot": 0,
"sponsor": "02c1a4...",
"commitmentIndex": 0,
"bountyCommitment": "e13a43...",
"bountySats": 21000,
"salt": "3c422c2f054d9e9a2ac262dd3c4a07a73be2ad0ba6da766a4c8f2561f5dd5fde",
"outcome": "claimed",
"settlementTxid": "..."
}
],
"signature": "<DER, hex>"
}
outcomeMUST be one ofoffered,installed,claimed,retained,expiredordefaulted.retainedsupersedesclaimedonce the share of section 7.5 is paid; a record MAY be amended upward through that sequence and never downward.- The record MUST NOT be published before the delay of section 8.4, except for the anchor of rule 9.
- It MUST NOT name the newcomer's identity key. The sponsor already knows it; nobody else needs it, and a public record joining identity keys to sites is a behavioural log.
- It MUST NOT contain what the house charged, what the site was paid, or any campaign budget. Those belong in section 4.8.
signatureis a BRC-3 signature by the house over the RFC 8785 canonical form withsignatureremoved, protocol ID[2, "wallet choice"], key IDsettlement, counterpartyanyone.
The bid set.
bidSetMUST contain the commitment drawn for every eligible campaign, winners and losers alike, in lexicographic order so the order carries no ranking. The house MUST draw and consume one index from each eligible campaign's batch whether or not it wins.- An opening MUST be published only for winners. A loser's opening MUST NOT appear in the record, in an aggregate, or anywhere else.
bidSetMUST NOT name a sponsor, a campaign or a wallet. A losing sponsor recognises its own entry by recomputingcommitment_ifrom its own key, and nobody else can.
The anchor.
- At auction time, before any opening exists, the house SHOULD anchor
SHA-256over the canonical form of(auctionId, site, platform, bidSet)as a BRC-48 PushDrop output, submitted to the overlay topictm_walletonboardingand discoverable throughls_walletonboarding. As BRC-87 establishes no registry, these names are claimed rather than reserved. Anchors MAY be batched. - Where an anchor exists the published record MUST match it. A record whose
bidSetdoes not reproduce its anchor was rewritten; a record published with openings before its anchor matured was published early. Both become observable rather than merely forbidden.
The bid set answers the sharpest conflict in this design: the house is paid on maxBidSats, required to rank on bountySats, and is the only party that sees both. Publishing every commitment gives each sponsor two checks it can run alone. Is my commitment in the set? If not, the house dropped my bid. Does the winner's published opening exceed my own bounty? If not, the house ranked on something other than the bounty. A house quietly ranking on its own fee is caught by the first sponsor that loses while bidding more, which is the sponsor most motivated to look.
Losers' openings stay closed on purpose. Publishing them would let anybody reconstruct the distribution of bids, and that is a market report, which is precisely the forward-looking intelligence section 8.4 withholds from a farmer. Every participant gets the check that concerns them; nobody gets the table. Commitments themselves leak nothing, which is why the set can be anchored immediately while the openings wait.
4.8 The billing statement, and the house's fee
Separately from the public record, a house MUST make available to a sponsor authenticated as its own sponsor key a statement of what it was charged.
{
"protocol": "metanet-connect-billing",
"version": 1,
"sponsor": "02c1a4...",
"campaignRef": "81c3d1...",
"fromHeight": 892000,
"toHeight": 892999,
"lines": [
{ "auctionId": "<32 bytes, base64>", "commitmentIndex": 0, "placementChargedSats": 4200 }
],
"signature": "<DER, hex>"
}
- The statement MUST let the sponsor reconcile delivered bounties against
budget.bountyTotalSatsandplacementChargedSatsagainstbudget.placementTotalSats. placementChargedSatsMUST NOT exceed the winning campaign'smaxBidSatsfor that auction.- A house MUST publish its fee model, and every charge MUST appear in a line the sponsor can tie to an
auctionId. - A fee MUST NOT be deducted from
bountySats, reduce it, or be conditional on reducing it. - A per-claim fee is a conflict of interest and MUST be disclosed as one, in the same document that publishes the model. A house paid per settled claim earns more when farming succeeds.
signatureis a BRC-3 signature by the house, protocol ID[2, "wallet choice"], key IDbilling, counterparty the sponsor addressed. Unlike every other signature here it is not publicly verifiable, and MUST NOT be, because a statement anyone can verify is one anyone can forward and be believed about.- A statement is addressed to one sponsor and MUST NOT be published.
The public record exists so a bid cannot be misreported; the statement exists so a sponsor cannot be overcharged. Merging them would either publish a sponsor's costs or conceal an opened commitment.
4.9 When the auction fails
A house that returns nothing, returns late, returns something unparseable, or cannot be reached is not an error to be surfaced. The chooser renders the unsponsored list and the user sees a working dialogue. An application MUST NOT tell a person that wallet suggestions are unavailable, because from where they are standing the suggestions are right there.
5. Targeting
5.1 Predicates are evaluated by the client
A campaign declares the audience it wants. The house returns the predicate with the offer. The chooser evaluates it locally, against attributes held on the device, and discards offers that do not match before rendering.
- A chooser MUST evaluate every offer's predicate before rendering it and MUST discard non-matching offers. A discarded slot MUST fall to the catalog's policy of section 3.4.2 and MUST NOT fall to another offer, because there is no other offer: section 4.4.8 forbids the house from sending one. A sponsored count lower than
sponsoredSlotsis not an error and MUST NOT be surfaced as one. - A chooser MUST NOT transmit the result of an evaluation to the house, the sponsor or the site.
- A chooser MUST NOT evaluate a predicate naming a value it did not list in
predicatesSupported. - A house MUST NOT require, request or accept predicate results, and MUST NOT return an offer conditional on receiving one.
This inverts the arrangement real-time bidding normalised. There the exchange holds a profile, the bidder buys against it, and the person is described to strangers in order to be shown something. Here the description travels outward and the evaluation happens where the facts already are. The sponsor learns whether its campaign was chosen from its own install numbers. It never learns who was excluded, or why.
Rule 1 costs the house real inventory, and the alternative should not be reintroduced as an optimisation. A ranked overflow list, so the chooser could fall through, would be an ordering by bounty arriving on the device of every person the chooser opens for. Losing a slot to the catalog is the cheaper failure, and it fails toward a wallet somebody vouched for.
5.2 The predicate vocabulary
A predicate is a JSON object combining terms with all, any and not.
{ "all": [ { "country": ["NLD", "BEL"] }, { "not": { "holdsWallet": true } } ] }
The vocabulary is closed. A chooser MUST reject a predicate containing a term it does not recognise and MUST discard the offer carrying it.
| Term | Type | Source |
|---|---|---|
platform |
list of platform strings | Detected, per BRC-137 section 2 |
language |
list of ISO 639-1 codes | Client locale |
country |
list of ISO 3166-1 alpha-3 codes | Attested attribute, per section 5.3 or 5.4 |
over18 |
boolean | Attested attribute |
holdsWallet |
boolean | Whether a wallet is reachable, per the two entry points of section 3.1 |
- A predicate MUST NOT name an identity key, a handle, an email address, an account, a device, a postal address, a coordinate, an age other than through
over18, or any attribute not above. - The empty predicate is
{"all": []}, and it is the form section 4.3 requires a campaign targeting everyone to write explicitly. Anallwith no terms is satisfied by everybody, ananywith no terms is satisfied by nobody, and those are the only zero-term forms a conforming validator accepts. Stating this is not pedantry: a validator that rejects an emptyallwhile an evaluator treats it as true will reject the one campaign shape the specification obliges a sponsor to be able to write. - A chooser MUST discard an offer whose predicate names more than four terms, or uses
notmore than twice, regardless of what the house admitted. - New terms arrive by revision of this document, not by a house, a catalog or a sponsor. A vocabulary any party may extend is not a vocabulary.
holdsWallet: trueis the portability case: somebody who already has one wallet acquiring a second. Such a campaign competes on migration rather than onboarding, and section 6.5 treats it differently.
The vocabulary is deliberately smaller than any sponsor will want. Every term added is a bit of information about the person and the terms combine multiplicatively. Five coarse terms describe an audience; fifteen fine ones describe a person.
5.3 BAP attestations
Where a client holds Bitcoin Attestation Protocol identity attributes it MAY satisfy country and over18 from them.
- A BAP attribute is a URN
urn:bap:id:<attribute>:<value>:<nonce>held by the subject; the chain carries only its SHA-256 hash, inside anATTESTrecord signed by an attestor. - To satisfy a term the client MUST hold the full URN, compute
urn:bap:attest:<SHA-256 of the id URN>:<identity key>, locate anATTESTrecord for the hash of that attestation URN, and verify its signing address resolves to an attestor the user trusts through the attestor'sIDrecord chain. - Attribute names MUST be the schema.org names BAP uses.
over18has value1. - The client MUST NOT reveal the URN, the nonce, the attestation, the attestor, or that a term was satisfied.
- A client with no BAP identity, which is every first-time visitor, evaluates
countryandover18as unsatisfied. A campaign targeting them will not be shown, which is correct, and MUST NOT be worked around by inferring the attribute from an address, a locale or a network route.
BAP is used rather than certificates alone for one property: the attestation is a hash on chain and its opening is held by the subject, so checking a term is local, needs no round trip to a certifier, and emits no signal that a check occurred. That is what a predicate on a 250 millisecond budget requires.
5.4 BRC-52 certificates
A client holding BRC-52 certificates MAY satisfy the same terms from a certificate field.
- It decrypts the field from its own master keyring and compares locally. It MUST NOT call
proveCertificate, construct a verifier keyring, or reveal a field. A predicate is not a proof request, and a chooser that turns one into a proof request has built the thing section 5.1 prevents. - It MUST consult the revocation outpoint from cached state only: evaluation happens inside the budget of section 3.6.2 and MUST NOT make a network call. With no fresh revocation state the term MUST evaluate as unsatisfied. A wallet suggestion is not worth a round trip, and a term failing closed costs a sponsor one impression.
- Where both a BAP attestation and a certificate could satisfy a term, either suffices, and the client MUST NOT report which.
5.5 Minimum audience
- A house MUST NOT admit a predicate whose terms, in combination, could match fewer people than a minimum it publishes.
- A chooser MUST discard an offer whose predicate names more than four terms or uses
notmore than twice, regardless. - A house MUST NOT accept a campaign naming an audience by enumeration, list upload, identifier match, or anything functionally equivalent.
Targeting narrow enough to identify a person is not targeting, and a predicate is a small enough object that this is easy to get wrong by accident. The bounds in rule 2 are claimed rather than derived, on the reasoning that a number every client shares is worth more than a number each client picks.
5.6 What the sponsor learns
| The sponsor learns | From |
|---|---|
| That its campaign won a slot | The settled record, after the delay of section 8.4 |
| The site and platform of that auction | The settled record |
| That a wallet was installed and connected | Its own claim endpoint |
| The newcomer's identity key, at claim | The connect receipt of section 6.2 |
| One bit, about a month later, on whether that person still uses the wallet | The retention proof of section 7.5, if the wallet sends one |
| Nothing about anyone who did not claim | By construction |
The sponsor learns the identity key of somebody who installed its wallet, which it would learn anyway, since it published the wallet. It does not learn who saw the offer, who was excluded by the predicate, who chose a competitor, or that any particular person was in any particular auction.
Because section 6.3.3 permits claiming without a prompt, the fourth row is the common case rather than the exceptional one: substantially everyone who installs will be claimed for, and their key disclosed, without having been asked. That is the trade this document makes for a mechanism that works, and section 6.4 is the whole of what is offered in return.
6. Attribution and Claim
6.1 Attribution stays with the site
An install crosses an application store and nothing survives that crossing reliably: deep links break, referrers are stripped, clipboards are unreliable, and every attribution scheme built on carrying a token through an install has an error rate people build businesses on. This document carries no token through the install.
- Before the newcomer leaves, the site MUST store a pending record of
(auctionId, chosen wallet, height, task reference), keyed to the browsing session by whatever means it already uses. The task reference is the one of section 2.7, and it is stored here because this is the crossing it has to survive.auctionIdis already a fresh 32-byte random value identifying the auction everywhere else, so it serves as the correlator and no second nonce is defined. - The handoff carries nothing.
installUrlis the vendor's ordinary install location and MUST NOT be decorated with an attribution parameter. - On return, in the wallet's browser or otherwise, the site recovers the pending record, detects the now-reachable wallet, and reads its identity key through the verified handshake of BRC-137 section 6.
- The site MUST confirm the returning wallet is the one chosen, comparing the vendor reported by BRC-100
getVersionagainst the entry. A mismatch MUST NOT issue a receipt. - A pending record MUST expire. It measures the gap between choosing and returning, which is minutes or hours, so a site MUST NOT honour one older than 144 blocks and SHOULD expire it far sooner. This is deliberately not
maturityBlocks, which runs forward from the receipt and says nothing about how long somebody may be away installing.
Attribution therefore never leaves the site's control and the install target stays a plain store link. A vendor MAY additionally accept a deep link carrying auctionId where that works, but a conforming implementation MUST NOT depend on it.
6.2 The connect receipt
The site issues a receipt as a BRC-52 certificate.
| Field | Value |
|---|---|
type |
ed3Kq2moHEFDShm3hzFmq0UmD8x2j+jh896zXBToz3o= |
subject |
The newcomer's identity key |
certifier |
The site's identity key, per section 2.5 |
fields.auctionId |
The auction identifier |
fields.sponsor |
The winning sponsor's identity key |
fields.walletVendor |
The vendor as reported by getVersion |
fields.origin |
The site origin |
fields.height |
The height at which the wallet first connected |
revocationOutpoint |
The site's revocation policy, or the disabled sentinel |
- The receipt MUST be signed and its fields encrypted per BRC-52. The sponsor receives a verifier keyring for every field, since every field is addressed to it.
- The site MUST issue at most one receipt per
auctionIdand MUST refuse a second for a different subject. - The site MUST NOT issue a receipt for an identity key already connected to it before the auction. A returning user acquiring a second wallet is handled by section 6.5, not by attributing an existing relationship to a new install.
- The receipt MUST be delivered to the newcomer's wallet, not the sponsor. Section 6.3.3 lets the wallet claim without asking, so this is not about who decides; it is about who holds the credential. A receipt routed to the sponsor would let it pay a key that never asked, and would put the person's only record of the transaction in the hands of the party it is evidence against.
certifierMUST equalmetanet.connect.certifierin the manifest atfields.origin. That equality binds a receipt to a domain; without it any key could sign a receipt claiming any origin.- The type identifier is
base64(SHA-256("wallet choice receipt v1")), derived in the manner of BRC-169 section 4.5 and for its stated reason: BRC-52 leaves the type opaque and no document defines an authority over it.
The site's signature is the scarce resource here. Identity keys are free and a farmer mints them all day; a site willing to sign a receipt is an accountable party with a domain, a reputation and a place in a settled record. Section 8.3 is built on that asymmetry.
6.3 The claim
The wallet presents the receipt to the sponsor's claim endpoint over BRC-104.
POST /.well-known/metanet-connect/claim
Content-Type: application/json
{
"protocol": "metanet-connect-claim",
"version": 1,
"auctionId": "<32 bytes, base64>",
"receipt": { "...": "the BRC-52 verifiable certificate, with a verifier keyring for the sponsor" },
"payTo": "02de91...",
"signature": "<DER, hex>"
}
payToMUST be the newcomer's identity key and MUST equal the receipt'ssubject.signatureis a BRC-3 signature by the newcomer over the RFC 8785 canonical form withsignatureremoved, protocol ID[2, "wallet choice"], key IDclaim <auctionId>using the Base64auctionIdafter one space, counterpartyanyone. The key ID is prefixed because the same key signs a retention proof for the same auction under the same protocol, and two object types under one derived key is an avoidable hazard.- A wallet MAY claim automatically, without a prompt. It MUST NOT interrupt a first run with a permission dialogue for this purpose. A prompt shown to somebody in the first minute of their first wallet, asking them to authorise something they have no vocabulary for, is answered by dismissal, and the mechanism would deliver nothing to almost everyone it exists for.
- A claim MUST be rejected before
maturityBlockshave passed sincefields.height, and the rejection MUST state the height at which it becomes claimable rather than failing opaquely. - Where the wallet claims without a prompt, section 6.4 applies.
6.4 The funding note
A silent claim is not a silent event. A wallet that claims without asking MUST make what happened legible afterwards, in the ordinary place a person looks for what happened to their money.
- The wallet MUST internalise the funding as a labelled action per BRC-65, carrying at minimum the label
wallet onboarding, and MUST NOT hide it fromlistActions. - The
descriptionMUST name the sponsor, the site, and the fact that placement was sponsored, as a plain sentence in the user's language, never a protocol identifier or a hash. Conforming:Nexus Labs funded your wallet after you connected at example.com. They paid to be suggested there. - It MUST NOT congratulate, reward, gamify or thank. It is a record of what happened, not a message from the sponsor, and a wallet MUST NOT let a sponsor supply the copy.
- The wallet MUST disclose, in the same place, that the sponsor now knows the identity key it funded. The user paid that cost before being asked, so they are entitled to learn it immediately rather than infer it later.
- The wallet MUST offer a setting disabling automatic claiming and automatic retention proofs together, reachable from the note itself, honoured for every subsequent campaign. Disabling MUST NOT reverse a claim already made, and the wallet MUST say so rather than implying the money can be sent back.
- Where the wallet will later produce a retention proof, the note MUST say so at the same time: that in about a month it will confirm, as a single yes or no with no detail attached, that the person still uses the wallet, and that this is how the site that showed them the list gets paid.
- The wallet MUST NOT present the amount comparatively, state or imply what a different wallet would have paid, or rank sponsors.
Rule 4 makes the trade acceptable. Silent claiming spends a disclosure the person did not authorise, in exchange for the mechanism working at all. The honest response is not to pretend otherwise but to tell them, in the first place they will look, exactly what was disclosed and to whom, and to give them the switch.
6.5 Eligibility
A sponsor MUST reject a claim unless all of the following hold. These are the sponsor's checks on the sponsor's money, specified here so a newcomer can be told in advance why a claim might fail.
- The receipt verifies; its
certifierresolves tofields.originthrough that origin's manifest; that origin is one the campaign was served to; and itsauctionIdmatches a settled record naming this sponsor. - The subject key has never been funded under any campaign by this sponsor.
- The subject key was not, at
fields.height, already connected to the certifying site. - The claim is the first for that
auctionId. maturityBlockshave elapsed.
Rule 2 needs care where the campaign targeted holdsWallet: true. Somebody moving between wallets keeps their identity key, which is what portability means, and reaching exactly that person is what the campaign was for; read literally, rule 2 disqualifies them the moment any sponsor has funded that key. For such a campaign rule 2 becomes: never funded by this sponsor for this wallet, identified by wallet.name and wallet.vendor. One person may therefore be funded once by vendor A to adopt wallet A and once by vendor B to adopt wallet B, which is two vendors each paying once to win a customer. What stays forbidden is one vendor funding one key twice for one wallet. A sponsor applying this reading MUST state it in its campaign terms, because "one bounty per identity key" is what a reader takes from rule 2.
6.6 Maturity
maturityBlocks is the interval between the receipt's height and the moment a claim is payable. Minimum 6; default SHOULD be 144.
- Maturity MUST be counted in blocks, never wall-clock time. A height is a fact both parties hold; a timestamp is a claim one of them makes.
- A sponsor MAY additionally require that the subject key has appeared in a confirmed transaction. It MUST state that in the campaign and MUST NOT add it afterwards.
- Maturity is not a probation period and MUST NOT be described as one. It is the interval over which farming becomes uneconomic and the sponsor's own checks have somewhere to run.
7. Settlement
7.1 The bounty is locked before it is bid
There is one settlement mode. A bounty MUST be pre-funded, using the voucher construction of BRC-227, before its campaign is admitted to an auction.
- The sponsor creates voucher outputs in advance, each holding
bountySatsplus the fee to spend it, with voucher keys derived from its own private key per BRC-227 section 2:
voucherKey_i = SHA-256( sponsorPrivKey (32 bytes) || campaignRef (32 bytes) || uint32LE(i) )
campaignRefandiare the same values that producesalt_iin section 4.5, so commitmentiand voucheriare the same slot. An offer carryingcommitmentIndex: iis backed by voucheri, and the sponsor needs no database to know which is which. The two derivations share an index and nothing else: section 4.5 tags its input and this one does not, sosalt_iandvoucherKey_iare independent values and publishing the first reveals nothing about the second. Sharing the index is the useful property. Sharing the output would hand the money to anybody who read the settled record.- On a valid claim the sponsor releases
voucherKey_iand the newcomer's wallet spends the voucher to itself. Payer and owner are decoupled as in BRC-227 section 4: the voucher funds the spend, and a key the newcomer already controls owns the result. - A voucher key is a bearer credential and MUST be released only over the authenticated channel of section 6.3, never in a link, a page, or a message the sponsor cannot bind to a verified claim.
- Unclaimed vouchers are ordinary UTXOs the sponsor MAY reclaim, found by the gap scan of BRC-227 section 5. A campaign that ends strands nothing.
- A house MUST NOT admit a campaign whose voucher batch it cannot observe on chain, and MUST stop returning offers when that batch's unspent balance falls below
bountySats.
An earlier draft offered a second mode in which a sponsor merely promised to pay, with a remedy made of publishable complaints and catalog demotions. It required this document to specify a reputation mechanism it could not enforce, in service of a promise a reader could not check. Pre-funding removes it: a sponsor bidding aggressively has visibly locked the funds, a house can check that before admitting the campaign, and the worst a sponsor can now do is refuse to release a key to money it has already immobilised.
7.2 What is delivered
- Delivered value MUST be ordinary spendable satoshis, exactly
bountySats, the amount the commitment opens to. Not less, not a different asset. - It MUST NOT be locked, vested, restricted to fees, restricted to the sponsor's own application, or reclaimable once claimed. A bounty the recipient cannot spend is not the recipient's, and a document arguing that the account belongs to the user cannot deliver its money in a form that does not.
- The claiming transaction MUST name the sponsor. Any accompanying payment message MUST follow BRC-29, derived under BRC-42, with the sponsor's real identity key in the remittance. The unlinkable profile of BRC-228 MUST NOT be used: the person is entitled to see who funded their wallet, and a bounty from an unattributable key is indistinguishable from dust.
- The sponsor SHOULD release within one block of a valid claim and MUST within 144.
7.3 Delivery to a wallet that may be offline
- Where the wallet publishes a messagebox per BRC-169 section 7, the sponsor SHOULD deliver the release there.
- Where it does not, the wallet MUST be able to discover the resulting output by the pull-based procedure of BRC-155, so the sponsor MUST direct the spend to a key the wallet is watching rather than one it has no reason to scan.
- Either way the wallet internalises per BRC-100
internalizeAction. A bounty is a payment and gets no special path.
7.4 Refusal to release
A sponsor that receives a valid claim and does not release within the window of section 7.2.4 is refusing, and the refusal is self-limiting in a way a broken promise was not: the funds are immobilised in a voucher the sponsor cannot spend without giving up the campaign's remaining credibility, and cannot use for anything else meanwhile.
- Any party holding the receipt and the settled record MAY publish a refusal record: the opened commitment, the settled record, and the unspent voucher output.
- A house MUST stop returning offers for a campaign with an open refusal and MUST publish that it has.
- A refusal record MUST NOT name the newcomer's identity key.
- Nothing compels a release. What the construction does is make a refusal cheap to prove, expensive to sustain, and visible against funds the refusing party has already given up the use of.
7.5 The site's share, paid for retention rather than for installs
A site provides the placement and will reasonably want paying. The obvious mechanism, payment per install or per receipt, is the one thing this document cannot do: a site's signature on a receipt is the scarce accountable resource all of section 8.3 rests on, and paying per signature attacks it directly. The share is therefore paid on retention, by the sponsor, after a condition the site cannot manufacture alone.
- The share is paid from
budget.placementTotalSats. It MUST NOT be drawn frombountySats, reduce it, or delay it. The bounty settles at maturity, independently and much earlier. - It becomes payable once, to the receipt's
certifier, when all of the following hold atfields.height + retentionBlocks:- the bounty for that
auctionIdwas claimed and delivered; - the subject key is still in use; and
- the subject key has been party to at least one confirmed transaction with a counterparty that is neither the certifying site nor the sponsor.
- the bounty for that
- Condition 2.3 is what a farming site cannot produce alone. A site that mints keys, signs its own receipts and transacts with them satisfies nothing, because its own transactions are excluded by name. Defeating it needs a second independent transacting party, which is either a real user or a second colluding operator, and the second case has doubled the attacker's cost and surface.
retentionBlocksMUST be at least 1008 and SHOULD be substantially longer thanmaturityBlocks. Maturity asks whether a claim is real; retention asks whether a person stayed.- Retention is not a safety signal and MUST NOT be presented as one. A satisfied retention condition says a funded key was still transacting a month later. It says nothing about whether the wallet holding it is honest, and a patient attacker's victims satisfy it perfectly: they install, they transact, they are robbed afterwards. No party may describe a
retainedoutcome, a retention proof, or a paid share as evidence that a wallet is safe, and a catalog MUST NOT use retention as an admission or ranking criterion.
The retention proof. Nobody may surveil the newcomer to establish condition 2.3.
- The condition is asserted by the newcomer's own key, in a signed object naming the auction, the height, and a single boolean:
{
"protocol": "metanet-connect-retention",
"version": 1,
"auctionId": "<32 bytes, base64>",
"subject": "02de91...",
"retentionHeight": 896460,
"transactedWithThirdParty": true,
"signature": "<DER, hex>"
}
signatureis a BRC-3 signature by the subject over the RFC 8785 canonical form withsignatureremoved, protocol ID[2, "wallet choice"], key IDretention <auctionId>, counterpartyanyone. The prefix distinguishes it from the claim signature of section 6.3.2, which the same key makes for the same auction.- The proof MUST NOT name the counterparty, the transaction, the amount, the count or the date. One bit leaves the device and nothing else. A sponsor MUST NOT require more or condition payment on being shown a transaction.
- A sponsor MUST NOT infer condition 2.3 by observing the chain, querying an indexer for the subject key, or any means other than the proof. Under BRC-42 derivation it could not do so reliably anyway, and a document inviting it to try would be inviting the surveillance section 5 refuses.
- The wallet produces the proof, so the attestation comes from the sponsor's own software about a fact the sponsor pays for. The only party positioned to inflate the number is the one who loses money by inflating it. The newcomer has no stake in the answer, and the site, which does, cannot produce the signature.
- A wallet is NOT obliged to produce a proof. Where none is produced the site is not paid and the newcomer is unaffected, and a sponsor MUST NOT withhold, claw back or reduce a bounty because one did not arrive.
- A wallet producing retention proofs MUST disclose the practice in the funding note, and the switch of section 6.4.5 MUST disable both.
Bounds.
- One share per
auctionId, and one per (site, subject key) per sponsor. - No share is payable to a site the house has ceased serving under section 8.3.3, or one whose receipts the sponsor has found invalid.
- Payment is a BRC-29 payment directly from the sponsor to the site's identity key. It MUST NOT be routed through the auction house, and a house MUST NOT retain, deduct from, or take a percentage of it. Unlike the bounty it need not be pre-funded, because a site is a commercial counterparty that can invoice, chase and stop carrying the auction, and none of those remedies are available to the newcomer.
- Rule 3 forbids routing because the alternative is an undisclosed second fee. A house's compensation is
maxBidSats, which is capped by the sponsor, published as a model under section 4.8.3 and itemised per auction in a statement the sponsor can reconcile. A share the house merely passes along is subject to none of that, so a house that took a percentage of it would be charging a party it has no billing relationship with, for a service it did not perform, at a rate nobody agreed.
8. Anti-Farming
8.1 The economic condition
Farming is prevented when the cost of obtaining a bounty exceeds the bounty, and by nothing else. Fresh identity keys are free and fresh installs nearly so, and both get cheaper with practice. What actually costs a farmer something is finding a site willing to sign a receipt, waiting out maturityBlocks, and surviving the sponsor's checks. A sponsor setting bountySats above the sum of those three is inviting farming and will get it. This document cannot fix that. What it does is ensure the sponsor is the party who chooses the number, sees the outcome and bears the loss, which is the only arrangement under which the number gets set correctly.
8.2 Silence is a mechanism, not a policy
The chooser cannot display the bounty because the offer does not carry it, per section 4.4.1.
- Nobody can sort wallets by payout, because no ordering over the offers reflects payout.
- A scraper reading every byte of every offer, from every site, forever, learns commitments and nothing else.
- An interface that tries to display the amount has nothing to display, so the rule cannot be broken by a careless implementation, only by a dishonest house.
- The silence is about the number, not the arrangement. No surface may state an amount, a range, a rank or a comparison of what any wallet pays. Any surface may explain that vendors compete for placement by funding the wallets of people who install them, per section 3.5.5, and a conforming chooser does exactly that when the reader taps "Sponsored".
- That line falls where it does because of what a farmer needs. A farmer needs an amount or a ranking; without one there is nothing to sort and the exercise degenerates into installing wallets at random, which is indistinguishable from being a customer. A farmer does not need to be told funding exists, and could not be prevented from learning it: a week after any real deployment it is common knowledge, and a specification built on keeping it quiet is built on something it cannot hold.
- Nothing is promised, so nothing is withheld. What is disclosed is that placement was paid for and how the competition works, which is what a person needs to read the list correctly, and which happens to be favourable rather than embarrassing.
- The silence about the number does not end at the choice. Section 6.4 requires the wallet to say who funded them and what that party now knows; it licenses no comparison, ranking, or claim about what a different wallet would have paid.
8.3 One receipt, one site, one identity
- One receipt per
auctionId. - One bounty per identity key per sponsor, per section 6.5.2.
- A receipt requires a site's signature, and sites are accountable, rate-limitable and excludable. A house MUST be able to stop serving a site and MUST publish that it has.
- A sponsor MAY publish a claim marker as a BRC-48 PushDrop output committing to the
auctionIdalone, so a second sponsor can detect a receipt already claimed elsewhere without learning who claimed it. A marker MUST NOT commit to the identity key. - A house MUST be able to stop serving a sponsor, and MUST publish that it has. Rule 3 gives sites that property and the first revision of this document gave sponsors nothing equivalent, which left a house able to exclude the party that shows a wallet and unable to exclude the party that ships it. A catalog SHOULD propagate an exclusion it is told about and SHOULD delist the wallet, since section 4.3.8 condition 10 makes delisting the effective remedy: a wallet out of the catalog cannot be bid for anywhere.
Rule 3 is where the real defence sits. A farmer generates identity keys at no cost, but each needs a receipt, each receipt needs a site, and each site is a party with a domain and a place in a settled record. The farm is visible in one place: a site issuing receipts at an implausible rate, or appearing in an implausible number of auctions. That is a thing a house can see and act on, and it is why site is in the bid request and why section 4.2.5 requires it to be attributable.
8.4 Delayed publication
- A house MUST NOT publish an opened commitment before the greater of the campaign's
maturityBlocksand 144 blocks after the auction. - A house MUST NOT publish, at any time, a live index, feed, ranking or query surface listing current bounties by wallet or sponsor.
- A house MUST NOT publish a losing campaign's opening at any time.
- It MAY publish aggregate statistics after the delay, and SHOULD, since sponsors setting budgets need them.
- Rules 1 and 3 are checkable where the anchor of section 4.7.9 exists: commitments carry no information, so the bid set may be anchored at auction time, and an opening appearing against an anchor younger than the delay was published early.
Auditability and farmability are the same property at different latencies. A sponsor needs the openings to verify its billing; a farmer needs them to know which wallet to install this morning. The delay separates the two, and it is keyed to maturity because a bounty that has already matured cannot be farmed by knowing about it.
8.5 What this does not prevent
Three failures belong here. The rest of what this design does not guarantee is in the ledgers and the Security Considerations rather than listed a third time.
- A determined farmer with their own site. Nothing stops somebody standing up a domain, issuing receipts to keys they control, and claiming bounties. What stops it being profitable is that the sponsor sees every claim arriving from one certifier and stops paying, and that the house can stop serving that origin. This is detection, not prevention, and it is imperfect. The site share of section 7.5 is not exposed to it: a farm cannot collect that without retention proofs from keys that transacted with a party other than itself.
- A malicious wallet vendor buying its way in front of newcomers. Section 4.3.8 conditions 10 and 11 mean it must first be catalogued and domain-attested, and section 3.2.1 means it cannot supply its own card, so what it can buy is position among vetted wallets rather than presence. That bounds the attack and does not end it: a wallet vetted honestly and turned malicious later passes every check in this document, and the remedy is delisting by a catalog whose governance this document does not specify. Section 7.5.5 exists because the retention share would otherwise pay a site for delivering that vendor's victims.
- Somebody who genuinely installs, claims and abandons the wallet. They did what was asked. If a sponsor considers that a loss,
maturityBlocksand the additional condition of section 6.6.2 are where to say so. The answer is not to restrict what the person may do with their own money.
9. Not Specified Here
- The sponsor console. Somewhere to author campaigns, set budgets, and read the records of sections 4.7 and 4.8. Reserved to a companion. Everything it operates on is already specified: the campaign object of section 4.3 is the complete authored artifact, and a conforming console MUST produce one valid against that section and signed by the sponsor's own key. Reserving rather than sketching is deliberate, because "every client MUST behave identically" is not a meaningful requirement for behaviour that has not been specified, and here the divergence would carry a budget.
- Fraud scoring. Whether a claim is genuine is the sponsor's judgement about the sponsor's money. This document specifies what evidence the sponsor receives, not what it should conclude.
- Catalog governance. Section 4.3.8 makes catalog listing a precondition of bidding, which places the whole admission decision in a party this document does not specify and BRC-137 section 3 only sketches. Who may list a wallet, on what evidence, who may delist one and how fast, and how an exclusion propagates between catalogs are all unaddressed, and the Security Considerations explain why that is now the load-bearing gap rather than a tidy-up.
- How a site sets its own price. Section 7.5 says when a site is paid and by whom; whether it can decline a campaign, negotiate
siteShareSats, or set a floor is commercial. - Cross-ecosystem reputation. Refusal records are publishable; aggregating them into a score is somebody else's document.
- Wallet-to-wallet migration. Named in section 5.2.4 as an audience and section 6.5 as an eligibility case. How the data actually moves is out of scope, and is the largest unwritten piece of the portability claim this document is built on.
- Bounties in anything other than satoshis. Excluded, because section 7.2.2 requires the value to be spendable and tokens, credits and vendor balances are not, in general.
What Is Verifiable and What Is Merely Promised
| Claim | Status | Checked by |
|---|---|---|
| An offer came from the auction house it names | Verifiable | Signature, section 4.4.4 |
| A campaign came from the sponsor it names | Verifiable | Signature, section 4.3 |
| The bounty was not reduced after the bid | Verifiable | Commitment and opening, section 4.5 |
| The house reported the bid it received | Verifiable, against the sponsor's own copy | Commitment, section 4.5.5 |
| The money to pay the bounty exists, before the bid | Verifiable | The voucher batch on chain, section 7.1 |
| A sponsor's bid was in the auction it entered | Verifiable, by that sponsor | Its own commitment in the bid set, section 4.7.6 |
| The winner outbid a given losing sponsor | Verifiable, by that sponsor | The winner's opening against its own bounty |
| Competing bids existed | Verifiable | The bid set |
| No emphasis was purchased | Verifiable | The offer schema is closed, section 4.4.7 |
| The record was not rewritten, and openings were not early | Verifiable, where anchored | The anchor, section 4.7.9 |
| A receipt's certifier is the domain it claims | Verifiable | The manifest, section 2.5 |
| A wallet connected at a site at a height | Verifiable | Connect receipt, section 6.2 |
| The bounty was delivered, and is spendable | Verifiable | The chain |
| The retention proof came from the funded key | Verifiable | Signature, section 7.5 |
| The person actually transacted with a third party | Asserted | Nothing. One bit, unfalsifiable either way |
| The full ranking was correct across all bidders | Asserted | Losers' openings are withheld on purpose |
| The predicate was evaluated honestly | Asserted | Nothing. The chooser runs on a device |
| A sponsored card shows what the catalog says, not what the sponsor says | Verifiable | The offer carries no card content, section 4.4.2 |
| The wallet in a sponsored slot was vetted by somebody | Asserted | Catalog governance is out of scope, section 9 |
| The site rendered its own cards uniformly | Asserted | No wire format reaches a stylesheet |
Four of eighteen rows are assertions, and they are the four no mechanism could reach: a bit only the person's own software can know, a distribution deliberately withheld, a computation on somebody else's device, and a stylesheet.
Three rows moved out of that column while this document was written, and the pattern in all three is the standard it tries to hold itself to: where a rule was asked to do a mechanism's work, find the mechanism or say plainly that there is none. Requiring pre-funding replaced a promise to pay. Publishing the bid set replaced the observation that a house's ranking is believed because a house's value is that its ranking is believed. Closing the offer schema replaced an appeal to conduct about badges and highlights. What survives in the assertion column survives because nothing cheaper than trust was available, not because nobody looked.
What This Costs, and Who Pays It
| Party | What they get | What it costs them |
|---|---|---|
| Newcomer | A funded wallet, chosen from at least two, on the merits of a uniform card | Their identity key is disclosed to the sponsor at claim, without being asked (sections 5.6, 6.3.3) |
| Relying site | A visitor who does not bounce at the wallet requirement, and a share when one stays | A signing key scoped to receipts and the liability with it; roughly a month's wait; a manifest entry that is a security-relevant artifact |
| Sponsor | A customer acquired at a price it set, before an audience it described | Capital locked before its first impression; a commitment consumed per auction entered rather than won; a bounty unrecoverable once claimed |
| Auction house | A market to run and a fee to charge | Inventory lost to every predicate miss; a published bid set that lets any sponsor audit its ranking; a fee model it must disclose |
| Catalog | Half the slots, unpurchasable | Nothing this document adds |
Two of these deserve saying plainly. The newcomer pays first and is told last: silent claiming is the largest concession here, made because a permission prompt in the first minute of somebody's first wallet is answered by dismissal. What is offered in return is candour rather than consent, and a reader who thinks that trade is wrong is disagreeing with a decision made on purpose. The sponsor pays for everyone else's certainty: pre-funding, the commitment batch and the published bid set all convert somebody else's trust into something checkable, funded by the sponsor locking capital earlier and burning batch entries faster than a promise-based design would need. That is the right party to charge, and it is also the barrier most likely to keep a small vendor out.
Security Considerations
The house is paid on one number and required to rank on another. Its revenue is maxBidSats, the winner is decided by bountySats, and it is the only party that sees both. The bid set of section 4.7.6 is the answer, and it is a mechanism rather than an exhortation: a losing sponsor recomputes its own commitment from its own key and asks whether its bid was in the set and whether the winner's opening exceeded its bounty. A house that drops a bid fails the first; a house that ranks on its own fee fails the second.
Collusion and entry deterrence are the residual risks, and they are the ones Klemperer says actually decide whether auctions work. A house can pad a bid set with commitments it made itself and never opens, making an auction look more competitive than it was. It cannot make a real bid disappear or make the winner's opening smaller than a loser's bounty, so the attack inflates apparent competition rather than defeating a bidder. Separately, the capital requirement of section 7.1 is a barrier to entry by design, and a small vendor is exactly who it excludes. Neither is solved here and both are more likely to matter than any pricing question in section 4.6.
A per-claim fee inverts the house's incentives. A house paid per settled claim earns more when farming succeeds; one paid per placement, listing or subscription does not. Section 4.8.5 requires disclosure rather than prohibition, because the alternative is a house with no revenue model, but implementers should read a per-claim fee as the thing it is.
Bid requests are attributable or they are refused. Section 4.2.5 exists because publishing the whole bid set means a commitment is consumed by entering an auction. Without attribution, forged requests naming a site the attacker does not control would drain every eligible campaign's batch until section 4.5.5 takes them dark, at no cost to the attacker. Rate limiting per attributed origin and pacing the batch draw are both required, and "the house can stop serving a site" is meaningless if the house cannot tell which site it is serving.
The chooser runs on the user's device and can be modified. Predicate evaluation is honest only where the client is. A modified client satisfies every predicate and sees every offer, which is acceptable because the consequence is bounded: offers carry no amounts, so the reward for lying is a wallet suggestion, and the claim path still needs a real site signature and a real maturity wait.
The ranking rule selects for the attacker, and the catalog is the only thing standing against it. This is the gravest risk in the design and it is structural rather than incidental. An honest vendor's bounty is bounded above by what a customer is worth to it. A vendor intending to steal is bounded by what it expects to steal, which scales with everything those users will ever hold, so it can outbid an honest competitor at every auction and section 4.3.1 forbids the house from considering anything else. The quantity this document ranks on correlates with intent to defraud. Nothing inside the auction fixes that, because the auction is a sorting rule and the problem is who is allowed into the set being sorted.
What contains it is that the auction sorts an already-vetted set and never admits to it. Section 4.3.8 conditions 10 and 11 make catalog listing and a domain-attested sponsor key preconditions of bidding; section 3.2.1 renders every card field from the catalog entry, so an offer cannot describe a wallet, only select one; section 8.3.5 lets a house stop serving a sponsor and a catalog delist the wallet. A deployment that relaxes any of these is not running a slower version of this design, it is running an unvetted one, and the bidder who benefits most from the relaxation is the one who intends to steal.
Three things remain true after all of that, and implementers should hold them plainly. Catalog governance is not specified here and only sketched in BRC-137 section 3, so the security of the sponsored half rests on a document that does not yet exist. A wallet honest for a year and malicious afterwards defeats every check here, and the catalog is the only party positioned to react. And the bounty makes the attack cheaper to run, because the attacker is paying its victims to install with money it expects to recover from them.
The chooser is a phishing surface: a dialogue shown at a moment of low familiarity containing install links. Choosers MUST verify offer signatures, MUST render every card field from the catalog entry per section 3.2.1 rather than from a bid response, and MUST pin vendor identity keys where the catalog publishes them per BRC-68.
The manifest is the site's consent and is only as safe as the site's hosting. An attacker who can write /manifest.json can point the chooser at a house of their choosing. Same exposure the manifest already carries for BRC-68 trust anchors and BRC-116 permission declarations, and the same mitigation: treat it as a security-relevant artifact, not configuration.
The site share is paid on an unverifiable bit, on purpose. Every alternative was worse. Establishing the fact from the chain means surveilling a key the sponsor funded, which BRC-42 derivation makes unreliable and section 5 spends its length refusing. Letting the site attest returns the payment to the party with the incentive to lie. Requiring the counterparty to attest requires them to know about an arrangement they are not part of. What is left is the wallet, which belongs to the party who pays on the answer.
Receipt issuance is the site's liability. A site signs that a particular key connected at a particular height. A site that signs loosely, or whose key is compromised, becomes a farm. Sites SHOULD rate-limit issuance, SHOULD scope the receipt key to this purpose alone, and MUST NOT reuse the key that authenticates users.
A bounty is an inducement whatever this document calls it. Keeping the number off every surface prevents it driving the choice; it does not change its regulatory character. A jurisdiction regulating inducements to acquire financial products will regulate this.
Maturity is a wait, and a wait can be attacked by reorganisation. A maturityBlocks of 6 is the documented minimum and is thin; 144 is comfortable. Sponsors funding meaningful amounts SHOULD use more.
Implementations
No implementation of the auction exists at the time of writing. The components it composes do.
- Catalog. BSV Radar exposes the wallet catalog contract of BRC-137 section 3, filterable by
category=walletandos=<platform>. The unsponsored slots of section 3.4.2 are what such a catalog already serves, so a conforming chooser can be built today against a house that always returns nothing. - Substrate detection and verified connect.
@bsv/sdk'sWalletClientperforms BRC-137's automatic substrate selection, and its signature methods produce the publicly verifiable signatures sections 6.2 and 6.3 require, with the counterparty omitted so it defaults toanyone. - Receipts. BRC-52 certificates in
@bsv/sdkand@bsv/wallet-toolbox, withacquireCertificateon the wallet side and ordinary certifier signing on the site side. The type identifier is derived rather than allocated. - Attestation checks. The BAP library implements the identity, attestation and key-rotation records section 5.3 relies on.
- Pre-funded settlement. Phar Lap implements the deterministic voucher batch, gap scan and payer/owner decoupling of BRC-227 that section 7.1 requires, and its
deriveVoucherKeyis the derivation the salts of section 4.5 also use. - Delivery. BRC-29 construction and
internalizeActionin@bsv/wallet-toolbox; the pull-based discovery of BRC-155 in its reference implementation.
What is verified. Every value in Appendix A is computed from the rules in the sections named, and rechecked by reading it back out of this document, so a document edited away from its own vectors fails as readily as a broken derivation. 100 checks, zero failures, no third-party imports, no network, deterministic across runs. Four are negative and they are the ones worth having: a tampered offer, an altered certificate origin, a flipped retention bit, and a commitment opened one satoshi low. A fifth asserts the separation section 4.5.2 exists to enforce, that a published salt is not the voucher key at the same index.
The scripts are not part of this proposal. They live at github.com/vincemedia/brc-conformance, under brc-300/, and are run against a checkout of this repository:
python3 verify_vectors.py path/to/apps/0300.md
That separation is deliberate and was asked for by the maintainers of this repository. A standards repository accumulating executable code acquires a maintenance surface nobody signed up for, and a reader then has to work out whether the code or the prose is normative. This document is normative. The vectors are a check on it, and where the two disagree the code has a bug.
The smallest useful conforming implementation
Almost none of this is needed to start, and three levels are worth naming so a site that wants the first need not read past section 3.
A conforming connect surface implements sections 2 and 3 and nothing else: a control labelled for connecting, a chooser holding at least two wallets from two vendors, every slot filled from a BRC-137 catalog. No auction, no sponsor, no bounty, no receipt, no manifest entry, because section 2.5 makes auction optional and a site that omits it has consented to nothing. The portability claim of section 3.1 is already demonstrated at this level, and it is buildable today.
A conforming auction participant adds sections 4 and 5: the manifest entry, the bid request, offer verification, client-side predicate evaluation. It issues no receipts and pays nobody; money moves between the sponsor and the newcomer through the sponsor's own claim endpoint.
A conforming settlement participant adds sections 6 and 7: receipts, claims, pre-funded vouchers, and the retention proof where it wants paying. This is where a site takes on the liability described above, and the only level requiring a signing key scoped to this purpose.
Section 8 is not a level; it constrains every party at every level. A house implements sections 4, 5 and 8, and is the only party needing all of them.
Appendix A: Worked Example
Every value below is computed from the rules in the sections named, and rechecked by reading it back out of this document, by the vectors linked from Implementations. Signatures are RFC 6979 deterministic ECDSA over secp256k1, low-S normalised and DER encoded, so they reproduce exactly; each is verified before printing, and each subsection that can carry a negative vector does.
A.0 Parties
Keys are derived from labels so anybody can regenerate them. They have no value and protect nothing.
| Party | Identity public key |
|---|---|
| Auction house | 037a990df4c7a1181b4a6e3b6690b67a260003cf42e9c59fc4e9524a15b4820589 |
| Sponsor, Nexus Labs | 0211ba1385b18c724b776ae387a26b5e0539458f242c45eba01842bee87929419f |
| Rival sponsor | 03f52cd299e1b0707b188c3372a61e9c498973423622be590c6a799eaf97bfbb40 |
| Site, example.com | 0332e410993b28f260394397d8487307a57af00f5c1e96f79844837dcf375cd640 |
| Newcomer | 030968f1ab174a293edd298402fe70f2b6a8a4345c4018af0e5df36c1e278d97cd |
The two sponsor private keys are printed in A.2 because the commitment derivation consumes them.
A.1 Derived identifiers
Type identifiers are derived from printed strings, in the manner of BRC-169 section 4.5, because BRC-52 leaves the type opaque and no document defines an authority over it. There is nothing to allocate and nothing to reserve.
| String | base64(SHA-256(string)) |
|---|---|
wallet choice offer v1 |
ib+7gKQf+w+DxKnFuWqVajxR7QIliw6wETwG+hasyxE= |
wallet choice receipt v1 |
ed3Kq2moHEFDShm3hzFmq0UmD8x2j+jh896zXBToz3o= |
wallet choice campaign v1 |
bcAznTfSJH5Xfpp3cQLuVnUjzY4+Od4C3FJ/kclUVTk= |
wallet choice settlement v1 |
w742NXb/0eQKFvlxY7NleqYLdSumb0546Eq3sFtR5CQ= |
None of the four contains a number, which is why renumbering this document from 250 to 300 required no recomputation of anything in this appendix except the values seeded from example labels.
The BRC-43 protocol identifier for every signature here except the receipt is [2, "wallet choice"]: thirteen characters, lowercase letters and one space, not ending protocol, not beginning p , satisfying the protocol name rules of BRC-100. The receipt uses BRC-52's own [2, "certificate signature"].
A.2 A commitment batch
Two consecutive slots of one campaign's batch, derived per section 4.5.
sponsorPrivKey = c0ffee00112233445566778899aabbccddeeff00112233445566778899aabbcc
campaignRef = 81c3d1d52d38fcb148c4aabef688f43ddc3039b701e18afdf5f9d540e7c6b1ba
bountySats = 21000
i = 0
salt_0 = 3c422c2f054d9e9a2ac262dd3c4a07a73be2ad0ba6da766a4c8f2561f5dd5fde
commitment_0 = e13a43f9a5c5d90a63947e66a8bd4a8159de40580dde2bb0e65b64e22429060d
i = 1
salt_1 = 3579b770cb29f7cee5d018a7ca63662376611e852b23869d5b9b099e22de2b87
commitment_1 = 9ebf244a4da8df05f726bde2b344fcb4028c1b08273b225b32c9081f26a6120e
Each salt is SHA-256("wallet choice salt" || sponsorPrivKey || campaignRef || uint32LE(i)) and each commitment is SHA-256(uint64BE(21000) || salt_i), whose first eight bytes are 0x0000000000005208.
The tag is why the salt can be published. Section 7.1 derives a voucher private key from the same three inputs without it, and that key controls the 21,000 satoshis this commitment is a bid for. At i = 0 it is:
voucherKey_0 = 89c101d51d76e5915bdd5920455bc400cdda6751ec577d4bd25ac0cb37c1eeb1
That value never appears in a settled record, a bid set, an offer or an aggregate. salt_0 above does, once the delay of section 8.4 has passed. Compare the two: they share an index and nothing else. An earlier revision of this document omitted the tag, which made them the same 32 bytes, so publishing an opening published the key to the money and anybody reading the record could sweep a voucher whose newcomer had not yet claimed it. The vectors assert the separation at two indices.
A second sponsor, bidding 12,000 satoshis on the same auction and losing, contributes the other entry of the bid set in section 4.7:
sponsorPrivKey = 0badc0de00112233445566778899aabbccddeeff00112233445566778899aabb
campaignRef = c77b1238c50c87a1b199d198dd1d9709ab2a64f68ea6de90d6740c7ba7fb9e8c
bountySats = 12000
salt_0 = b34e64c9c1977fea02ce402e11b01109a1b61a87ea12417ae955310cf48c4aa0
commitment_0 = 14c8c9a850de3b9bf16e7c9f7d0ec6d22ce7da673acffbaec471f77957d70f82
That commitment is published and never opened, per section 4.7.7. It is printed so the bid set is visibly two campaigns rather than two draws from one, and so the loser's own check is reproducible: recompute it from the key, find it in the set, compare 12,000 against the winner's published 21,000.
The first two commitments are the other point. The bounty is identical and the commitments share no structure, so an observer shown every offer a campaign ever makes cannot tell they are the same campaign, cannot tell the amount never changed, and cannot group them. That is what a fresh salt per auction buys, and why the salts are derived rather than stored.
A.3 The negative vector for the commitment
Reduce the amount by a single satoshi, keep salt_0:
bountySats = 20999
commitment = f5bd5bd52d243cd38759c27c649c698e5ecbc7701ffa6c1749f8622a2c07199d
It shares no structure with commitment_0, so a sponsor cannot open a commitment to any amount other than the one it committed to, and an auction house cannot report a bid as a nearby number.
A.4 An offer signature
Per section 4.4.5. The auction house signs the RFC 8785 canonical form of the offer with signature removed.
auctionId = qGLkBXQRMiGq9WxrFxaURDYkGf6cOHxlwEjAgF5AqeI=
invoice number = 2-wallet choice-offer
signing pubkey = 03b8139854a576984347d7fc0c1c664275e058b2b75a3b4ede631b9b3ad3996744
The signing key is BRC-42 derived with counterparty anyone, which is private key 1, so the shared secret is the house's own public key and any verifier holding only the house identity key rederives the same point.
canonical form:
{"auctionId":"qGLkBXQRMiGq9WxrFxaURDYkGf6cOHxlwEjAgF5AqeI=","bountyCommitment":"e13a43f9a5c5d90a63947e66a8bd4a8159de40580dde2bb0e65b64e22429060d","claim":"https://nexus.example/.well-known/metanet-connect/claim","commitmentIndex":0,"expiresHeight":892150,"house":"037a990df4c7a1181b4a6e3b6690b67a260003cf42e9c59fc4e9524a15b4820589","maturityBlocks":144,"predicate":{"all":[{"platform":["android","ios"]}]},"protocol":"metanet-connect-offer","slot":0,"sponsor":"0211ba1385b18c724b776ae387a26b5e0539458f242c45eba01842bee87929419f","vendorKey":"0211ba1385b18c724b776ae387a26b5e0539458f242c45eba01842bee87929419f","version":1,"walletName":"Nexus"}
signature:
3045022100d2f3c42662eb85348af7b439924849f194c7754d53086074de4f9ac5a66f31fc02203406da2b3b7f6282ce4be185932d9652876c7b10567faa83ed0a64545d6d449b
Two things are worth reading off the canonical form. It contains no amount, which is section 4.4.1. And it contains no icon, description, install target or selfCustody flag: vendorKey and walletName name a catalogued wallet and the chooser renders the card from the catalog entry, per sections 3.2.1 and 4.4.2. An offer is therefore incapable of describing a wallet, which is what stops a sponsor shipping its own card content past whoever vetted the catalog.
Change maturityBlocks from 144 to 6, leave the signature alone, and verification fails.
A.5 A connect receipt
Per section 6.2, as a BRC-52 certificate.
type = ed3Kq2moHEFDShm3hzFmq0UmD8x2j+jh896zXBToz3o=
serialNumber = vCKFPWfZoKsVflfUTzy3VvgTwe8tN8fu5us5XN9bou0=
subject = 030968f1ab174a293edd298402fe70f2b6a8a4345c4018af0e5df36c1e278d97cd
certifier = 0332e410993b28f260394397d8487307a57af00f5c1e96f79844837dcf375cd640
revocationOutpoint = 70b4346626d0132bb9076ae908534cbad5fbfed245c25b2031bd1dfc18863337.0
invoice number = 2-certificate signature-ed3Kq2moHEFDShm3hzFmq0UmD8x2j+jh896zXBToz3o= vCKFPWfZoKsVflfUTzy3VvgTwe8tN8fu5us5XN9bou0=
signing pubkey = 022ca1853a904300e502b63230e507c166869f4a2becb9ddae51902c61360680d8
BRC-52 signs the encrypted field values as Base64 strings, so field encryption is that document's construction and is not re-derived here; the five ciphertexts are opaque inputs, exactly as a verifier treats them. Reproduced is the part this document is responsible for: the type identifier, the field names, the CertificateBinary layout and the signature parameters.
preimage, CertificateBinary with includeSignature = false:
79ddcaab69a81c41434a19b7873166ab45260fcc768fe8e1f3deb35c14e8cf7abc22853d67d9a0ab157e57d44f3cb756f813c1ef2d37c7eee6eb395cdf5ba2ed030968f1ab174a293edd298402fe70f2b6a8a4345c4018af0e5df36c1e278d97cd0332e410993b28f260394397d8487307a57af00f5c1e96f79844837dcf375cd64070b4346626d0132bb9076ae908534cbad5fbfed245c25b2031bd1dfc1886333700050961756374696f6e49642c3944724c513237616b2f6633746c5142362f4c49782f797455657a4b58576b5a667a6a646a7330727a734d3d066865696768742c7642784f4c6b4c6d5456445866494c75527755376e7139594d485148636b30435766582b53494c686f426f3d066f726967696e2c586b79746170716f766578675354317a53386d7279716a4554346677564c4a596c394177666c6535374f6f3d0773706f6e736f722c666e4c5053337567655757452f6c724c524e6747547662556f3230667769575635634371594f512b4674413d0c77616c6c657456656e646f722c6d55754569756e74583667317a6353716e4b2f42737161574e61395938334578616837444b57754c326c343d
signature:
3045022100f431c48e22ee914ef6e1e98220e13e30d8049b1328dfa2a07a89728ee13da7af022062896e90efd429c3633e8eeddea054594adbf49e37acaba3c9e2a32926e8771e
Substitute one field value, here origin, and verification fails against the same signature. The certifier's signature covers which origin the connection happened at, so a site cannot be made to vouch for somebody else's.
A.6 A claim signature
Per section 6.3.2. The newcomer signs with key ID claim <auctionId>.
invoice number = 2-wallet choice-claim qGLkBXQRMiGq9WxrFxaURDYkGf6cOHxlwEjAgF5AqeI=
signing pubkey = 0327a7d3ea726715d7dce5ed0f2a36bb3b9b1c9922c45db14dbfb39f9d9fca9b59
canonical form:
{"auctionId":"qGLkBXQRMiGq9WxrFxaURDYkGf6cOHxlwEjAgF5AqeI=","payTo":"030968f1ab174a293edd298402fe70f2b6a8a4345c4018af0e5df36c1e278d97cd","protocol":"metanet-connect-claim","version":1}
signature:
304402200df3a0f4e763ec8bc1180ed2235c7c5aab2f16e9a0c91aa106a6a3f5a9d2b08f022061a013ebeeb041d1e35c53167984ecdaa1a1d309bd605209e0724a9b0153970e
The receipt member is omitted from the canonical form shown; it is the certificate of A.5 and including it would print the same bytes twice.
A.7 A retention proof, and the vector that earns its place
Per section 7.5. The same newcomer key signs, under key ID retention <auctionId>.
invoice number = 2-wallet choice-retention qGLkBXQRMiGq9WxrFxaURDYkGf6cOHxlwEjAgF5AqeI=
signing pubkey = 02b88aef420af4f1c7e9292e90bf870363073016b69a11ace64d67aaabf2356534
canonical form:
{"auctionId":"qGLkBXQRMiGq9WxrFxaURDYkGf6cOHxlwEjAgF5AqeI=","protocol":"metanet-connect-retention","retentionHeight":896460,"subject":"030968f1ab174a293edd298402fe70f2b6a8a4345c4018af0e5df36c1e278d97cd","transactedWithThirdParty":true,"version":1}
signature:
3045022100e14e2225471b417595cacdb2356ced6be315d636ec46b724e32be5dc3ac37d1702200c7ece2604b967021226b6c608ab1a73ed4d202ecb8920a78ef5a2888998e3a8
The signing keys differ, because the key IDs do, which is why section 6.3.2 prefixes them: one identity key signs a claim and a retention proof for the same auction under the same protocol, and one derived key signing two object types is an avoidable hazard.
The negative vector is the whole of what the site's share rests on. Flip the boolean to false, keep the signature:
{"auctionId":"qGLkBXQRMiGq9WxrFxaURDYkGf6cOHxlwEjAgF5AqeI=","protocol":"metanet-connect-retention","retentionHeight":896460,"subject":"030968f1ab174a293edd298402fe70f2b6a8a4345c4018af0e5df36c1e278d97cd","transactedWithThirdParty":false,"version":1}
Verification fails. The bit is unverifiable in that nothing proves the person really transacted with a third party, but it is not forgeable: only the funded key can produce a proof, and it cannot produce one saying something other than what it signed.
References
- BRC-3: Digital Signature Creation and Verification
- BRC-29: Simple Authenticated BSV P2PKH Payment Protocol
- BRC-42: BSV Key Derivation Scheme (BKDS)
- BRC-43: Security Levels, Protocol IDs, Key IDs and Counterparties
- BRC-48: Pay to Push Drop
- BRC-52: Identity Certificates
- BRC-65: Transaction Labels and List Actions
- BRC-68: Publishing Trust Anchor Details at an Internet Domain
- BRC-87: Standardized Naming Conventions for BRC-22 Topic Managers and BRC-24 Lookup Services
- BRC-100: Unified, Vendor-Neutral, Unchanging, and Open BSV Blockchain Standard Wallet-to-Application Interface
- BRC-104: HTTP Transport for BRC-103 Mutual Authentication
- BRC-110: Zero-Friction, Mobile-First Onboarding for MetaNet-Enabled Apps
- BRC-116: Wallet Permissions and Counterparty Trust
- BRC-137: Device-Aware Wallet Onboarding and Fallback Login for BRC-100 Applications
- BRC-151: BRC-100 Risk Assessment and Best Integration Practices
- BRC-155: Pull-Based Receive Discovery
- BRC-169: Universal Handle Addressing and Resolution for the Metanet
- BRC-185: Wallet Substrate Discovery, Selection, and Encrypted Mobile Connections
- BRC-219: Wallet Permission Prompt Liveness Contract
- BRC-227: Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens
- BRC-228: Unlinkable Payments under the Identity Paradigm
- Bitcoin Attestation Protocol
- Google: Android choice screen, auction winners and the 2021 withdrawal of the auction format
- IAB Tech Lab: OpenRTB Specification
- Klemperer, P. (2002). What Really Matters in Auction Design. Journal of Economic Perspectives 16(1), 169-189
- MDN Web Docs: HTTP response status codes
- RFC 2119: Key words for use in RFCs to Indicate Requirement Levels
- RFC 6979: Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)
- RFC 8785: JSON Canonicalization Scheme (JCS)
- Vickrey, W. (1961). Counterspeculation, Auctions, and Competitive Sealed Tenders. The Journal of Finance 16(1), 8-37