Beersy
BRC-369

Keyed Content and Conditional Key Release

There was no honest way to sell access to a piece of content on chain. Existing paid-post systems just hid restricted text behind a rendering convention while leaving the real content sitting in plaintext on the blockchain for anyone to read for free. This makes it possible to actually encrypt content once, publish it anywhere, and hand out the key only to people who pay or otherwise qualify, so the restriction is real and the buyer keeps access forever.

Crumbs, Elon Moist, Ty Everett, Darren Kellenschwilerchanged 28 Aug 202663 min read

Reference for an AI

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

Summary

Why
Paid content on chain needs to be genuinely restricted and durably owned by the buyer, but encrypting to unknown future recipients and proving they got the right key without the publisher being online for every sale had no standard way of being done.
What
BRC-369 defines how to encrypt content once, publish the ciphertext openly, and release the content key per recipient once a payment or access condition is satisfied, with a signed that lets the recipient verify the key locally.
How
A publisher commits to and signs a content key before any recipient exists, publishes the ciphertext by its hash, and authorizes one or more releasers to hand the key to recipients who pay (BRC-29) or pass an access gate (BRC-190), with delivery over messaging and key derivation via /BRC-43.

What this lets you do

  • Encrypt content once and publish it for anyone to host
  • Verify ciphertext exists and matches the publisher's commitment before paying
  • Release a content key to a recipient after payment or a gate check
  • Detect and attribute exactly which party caused a failed key release
  • Prove publisher fraud to a third party using values already public

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

clientclientclienthost

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

The specification

Abstract

This document specifies how content is encrypted once, published where anyone can fetch it, and opened only by parties who satisfy a stated condition. Four properties follow, and each is unavailable to a paywall that decides what to render.

Restricted content is actually restricted. Not hidden by a convention that anybody who reads the underlying record can walk around.

A recipient keeps what they bought. They hold a key against ciphertext addressed by its hash, so access survives the publisher, the releaser and the platform that sold it, and needs none of them again.

A recipient can verify the goods before paying. Ciphertext is not secret, so establishing that the content exists, is retrievable, and is the bytes the publisher signed costs bandwidth and no permission.

Availability grows with distribution. Anybody may host ciphertext, including every recipient, so a well-bought publication becomes more durable rather than decaying with its publisher's interest.

These follow from one design decision: the publisher commits to the content key, and signs the , before any recipient exists. A recipient later handed a candidate key checks it locally, in one operation, without the publisher's cooperation. With the published ciphertext hash and the AES-256-GCM authentication tag this gives three checks performed in a fixed order, each of which fails alone and names a different party, and a failure at the third is a fraud proof the recipient can publish to anybody.

That is what makes this a verification rather than a trust relationship. BRC-94 already settles, from public values alone, that a revealed secret was computed correctly, and this document uses it as written. The commitment settles the separate question of whether the correctly computed secret was the key for this object, which only arises once there is a published object to commit against. The primitives are borrowed and the Prior Art section says from where.

Two condition profiles are given: a payment, and an access-gate under BRC-190. The first is what a paid post needs. The second is what a room with something to hide needs, and it carries a rotation requirement the first does not.

Motivation

BRC-190 section 9 says what an access gate is not:

A gate decides what a conforming client renders. It does not encrypt, does not authenticate, and does not constrain a non-conforming client at all. [...] A room whose confidentiality matters must key its contents so that non-holders cannot decrypt them; that mechanism is out of scope here, and a gate is not a substitute for it.

This document is that mechanism. It is written because the alternative is already deployed and does not work.

The deployed thing. In August 2026 an ecosystem shipped a paid-post feature in which the composer writes a command, the client rewrites it into a delimiter inside the post body, and everything after the delimiter is treated as restricted. The delimiter and the restricted content go into the same OP_RETURN, in plaintext, along with the attachments. Conforming clients decline to display the restricted part. Anybody who reads the transaction has it for nothing. The unlock is real, the payment is real, the split is real; only the restriction is imaginary.

That is not a criticism of the implementers, who built the only thing the available standards supported. There was no document describing how to key content so that a payment, and not a rendering convention, decided who could read it. Encryption is not hard; agreeing on where the key lives, who hands it over, and how a recipient knows they got the right one is the part that needs a standard.

Why this cannot be avoided by cleverness. Three approaches suggest themselves and all three fail. Encrypting to the recipient is impossible at publication time, because a paid post has no recipients yet and acquires them indefinitely. A hash-locked payment that forces the publisher to reveal k on chain to claim the money is genuinely trustless and solves the wrong problem: the first purchase publishes the key and every later reader gets in free. Serving plaintext from a paid endpoint works and quietly recreates the platform dependency the ecosystem exists to remove, because the recipient's access then lives on somebody else's server forever.

What survives is: encrypt once, release the key per recipient, and accept that some party must be reachable when a recipient arrives. That party is named, bounded by a delegation , and revocable, which is the most that can honestly be offered.

Prior Art

This document exists because the pieces were already here. The key derivation is BRC-42 and BRC-43, the symmetric construction is BRC-2, the wrapper is BRC-78, the transport is BRC-33, the content addressing is BRC-26, the payment is BRC-29, the verifiable secret revelation is BRC-94, and the pattern of encrypting once and releasing a key per recipient is BRC-52's. Each is cited where it is used, and none of them is restated here.

The one part that is new can be stated in a sentence: the commitment is published before any recipient exists. Being precise about which is which matters, because a document claiming novelty it does not have invites a reviewer to go looking for the rest of what it got wrong.

License servers. Commercial video has solved this problem since the 2000s and solved it this way. Widevine, FairPlay and PlayReady all encrypt content once under a content key, publish the ciphertext to any CDN that will carry it, and hand a per-device license carrying the key from an online server that decides who qualifies. The architecture here is the same architecture. Every substantive difference is about the license server: it is named in a signed object rather than configured in a client, bounded by a certificate anybody can fetch and read, revocable in one action whose effect a can verify, and replaceable by the publisher without re-encrypting anything. The recipient also keeps what they bought, because they hold the key rather than the right to ask for it again. What none of that changes is that a party who is online decides who reads, and this document is worse than useless if it lets an implementer believe otherwise.

Certificate field revelation, which is the closest neighbour in this repository. BRC-52 already does encrypt-once-release-per-recipient, in almost exactly this shape. A certificate field is encrypted under a random 32-byte ; that key is held encrypted in a ; and to reveal the field, the subject decrypts its own keyring entry and re-encrypts the raw field revelation key to a named verifier under [2, "certificate field encryption"]. Swap "field" for "content" and that is section 5.2 of this document. The pattern is 's and is used unchanged. What it does not carry, and what sections 2.2 and 5.3 add, is any way for the receiving party to establish that the key it was handed is the one that opens the object: a keyring revelation that fails leaves a verifier with a disagreement rather than a finding, which is the defect BRC-93 named in a neighbouring context.

So why not simply use a certificate. A reviewer who notices the resemblance should have the answer rather than have to construct it. Certificate revelation is driven by the subject, interactively, to one named verifier at a time, and the condition it satisfies is the subject's own decision to disclose. A paid publication inverts all four. The publisher is not present when a recipient arrives, the recipient is unknown when the content is published, there may be any number of them arriving indefinitely, and the condition is a fact about the recipient that somebody must evaluate rather than a choice the publisher makes in the moment. Putting a content key in a certificate field would require the publisher to be online for every sale, which is the constraint this document exists to remove.

Two further things a keyring does not provide, and this document needs. There is no commitment: a verifier handed a bad field revelation key simply fails to decrypt, with nothing signed in advance to point at and no way to tell a lying subject from a broken client. And the encrypted object is carried in the certificate, where content needs addressing, hosting and an availability story that section 3 has to supply.

Consumer paywalls. Substack, Patreon and Medium are stored grants with a server attached: a subscription row is written, and every read consults it. BRC-190's Prior Art sets out what a stored grant costs, and the whole of it transfers, with one addition specific to reading rather than joining. When the platform ends, the row ends, and so does access to everything bought under it. A recipient here holds a key against content addressed by hash, so what they bought outlives the arrangement that sold it. That is the property being bought, and it is the only one worth the additional machinery.

Paid posts on chain. The construction the Motivation describes has shipped, on this chain, in production. It is the honest form of the idea in every respect but the one that matters: the composer's intent is recorded, the payment is real, the split is real, and the restriction is a rendering convention over plaintext that anybody can read from the chain for nothing. It is named nowhere in this document, because the defect is a property of the design and not of whoever shipped it first, and every implementer who reaches for the obvious construction arrives at the same place.

Contingent payment. A hash-locked payment that forces a seller to reveal a key on chain to claim their money is the zero-knowledge contingent payment of Maxwell and others, from 2016. It is genuinely trustless and genuinely wrong here, for the reason the Motivation gives: it is a one-buyer construction, and the first purchase publishes the key to everybody.

Commitment. Publishing k * G to commit to a scalar k is the ordinary discrete-log commitment and is as old as Schnorr, and the digest form of section 2.2 is a plain hash commitment. What section 2.2 adds is the observation that the two forms have very different lifetimes when the commitment is durable and public: a hash commitment is bounded by SHA-256, a point commitment reduces the content's confidentiality to the discrete logarithm problem, and a specification that treats them as interchangeable is choosing one of those without saying so.

Secret sharing. Section 6.4's additive split with published per-share points is Feldman's 1987 observation applied to the degenerate m-of-m polynomial, where verifiability is free because the shares simply add. Pedersen's 1991 scheme and BRC-140 do the general case, and section 10 says why neither is used.

Group rekeying. Section 8's per-participant rekey is the naive construction that the Messaging Layer Security protocol, RFC 9420, exists to improve on. TreeKEM makes it logarithmic. Section 10 defers to it by name rather than gesturing at "future work".

Threshold key release. Lit Protocol operates a live network doing roughly what section 6.4 describes, with a rather than an m-of-m split and a set of nodes rather than named releasers. The comparison is instructive in one direction: a threshold buys availability that this document does not have, at the cost of shares that cannot be individually checked, which is the trade section 10 declines to make without a verifiable sharing scheme.

What this document contributes. The primitives above are borrowed. The results below are not, and they are stated as claims a reviewer can check rather than as a summary of effort.

1. Two independent questions about a released key, both answerable. BRC-93 identified that a party handed a revealed secret under BRC-69 could not confirm what they had been given, and BRC-94 answered it in full: a prover can demonstrate to any verifier, from public values alone, that a shared secret was correctly computed. Nothing here improves on that and this document uses it unmodified in section 5.5.

Publishing content raises a second and independent question that does not arise in BRC-69's setting, because a key linkage is revealed for audit rather than to open something: is this the key for that object? A correctly computed secret can still be the wrong scalar, and the right scalar can be wrapped badly, so neither answer implies the other. A commitment published and signed before any recipient exists answers the second. Together with BRC-94 the two cover a release completely, and section 5.3 is the first path in this repository where a recipient can settle both.

2. Failure is attributable. The three checks of section 5.3 run in a fixed order and each one fails alone: a hash mismatch means the ciphertext was substituted in transit or at the host, a commitment mismatch means the releaser delivered something other than the committed key, and a tag failure with the first two passing means the publisher encrypted under a key they did not commit to. A keyring revelation that fails tells a verifier only that something is wrong, and BRC-93 is the observation that this is a real cost.

3. Publisher fraud is provable to third parties, and the proof is self-limiting. Section 5.4 costs the recipient one publication of a key they already hold, is reproducible by anybody from values that were public before the dispute, and reveals nothing the fraud has not already established. A false accusation exposes itself, because anybody re-running the decryption watches it succeed. Neither property is available where the only evidence is that two parties disagree.

4. Verifiable secret sharing at the degenerate threshold, at no cryptographic cost. Section 6.4 splits the key additively and publishes a point per share. Every share is checkable on arrival against its own commitment, the sum is checkable against the whole, and a bad share names the releaser that sent it. BRC-140 is explicit that Shamir sharing as specified there is not verifiable, and Feldman commitments do not drop into it because its polynomial is defined by implicit points. The construction here buys individual verifiability using nothing but elliptic curve addition, and pays for it in availability rather than in machinery, since it is m-of-m.

5. A negative result about keyed access control. Encrypting a gated room's contents under a key that does not rotate leaves the room strictly worse off than the rendering convention it replaced. It is set out under Why a Keyed Room Must Rotate below, because it reaches past this document: any specification that keys group content without mandating rekeying on membership change has shipped the defect, and it is invisible on the day it is introduced.

6. Availability is decidable before payment. Because the ciphertext is published in the open and only the key is withheld, a prospective recipient can establish that the content exists, is retrievable, and hashes to what the publisher signed, before parting with anything. Section 3.2 makes this a MUST and BRC-190 section 3.3's three-state verdict makes an unreachable host indeterminate rather than a failure. A license server cannot offer this, because it gates the bytes; a subscription platform cannot offer it either.

7. Availability grows with distribution instead of decaying. Section 3.3 requires a recipient to retain what it fetched and recommends re-advertising it. Because the ciphertext is not secret, every recipient is a candidate host, so a widely bought publication becomes more durable over time. Every incumbent decays in the opposite direction, through link rot, platform closure or license-server sunset.

What is genuinely not claimed: the cryptography, the transport, the addressing, the encrypt-once-release-per-recipient pattern, or the architecture, which is a license server's. The contribution is what the commitment makes checkable, and what falls out of publishing ciphertext in the open rather than gating it.

Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in RFC 2119.

Throughout, "resolve" means the client resolution algorithm of BRC-169 section 5.7.

1. Terminology

  • Content key: a scalar k, from which the symmetric key for one piece of content in one is derived.
  • Commitment: a published, signed binding to the content key, in one of the two forms of section 2.2, made before any release.
  • Ciphertext: the encrypted content, addressed by its SHA-256 hash.
  • Descriptor: the signed object binding a commitment, a ciphertext, a condition and a set of releasers.
  • Publisher: the identity that signs a descriptor. It appears on the wire as author, which is the word a reader of a paid post recognises; the two are the same party throughout.
  • Releaser: an identity authorised by the publisher to hand k to recipients who satisfy the condition.
  • Recipient: a party requesting release.
  • Epoch: a numbered generation of content under one content key.
  • Release: the delivery of k to one recipient.

2. The Content Key

2.1 The key and the symmetric key

  1. k MUST be sampled uniformly from [1, n-1] with a cryptographically secure random number generator, where n is the order of the secp256k1 group. It MUST NOT be derived from the publisher's , from the content, or from any other reusable secret. A publisher who derives content keys deterministically from one secret loses every piece of content the moment that secret leaks.
  2. k MUST be fresh for each piece of content and, in the gate profile, for each epoch.
  3. The symmetric key is SHA-256("metanet keyed content symmetric v1" || k), where k is the 32-byte big-endian encoding of the scalar and || is concatenation.
  4. The symmetric key MUST NOT be derived from K. BRC-2 hashes the coordinates of an elliptic curve point to obtain a symmetric key, and an implementer following that shape by reflex will reach for the commitment, which this document publishes. Deriving the symmetric key from K publishes the content.
  5. This document borrows BRC-2's cipher choice, AES-256-GCM, and not its key derivation. k is not a BRC-42 child key and the document does not describe it as one. BRC-42 derivation appears in section 5.2, where it belongs, for delivering k to a named recipient.

2.2 The commitment

The commitment is what makes a release checkable without a trusted party. It is published before any recipient exists, so a publisher cannot hand two recipients different keys, cannot hand out a key that was never the content key without detection, and cannot later deny which key was committed to.

Two forms are defined. A descriptor MUST carry exactly one.

  1. Digest form, the default. The commitment is SHA-256("metanet keyed content commitment v1" || k), 32 bytes, carried as key.digest. A recipient checks a candidate scalar by hashing it.
  2. Point form, K = k * G as a 33-byte point in the compressed encoding of SEC 1 section 2.3.3, carried as key.point. A recipient checks a candidate scalar by multiplying. This form MUST be used where the key is split under section 6.4 and SHOULD NOT be used otherwise.
  3. The commitment domain string MUST differ from the symmetric-key domain string of section 2.1.3. Were they equal the published commitment would be the symmetric key. This is the same class of mistake as 2.1.4 and is just as total.
  4. The commitment MUST appear in the descriptor and is therefore covered by the publisher's signature under section 4.2.
  5. A recipient MUST verify a candidate scalar against the commitment before decrypting anything.

Why the default is a digest, and what the point form costs. The two forms are equally good at the job and differ entirely in what they leak over time.

A digest commitment reveals nothing about k to anybody who cannot already invert SHA-256. A point commitment publishes k * G, permanently and in a signed object, which means the confidentiality of the content reduces to the discrete logarithm problem on secp256k1 rather than to AES-256. An adversary who can solve one discrete log recovers k, then the symmetric key of section 2.1.3, then the plaintext. Because the commitment and the ciphertext are both published and durable, this is a harvest-now-decrypt-later exposure with a public record of both halves, and it reaches retroactively everything ever published in the point form.

That is a real cost over the lifetime a durable publication implies, and it is accepted only where section 6.4's splitting is in use, because verifiable additive shares need algebraic structure a digest does not have. A publisher who does not split gains nothing from the point form and should not choose it.

The wrapper of section 5.2 also rests on elliptic curve assumptions, being an exchange, but the exposure is not the same: a release payload is delivered to one and need never be public, where a descriptor's commitment is public by construction and permanent.

2.3 Segmentation

Content is encrypted in fixed-size segments so that a large payload can be verified and decrypted as it arrives rather than in one pass.

  1. A descriptor states a segment size in bytes. The final segment MAY be shorter. Content smaller than one segment is the single-segment case.
  2. The initialization vector for segment i is the 4-byte salt from the descriptor followed by i as an 8-byte big-endian integer, giving 12 bytes. This is the deterministic construction of NIST SP 800-38D: a fixed field and an invocation counter.
  3. The salt MUST be freshly sampled per content key. Uniqueness of the pair (symmetric key, initialization vector) is what AES-GCM requires, and reusing a salt across two contents encrypted under the same key would break it. Since rule 2.1.2 already requires a fresh key per content, the salt is a second independent guard rather than the only one.
  4. Each segment carries its own 16-byte authentication tag. The ciphertext is the concatenation, in order, of each segment's ciphertext followed by its tag.
  5. A recipient MUST verify each segment's tag before treating that segment's plaintext as content, and MUST NOT render a prefix of the content whose remaining segments have not yet verified unless it marks the content as incomplete.

3. The Ciphertext

3.1 Addressing and integrity

  1. The descriptor MUST carry the SHA-256 hash of the complete ciphertext and its length in bytes.
  2. Ciphertext SHOULD be published to content-addressed storage under BRC-26 and located by uhrp:// URL, which is Base58Check over the prefix ce00 and the SHA-256 hash. It MAY instead be carried inline, as content.inline, a base64 encoding of the identical bytes. Inline is appropriate for small payloads and costs exactly what carrying the same bytes in plaintext costs today. A descriptor MUST carry at least one of content.locator and content.inline. Where both are present they MUST be the same bytes, and a recipient that finds otherwise MUST treat the descriptor as malformed rather than preferring one: two sources disagreeing under one hash means at least one is lying, and guessing which is how a client ships a substitution attack.
  3. A descriptor MAY list several locators for one hash. They are alternative sources for identical bytes, not alternative content.
  4. A recipient MUST verify the hash of what it fetched against the descriptor before decrypting. Ciphertext is not secret, so this check is available to anyone at any time and costs nothing but bandwidth.

3.2 Availability, before payment

Ciphertext is not secret. That is not a weakness to be apologised for; it is the property that lets a prospective recipient establish, before parting with anything, that what they are being offered actually exists.

  1. A client MUST NOT present a release as available unless it has resolved at least one live advertisement for the content hash and confirmed that a host answers. Under BRC-26 an advertisement is a carrying an expiry and a content length, so "is anyone still hosting this" is a chain query rather than a promise.
  2. A client SHOULD fetch and hash-verify the whole ciphertext before the confirmation where its size permits, and MUST hash-verify it before decrypting in every case.
  3. Where a hash does not match, the client MUST refuse to proceed and MUST report the mismatch. This is BRC-218 section 4.6 applied to content rather than to : a resolution failure is surfaced at confirmation and blocks execution.
  4. Where availability cannot be determined, the result is indeterminate and MUST be presented as such rather than as failure, following the three-state verdict of BRC-190 section 3.3. A host that did not answer this minute may answer next.
  5. A client SHOULD show the number of live advertisements and the nearest expiry. Content held by one advertisement expiring next week is a different offer from content held by five with year-long terms, and a prospective recipient cannot price the difference without being told.

3.3 Retention and re-advertisement

  1. On a successful release, a client MUST retain the ciphertext it fetched, or the plaintext, so that later reads do not depend on any host remaining reachable.
  2. A client SHOULD advertise its retained copy under BRC-26.
  3. The publisher SHOULD remain a host of last resort and a client SHOULD warn a publisher before their own advertisements expire.
  4. Rules 1 and 2 exist because of an incentive this document would otherwise create. A publisher controls hosting, has already been paid, and can stop delivering by doing nothing. Because a recipient holds k permanently and needs the ciphertext only once, every recipient can become a host, and availability grows with distribution rather than decaying with the publisher's continued interest. A recipient who never re-advertises is still protected by rule 1; the ecosystem is protected by rule 2.

4. The Descriptor

4.1 Shape

{
  "metanetKeyedContent": "1.0",
  "author": "@crumbs@nexus.app",
  "epoch": 0,
  "content": {
    "cipher": "aes-256-gcm",
    "hash": "00fdc251d4d520fe8e343c471e293dfbfd20ef352cdd6cbb7b1fc4d266c8988a",
    "length": 192,
    "locator": ["uhrp://XUSuDZnz4PW5xBBn68YkcJWxZhE22a1ZXraTHPpTLcxrZVXsiiE6"],
    "salt": "4168fb26",
    "segment": 64
  },
  "key": {
    "digest": "2013064f87db545aa20f273b3abc8bdb470ddc69349cac55b7b397585af15797"
  },
  "release": {
    "condition": "payment",
    "on": "broadcast",
    "payees": [
      { "handle": "@crumbs@nexus.app", "share": 9300 },
      { "handle": "@fees@nexus.app", "share": 700 }
    ],
    "price": { "amount": "1.00", "currency": "USD" },
    "releasers": [{ "handle": "@keys@nexus.app", "delegation": "<outpoint>" }]
  },
  "signature": "3045..."
}
FieldRequirementMeaning
metanetKeyedContentMUSTVersion discriminator. A client meeting a version it does not implement MUST treat the content as unopenable and say so, rather than acting on the fields it recognises
authorMUSTThe publisher's handle, resolved per BRC-169 section 5.7
epochMUSTZero for content that does not rotate. See section 8
content.cipherMUSTaes-256-gcm in this version
content.hashMUSTSHA-256 of the complete ciphertext, hex
content.lengthMUSTByte length of the complete ciphertext, for the availability check of section 3.2
content.locatorMUSTArray of sources. MAY be empty where the ciphertext is inline
content.saltMUSTFour bytes, hex, per section 2.3
content.segmentMUSTSegment size in bytes
content.inlineconditionalBase64 ciphertext, where no locator is given
key.digestconditionalDigest-form commitment, hex. Exactly one of key.digest and key.point
key.pointconditionalPoint-form commitment, compressed SEC, hex. Required with key.shares
key.sharesconditionalPer-share point commitments, per section 6.4
previousconditionalContent id of the preceding epoch. Required for any epoch after the first
withdrawnMAYSigned statement that no further release will be made, per section 4.4
release.conditionMUSTpayment (section 7) or gate (section 8)
release.onMUSTFor the payment condition: when the releaser acts. See section 7.3
release.releasersMUSTEach names a handle and the of its delegation certificate
signatureMUSTPer section 4.2

The version discriminator carries no BRC number, and neither does any other name on the wire. A number in a deployed name outlives the document and points at a superseded revision.

4.2 Canonicalisation and signature

  1. The descriptor is canonicalised per RFC 8785 with the signature member removed, hashed with SHA-256, and signed by the publisher's identity key with a DER-encoded ECDSA signature.
  2. Canonicalising rather than signing the transmitted bytes lets an intermediary re-serialise the object without invalidating it, while any change to a value is detected. This is the rule BRC-169 section 7.2 applies to envelopes and BRC-190 section 2.3 applies to , and it is applied here for the same reasons.
  3. The SHA-256 of the canonical form is the content id. It is the identifier used in , in delegation qualifiers, and in release requests. It is self-certifying: no registry assigns it and nothing needs to be trusted to compute it.
  4. A verifier MUST check the signature against the identity key that author resolves to, per BRC-169 section 5.7.

4.3 Change

  1. A descriptor is immutable. Changing any field produces a different content id and is a different publication.
  2. Republishing at a different price does not reach a release that has already happened. A recipient who has been given k has it, and no later descriptor takes it back.
  3. Changing the content requires a fresh content key. Re-encrypting new content under a key that has already been released hands the new content to everybody who bought the old.

4.4 Withdrawal

  1. A publisher MAY publish a withdrawn statement over a content id, signed by the same identity key, after which conforming releasers MUST stop releasing and clients MUST stop offering the content.
  2. Withdrawal reaches nobody who already holds the key. It closes an offer; it does not recall anything.
  3. Without this, a publisher who wants to stop selling can only instruct their releasers to fall silent, which is indistinguishable to a prospective recipient from a releaser that has failed or is refusing them in particular. An offer that can be opened should be closable in a way the counterparty can read.

5. Release

5.1 The request

  1. A recipient sends a release request to a releaser's messagebox as a BRC-169 section 7.2 envelope. The envelope content is:
{
  "metanetKeyedRelease": "1.0",
  "request": "4506d4b11347776160524138f6711c7c747d65e6bf0ea2d3f5947b98dec25a97",
  "evidence": { "payment": "<Atomic BEEF>", "vout": [0, 1] }
}

request is the content id of section 4.2.3. evidence carries what the condition requires and is empty for the gate condition, where the releaser evaluates public facts about the sender rather than reading anything the sender supplies. A releaser MUST reject a request naming a content id it does not serve, rather than releasing a key for a descriptor it has not read. 2. The envelope signature identifies the recipient. A releaser MUST resolve and verify the sender per BRC-169 section 5.7 before evaluating any condition that depends on who is asking. 3. A releaser MUST evaluate the condition itself. It MUST NOT accept a recipient's assertion that the condition is met. 4. A releaser is a service anybody can address, and evaluating a condition costs it work before it has been paid anything. A releaser MAY therefore set a toll on its messagebox under BRC-169 section 8.2, which the requesting client surfaces and attaches like any other. The toll is a separate amount from the price and MUST be shown separately at confirmation, per BRC-218 section 5.1.4. A releaser SHOULD NOT set one where it is also a payee, since it is then charging twice for one transaction.

5.2 The response

This is the keyring operation of BRC-52 with a different object and a different party performing it. There, a subject re-encrypts a field revelation key to a named verifier; here, a releaser re-encrypts a content key to a recipient it has evaluated. The cryptography is the same and is not restated.

  1. The releaser returns k encrypted to the recipient's identity key, using the BRC-78 serialization and BRC-42 child key derivation over the BRC-2 symmetric construction.
  2. The derivation parameters are this document's, not BRC-78's:
BRC-43 parameterValue
2
protocolIDkeyed content release
keyID`SHA-256(content id
counterpartythe recipient, when releasing; the releaser, when receiving

BRC-78 specifies the message encryption and a random . Both are wrong here, for reasons BRC-52 already worked out for its own keyrings, where it likewise declined the generic protocol and chose a deterministic key ID.

A distinct protocol ID is what lets a wallet permission key release separately from ordinary messaging. Under BRC-43 and BRC-100 permissions are granted per protocol, so a release carried under message encryption cannot be authorised without also authorising every message the same counterparty sends.

A derived key ID binds the wrapper to what it carries. A payload cannot be replayed against a different descriptor or a different epoch, and either party recomputes the key ID from the descriptor rather than carrying a random value in state. 3. The payload carries the 32-byte big-endian scalar and nothing else. Everything a recipient needs to use it is already in the signed descriptor. 4. A releaser MUST NOT release to a recipient it has not authenticated, and MUST NOT release the same k to a broadcast or anyone counterparty. BRC-78 forbids the latter and this document restates it because the temptation is real: one broadcast release is cheaper than a thousand individual ones and destroys the property the document exists to provide.

5.3 Verification, in order

A recipient MUST perform these checks in this order and MUST stop at the first failure.

  1. The ciphertext is the committed one. SHA-256 of the fetched ciphertext equals content.hash.
  2. The key is the committed one. The candidate scalar matches the commitment, under whichever form of section 2.2 the descriptor carries.
  3. The key opens the ciphertext. Every segment's GCM tag verifies.

The order matters. Checking the tag first conflates three different failures into one indistinguishable error. Performed in order, each check names its own culprit: a hash mismatch means the recipient fetched the wrong bytes or a host substituted them; a commitment mismatch means the releaser handed over something other than the committed key; a tag failure with the first two passing means the publisher encrypted under a key other than the one they signed.

5.4 What a failed release proves

Where checks 1 and 2 pass and check 3 fails, the publisher committed to a key that does not open the ciphertext they committed to. The recipient can prove this to anybody by publishing k:

  1. The descriptor is signed and public, so K and content.hash are established.
  2. k * G == K is checkable by anybody.
  3. The ciphertext hashes to content.hash, so it is the committed ciphertext.
  4. Decryption under k fails its tag, which anybody can reproduce.

Nothing is disclosed by this that the fraud has not already established, because the content was never encrypted under the committed key in the first place. A recipient who fabricates the claim exposes the fabrication, since anybody re-running the decryption watches it succeed, and gives away content they paid for. The incentives make the proof self-limiting in both directions.

5.5 Proving a shared secret

A recipient who cannot decrypt the BRC-78 payload at all cannot tell a malformed release from a broken client. Where that is disputed, a releaser MAY furnish a BRC-94 Schnorr proof that it computed the ECDH shared secret with the recipient's key correctly. Verification is by the equations of BRC-94, using only public values.

This is optional and belongs to the dispute path, not to every release, because the two proofs answer different questions and only one of them is needed most of the time. BRC-94 establishes that the shared secret was computed correctly. The commitment of section 2.2 establishes that the key inside it was the key for this content. Neither implies the other: a releaser can compute a flawless shared secret around the wrong scalar, and can wrap the right scalar in a botched envelope. A recipient who can decrypt the payload at all needs only the commitment, which is why section 5.3 requires that and this section does not.

BRC-94 is complete for the question it poses, and the reason a second question exists here is that this document publishes an object, which BRC-69's audit setting does not.

The proof is worth using rather than inventing, because the ecosystem has already been round this loop. BRC-69 specified revealing a shared secret, BRC-93 observed that its first method left a verifier unable to confirm what it had been given, and BRC-94 restored the property with the Schnorr construction used here. A document that invented a fourth way to reveal a secret to a counterparty would be repeating that sequence rather than benefiting from it.

6. Releasers

6.1 Delegation

  1. A releaser other than the publisher MUST hold a BRC-169 section 9 delegation certificate from the publisher, whose scope includes the token release, optionally qualified by content id.
  2. The descriptor names the certificate's . A recipient MUST check that it is unspent before treating the releaser as authorised, subject to the indexing lag of BRC-169 section 4.2.
  3. Revoking the certificate ends the releaser's authority. It does not un-release anything already released, and a client MUST NOT present revocation as though it did.
  4. Unknown scope tokens fail closed under BRC-169 section 9.2, so a verifier that does not implement this document denies rather than ignores.

6.2 The publisher as fallback

The publisher knows k and MAY always release. A descriptor SHOULD list the publisher among its releasers. This costs nothing, since the publisher already holds the key, and it removes the single point of failure that a sole delegated releaser would otherwise be.

6.3 Several releasers, and the trade that cannot be avoided

  1. A descriptor MAY name several releasers, each holding k. Any one of them can serve a recipient, so availability improves.
  2. Each of them can also leak k, so the exposure grows with the same number.
  3. These pull in opposite directions and no configuration is good at both. More releasers means better uptime and more parties who can publish the content for nothing. Section 6.4 inverts the trade rather than resolving it. A publisher chooses, and a client SHOULD show a prospective recipient how many parties hold the key, because it is a fact about how long the restriction is likely to hold.

6.4 Split keys

Where leaking matters more than uptime, the content key MAY be split additively.

  1. Choose k_1 ... k_m with k_1 + ... + k_m = k (mod n) and give one share to each releaser.
  2. Publish every K_i = k_i * G in the descriptor, alongside the point-form commitment K that section 2.2.2 requires here. Anybody can check that K_1 + ... + K_m == K.
  3. A recipient collects all m shares, checks each against its own K_i on arrival, and sums them.
  4. No releaser alone can decrypt, and a share that fails its check names the releaser that sent it. That is what this construction buys over a threshold scheme: shares are individually verifiable using nothing but point addition.
  5. The cost is that this is m-of-m. Any releaser that stops answering makes the content unopenable for new recipients. Existing recipients are unaffected, holding k already.
  6. A threshold scheme would trade the verifiability back for availability. See section 11.
  7. Splitting forces the point-form commitment and with it the cost set out in section 2.2. A publisher who splits is choosing to rest the content's long-term confidentiality on the discrete logarithm problem, and a client MUST NOT offer splitting without saying so.

7. Condition Profile: Payment

7.1 Price and conversion

  1. price carries an amount and an ISO-4217 currency, or a amount.
  2. A fiat price is converted at release request time, not at publication time, through the oracle interface of BRC-169 section 6.2, subject to its staleness bound, its disclosure rules and its audit-trail requirements. This document does not restate them.
  3. Conversion to rounds half up.
  4. A fiat price carries at most two decimal places and a finer one is rejected rather than rounded, per BRC-218 section 3.6.

7.2 Payees

  1. payees MUST enumerate every recipient of the purchase as a resolvable handle with an integer share in basis points, and the shares MUST sum to 10000.
  2. The recipient's client MUST verify that the payment it constructs pays each declared payee its declared share, and MUST refuse to proceed on any mismatch.
  3. Division is by integer satoshis first, with any remainder allocated one satoshi at a time to payees in the order given. This is the rule of BRC-218 section 5.5.2 and is not restated differently here, so that two clients dividing the same price agree to the satoshi.
  4. The confirmation MUST show each payee and the amount each receives, per BRC-218 section 5.1.3.
  5. The publisher MUST be shown what the publisher receives at the moment the price is set, not the headline price. Where a platform takes a share, that share is a fact about the offer the publisher is making and belongs in front of them before they make it.
  6. This binds on-chain outputs and nothing else. A declaration cannot reach a private arrangement between a publisher and a platform, and this document does not pretend otherwise. What it prevents is an undeclared recipient of the purchase transaction, which is checkable, rather than an undisclosed commercial term, which is not.

7.3 The purchase

  1. The recipient constructs a BRC-29 payment paying every declared payee, serialised as (BRC-95), and delivers it in a BRC-169 section 7.2 envelope to each payee's messagebox and to the releaser.
  2. The sender MUST NOT broadcast. Each payee broadcasts on internalisation, per BRC-169 section 6.1. This is what makes a releaser that takes payment without releasing leave evidence rather than a deniable absence.
  3. The payment MUST be labelled with the content id using BRC-65, so that a second client can answer whether a given party has paid without asking the releaser.
  4. A recipient MAY use the BRC-228 ephemeral-sender profile. It is wire-compatible with an unmodified payee and keeps the recipient's long-term identity key out of the remittance, so that a releaser does not accumulate a purchase history against a stable identity.
  5. release.on states when the releaser acts: receipt on a well-formed payment, broadcast once a payee has internalised, or confirmation once the transaction confirms. The releaser bears the double-spend risk under receipt and the recipient bears the latency under confirmation. This is disclosed, not enforced, and a prospective recipient sees it before paying.

7.4 Refunds

  1. A release cannot be reversed. A refund under BRC-218 section 5.17 returns money and does not un-read anything.
  2. A publisher MAY refund. A client MUST NOT present a refund as revoking access.

8. Condition Profile: Gate Verdict

8.1 Epochs and the reason for them

Under the payment profile a key is released once and kept forever, which is what a purchase means. Room membership is the opposite: it lapses.

A room keyed under a single static key is weaker than the rendered gate it replaces. Under a rendered gate, delivery is the enforcement point, so a participant who is removed stops receiving. Under a static key the ciphertext is content-addressed and fetchable by anyone, and the only thing gating access is holding the key, so a departed participant decrypts everything the room publishes afterwards. Encryption without rotation converts a lapse from meaningful into decorative.

  1. Content published under the gate condition MUST carry an epoch number, starting at zero and increasing by one.
  2. Each epoch MUST have its own content key, sampled independently under section 2.1. Epoch keys MUST NOT be derived from one another.
  3. A descriptor covers one epoch. The commitment in it is the commitment for that epoch's key.
  4. The reason this is a MUST rather than a recommendation is set out under Why a Keyed Room Must Rotate, which is stated separately because it reaches past this document.
  5. A descriptor for any epoch after the first MUST carry previous, the content id of the descriptor it succeeds. This is what lets a client enumerate a room's epochs, see which it holds keys for, and notice a gap. Without it a room's history is a set of unrelated objects, and no client can tell an epoch it missed from an epoch that never existed.

8.2 Rotation

  1. A room MUST begin a new epoch whenever a participant ceases to satisfy its gate.
  2. A room MAY begin a new epoch at any other time.
  3. Content published before a rotation stays readable by whoever held the earlier key. This is deliberate and consistent with BRC-190 section 6: lapsing is not retroactive, and a participant who has already received messages has them.

8.3 Distribution

  1. On rotation, the new epoch key is released to each currently qualifying participant by the ordinary release protocol of section 5, one release per participant.
  2. This is linear in the number of participants and the document does not hide it. A room that rotates often and has many participants performs many releases. Rooms above a few hundred participants, or rotating more than occasionally, are not well served by this and should wait for the mechanism named in section 11.
  3. The releaser evaluates the BRC-190 gate itself, using the three-state verdict of BRC-190 section 3.3. An indeterminate verdict MUST NOT be treated as a pass.

8.4 What rotation does and does not give

  1. It gives forward exclusion: a departed participant cannot read later epochs.
  2. Because epoch keys are independent, a compromised epoch key does not expose any other epoch.
  3. It does not give proactive recovery. Rotation happens on membership change, so a room whose current key has leaked without any membership change stays compromised until it rotates. A room that wants recovery on a schedule must rotate on a schedule.
  4. It does not reach anybody who kept a copy of the plaintext. See section 9.

9. What This Does Not Do

Anybody who has been released a key can publish the plaintext. Encryption buys "not readable by the whole world by default"; it does not buy "only ever read by parties who satisfied the condition", and no mechanism in this document or any other reaches a recipient who has decided to redistribute what they legitimately opened.

Nor is a leak attributable as specified here. Every recipient in an epoch receives the same key, so a plaintext that appears in public names nobody. That is a limit of this construction rather than a fact about the problem: the traitor-tracing literature identifies a leaker from a shared decryption capability without per-recipient ciphertext, and section 10 records why none of it is specified here.

A releaser can defect. It holds k, so it can publish the content, sell it, or be compelled to hand it over, and section 6.4 raises the number of parties that must cooperate rather than removing the exposure. What this document provides is that the party is named in a signed descriptor, bounded by a certificate anybody can inspect, and revocable in one action. That is a smaller claim than "your content is safe" and it is the true one.

A client that says more than this to a publisher has sold them a guarantee nobody can deliver.

10. Choosing to Key

Everything above is how. This section is whether, because a document answering only the first leaves every implementer to discover the second by shipping it.

Key it when the restriction is the product. Paid writing, paid media, a room whose contents would embarrass its participants if they surfaced. In each the publisher is selling or protecting access itself, and a rendering convention that anybody can walk around is not a weaker version of that, it is a different thing wearing its name.

Do not key it when the restriction is a courtesy. Plenty of content is marked restricted to shape attention rather than to withhold it: an announcement staged for a launch, a draft not ready for comment, a room that is quiet rather than secret. A rendering convention is the right instrument there, costs nothing, and fails harmlessly. Keying such content buys a releaser, a hosting obligation and a permanent key, and buys them to solve a problem nobody had.

Do not key what must remain readable. A key can be lost, a releaser can vanish, and section 3.3's retention rule protects only those who already fetched. Anything that must be readable in ten years by somebody who has not read it yet is a bad candidate, and an archive that might one day be opened is a worse one: ungating a rendered restriction is an edit, while unkeying is a republication of the whole content in the clear, under a fresh identifier, leaving the original ciphertext in place forever.

Do not key what is already public. Once plaintext has been published, keying a later copy protects nothing and misleads a reader into believing otherwise. This is the case an implementer migrating an existing product will hit first, and the honest answer is that content published in the clear stays published: there is no retroactive fix, only a decision about what happens next.

Prefer not to key where the recipient cannot judge the offer. Section 3.2 lets a prospective recipient establish that ciphertext exists and matches its hash, and nothing more. The less able a reader is to tell from the preview whether the content is worth the price, the more the arrangement rests on the publisher's reputation and the less on anything the document provides. That is a fine trade for a known author and a poor one for a stranger, and no rule here improves it.

The general form: key what somebody would otherwise take, and render what somebody would otherwise merely see early. Where the honest answer is that nobody would go to the trouble of reading the raw record, a rendering convention is not a compromise, it is the correct instrument, and BRC-190 section 9 describes it accurately.

One consequence is about time. A publisher choosing between the commitment forms of section 2.2 is choosing how long the restriction has to hold. A digest commitment is bounded by SHA-256 and a point commitment by the discrete logarithm problem, and content whose value decays in a week is not troubled by either, while content intended to stay restricted for a career should not be published under a point commitment at all.

11. Not Specified Here

Recorded so that the next author does not rediscover them or ship them incompatibly.

Threshold sharing. Section 6.4 is m-of-m because additive shares are individually verifiable against published points. A t-of-m scheme would tolerate an absent releaser. BRC-140 specifies Shamir sharing over secp256k1 but is explicitly not a verifiable secret sharing scheme, and its polynomial is defined by implicit points rather than by coefficients, so Feldman commitments do not drop into it. A share arriving from a t-of-m releaser could not be checked on arrival, and a failed reconstruction would name nobody. Specifying this needs a VSS, and that is a key-derivation document rather than this one.

Tree-based rekeying. Section 8.3 is linear. Logarithmic group rekeying is well understood outside this ecosystem and would make large rooms practical. It needs a group key agreement specification with its own state machine and its own failure modes.

Proxy re-encryption. A releaser that could transform ciphertext for a recipient without ever holding k would remove the exposure of section 9 entirely. The constructions require pairings or schemes no BRC currently specifies.

Traitor tracing. Section 9 says a leak names nobody, and that is a property of this construction rather than of the problem. Broadcast encryption with traitor tracing has identified leakers from a shared decryption capability since Fiat and Naor in 1993, and Boneh, Sahai and Waters gave a fully collusion-resistant construction in 2006 whose ciphertext grows with the square root of the recipient count rather than linearly with it. Those schemes need bilinear pairings, which no BRC specifies and secp256k1 does not provide. Recording it is the point: an implementer should know the property is achievable, and that acquiring it means adopting a curve and a body of machinery this ecosystem has not adopted.

Watermarking. Per-recipient plaintext variation would also make a leak attributable, more cheaply and much more weakly, at the cost of publishing once.

12. Relationship to Other Documents

This document specifies a mechanism and no interface. It defines no verb, reserves none, and describes nothing a user does.

BRC-218 owns the conversational surface. Its section 5.22 /paywall is the verb that prices a message, its section 4 governs every confirmation this document's payment profile causes, and its section 3 owns how an amount is written and displayed. Where that document and this one both mention a price, this one is describing what the releaser evaluates and that one is describing what the user sees.

BRC-190 owns the gate. Its section 3.3 verdict is the input to the gate profile of section 8 here, and its section 2.1 keyed field is where a room states that it uses this document. Nothing here evaluates a gate condition, defines one, or adds one. Section 5 of that document is a fee and is not a keyed release: a fee is periodic, denominated in the gate's asset, paid to a named recipient and enforced by the gate itself, so a reader who stops paying stops qualifying. A release under this document is once per epoch and enforced by the key, so a recipient who has been given one keeps what it opens whatever the room later decides.

BRC-169 owns identity, resolution, envelopes, payments and delegation. Every party here is a handle resolved by its section 5.7, every message travels in its section 7.2 envelope, every payment follows its section 6.1 including the rule that the sender does not broadcast, every fiat conversion follows its section 6.2, and every releaser's authority is a certificate under its section 9.

The division holds in one direction: those three documents are usable without this one, and this one is not usable without BRC-169. A client implementing only this document and BRC-169 has keyed content with no conversational surface and no rooms, which is a coherent thing to build.

Why a Keyed Room Must Rotate

This result is set apart because it reaches past this document. Any specification that encrypts group content and does not require rekeying on membership change has shipped the defect below, and the defect is invisible on the day it is introduced.

Encrypting a gated room's contents under a key that does not rotate leaves the room strictly worse off than the rendering convention it replaced.

The argument is three steps. Ciphertext is content-addressed and published in the open, so anybody can fetch it, including somebody the room has removed. A key does not expire and cannot be recalled, so a participant who was given one holds it after they cease to qualify. Therefore a lapsed participant decrypts everything the room publishes afterwards. Under a rendering gate the room's enforcement point is delivery, and a removed participant simply stops receiving; under a static key, delivery has been decoupled from access and nothing is left to stop them.

The failure is worse than an unkeyed room in two ways rather than one. It is silent, because nothing observable changes at the moment the participant lapses. And it arrives precisely when a room believes it has strengthened itself, so the mistake is made by the operators taking confidentiality most seriously.

BRC-190 section 6 already accepts that lapsing is not retroactive: a participant who received messages has them, and no client can change that. What rotation preserves is the other half, that lapsing is at least prospective. Section 8 of this document turns that into a MUST, section 8.2 fixes when, and section 8.4 states what rotation does not recover.

The cost is real and section 8.3 does not hide it: rekeying is linear in the number of participants, which bounds the size of room this document serves well, and section 11 names the tree-based construction that would lift the bound.

What Is Checkable, and What Is Taken on Trust

The document's claim is that a release is checkable rather than trusted. That claim is worth totalling, because it is true of less than it sounds and the parts it is false of are the parts that matter most.

FactWho can check itHow
The descriptor is the publisher'sAnybodySignature over the RFC 8785 canonical form against the key author resolves to, section 4.2
The ciphertext is the one committed toAnybodySHA-256 against content.hash, section 5.3.1. Ciphertext is not secret, so this needs no permission
The released key is the one committed toThe recipientAgainst the commitment of section 2.2, section 5.3.2
The key opens the ciphertextThe recipientEvery segment's GCM tag, section 5.3.3
The publisher committed to a key that does not open the contentAnybody, once the recipient publishes the keySection 5.4
The releaser is authorisedAnybodyDelegation certificate and unspent revocation outpoint, section 6.1
Split shares are the committed onesThe recipient, per shareEach against its own K_i, and the sum against K, section 6.4
The payment pays the declared payeesThe recipient, before sendingConstructed outputs against release.payees, section 7.2.2
Somebody has paidAnybodyBRC-65 label naming the content id, section 7.3.3
The ciphertext is retrievable nowAnybodyLive advertisement plus a responding host, section 3.2
The releaser wrapped the key correctlyThe recipientOnly on request, and only where the releaser furnishes a BRC-94 proof, section 5.5

Against that, these are asserted and cannot be checked by anyone.

ClaimWhy it cannot be checkedWhat stands in for a check
The content is worth the priceNo mechanism can settle it. The commitment binds a key to a ciphertext and says nothing about what the ciphertext containsThe preview, and the publisher's reputation
The releaser has not leaked the keyA copy leaves no traceThe certificate is public and revocable, so the party who could leak is at least named
The releaser will release on paymentA refusal is indistinguishable from an outageSection 7.3.2's non-broadcast rule leaves evidence that payment was made
No recipient has redistributed the plaintextEvery recipient holds the same key, so a leak names nobodyNothing. Section 9
The declared payees are all the payeesThe declaration binds on-chain outputs, not private arrangementsSection 7.2.6 says so rather than implying otherwise
The ciphertext will still be retrievable laterAvailability now is not availability tomorrowSection 3.3's retention rule, which protects only those who already fetched
The publisher will not republish the content in the clearNothing prevents itNothing

Two things follow that a reader should take away rather than infer.

Everything in the first table is checkable by the recipient before or immediately after paying, and nothing in the second is. The mechanism moves the trust rather than removing it: what a recipient once had to trust about key delivery is now arithmetic, and what they must trust about the offer itself is exactly what it always was.

The releaser appears in both tables and is the hinge. Its authority is verifiable and its behaviour is not. Every honest description of this document reduces to that sentence.

What Keying Buys, and What It Costs

Five parties, and the ledger is not flattering to all of them.

PartyGainsPays
PublisherContent that is actually restricted rather than merely undisplayed, and a restriction that survives the client rendering itA hosting obligation under section 3.3, a releaser who must be chosen and trusted, and a key that cannot be un-released
RecipientA permanent copy, readable offline, that outlives the publisher, the releaser and the platformEverything is bought unseen. The preview is all there is, and section 3.2 can establish only that the ciphertext exists, never that it was worth the price
ReleaserNothingHolds the most dangerous secret in the arrangement, and is the party a compelled disclosure is addressed to
PayeesA declared share that a recipient's client verifies before paying, per section 7.2The declaration is public, which for a platform cut means publishing a commercial term that was previously invisible
EverybodyCiphertext anyone may host, so availability grows with distributionThe content's confidentiality now depends on a live party, where plaintext on chain depended on nobody

Two lines deserve drawing out rather than leaving in the table.

The recipient buys unseen, and this is worse than what it replaces. Under a rendered paywall a determined reader could check what they were buying by reading the chain. That was the defect, and removing it removes that too. Section 3.2's checks establish that ciphertext of a stated length exists and hashes to what was signed, which is a claim about existence and not about worth. Everything a prospective recipient can learn about the content itself comes from a preview the publisher wrote.

The releaser is charged the most and paid the least. It gains nothing, holds the key, and absorbs the legal and operational exposure of being the party who decides. A publisher acting as their own releaser under section 6.2 pays this cost themselves. A publisher who delegates it has found somebody else to carry it, and should expect that party to want either payment or control, neither of which this document specifies and both of which will show up in practice.

Security Considerations

The commitment is not a guarantee that the content is what was advertised. It binds a key to a ciphertext. A publisher who encrypts something worthless under the committed key passes all three checks of section 5.3. No cryptographic mechanism can settle whether content was worth its price, and section 5.4's proof should not be read as covering it.

Deriving the symmetric key from the commitment publishes the content. Section 2.1.4 states this as a MUST NOT because it is the single most damaging mistake available to an implementer, it is easy to make by pattern-matching on BRC-2, and it produces a system that appears to work perfectly.

A point commitment makes the content's confidentiality rest on the discrete logarithm problem. Section 2.2 sets this out and it belongs here too, because it is the one exposure in this document that gets worse with time rather than better. Where k * G is published, an adversary who can compute one discrete logarithm on secp256k1 recovers the content key, the symmetric key and the plaintext, for every publication in that form, retroactively, from records that are public and durable by design. AES-256 offers no protection because it is not what is being attacked. The digest form has no such property, which is why it is the default and why splitting is the only reason to give it up.

Initialization vector reuse breaks AES-GCM catastrophically. Two segments encrypted under the same key and the same IV leak the XOR of their plaintexts and, worse, allow forgery of the authentication tag. Section 2.3's construction is safe only while both the per-content key and the per-content salt are fresh.

Availability is checkable before payment and not after. A host can satisfy every check of section 3.2 and stop answering immediately afterwards. Section 3.3's retention requirement is the mitigation, and it works only for recipients who have already fetched.

A releaser learns who is asking. Under the payment profile a releaser accumulates a purchase history unless the recipient uses BRC-228. Under the gate profile a releaser evaluates readers' standing in aggregate, which no individual client previously did. The facts it reads are publicly enumerable by BRC-190's design, so nothing is disclosed that a determined observer could not assemble, but assembling it is exactly what a releaser does as a matter of course.

Revocation is not immediate and does not reach released keys. Section 6.1.3 requires clients to say so. A revoked releaser still knows k.

The gate profile is only as good as its rotation. A room that keys its contents and never rotates has replaced a working rendering convention with a broken cryptographic one, and its participants will believe the opposite. Section 8.1 exists for this reason.

Implementations

  • The primitives are in the maintained @bsv/sdk: BRC-42 and BRC-43 derivation, the BRC-2 symmetric construction, Schnorr.ts for BRC-94 proofs, and StorageUploader, StorageDownloader and StorageUtils for BRC-26 content addressing. Delegation certificates are BRC-52 certificates issued through the BRC-100 .
  • Messagebox delivery is BRC-33, resolved per BRC-169 section 5.2. No new transport is introduced.

Conformance levels

Three levels, each a superset of the one before. An implementation states which it reaches, because "supports BRC-369" is not a claim a counterparty can act on.

Reader. Fetches a descriptor, verifies its signature, runs the availability checks of section 3.2, requests a release, and performs the three checks of section 5.3 in order. Retains what it fetched per section 3.3. This is the whole of what a client needs to consume keyed content, it introduces no key management beyond an identity key, and it is where most implementations should stop.

Publisher. Adds key generation under section 2.1, the digest commitment of section 2.2.1, segmentation, descriptor construction and signing, and the disclosures of section 7.2.5 and section 10. A publisher implementation MUST also be able to act as its own releaser, per section 6.2, or it has shipped a dependency its users cannot escape.

Releaser. Adds condition evaluation, the release protocol of section 5.2, and delegation verification under section 6.1. A releaser implementing the gate profile additionally implements epochs and the rotation of section 8.2.

Independently optional at every level: the point commitment of section 2.2.2 and the splitting of section 6.4, which are needed only together; the BRC-94 proof of section 5.5; and the gate profile of section 8. A reader that does not implement section 6.4 MUST refuse a descriptor carrying key.shares rather than attempt the single-share path, per the version rule of section 4.1.

  • Conformance vectors, including the substituted-key case of section 5.4, are published at vincemedia/brc-conformance under brc-369/. They import nothing outside the Python standard library and need no network.

Failure Modes

Claim 2 of the Prior Art section is that failure here is attributable. This is that claim written out, and it doubles as the error surface an implementation owes its user. Every row is detectable by the party in the third column without anybody's cooperation.

What went wrongHow it showsDetected byAttributable to
Descriptor was altered after signingSignature does not verify against the resolved keyAnybodyWhoever served the descriptor
Ciphertext substituted at a host or in transitSHA-256 does not match content.hashAnybody, before payingThe host
No host is serving the contentNo live advertisement, or none answersAnybody, before payingNobody. Section 3.2.4 makes this indeterminate, not a failure
Releaser is not authorisedCertificate absent, expired, or revocation outpoint spentAnybodyThe releaser, or a publisher who named a lapsed one
Releaser delivered the wrong keyCandidate scalar does not match the commitmentThe recipientThe releaser
Release payload cannot be opened at allBRC-78 decryption fails before any scalar is recoveredThe recipientUndetermined until a section 5.5 proof settles it
One share of a split key is wrongThat share fails its own K_iThe recipientThe releaser that sent that share
Publisher committed to a key that does not open the contentChecks 1 and 2 pass, segment tags failThe recipient, provably to anybodyThe publisher, per section 5.4
Payment would not pay the declared payeesConstructed outputs disagree with release.payeesThe recipient, before sendingThe client, which MUST refuse
Content withdrawnSigned withdrawn statement over the content idAnybodyNobody. The offer is closed, per section 4.4
Epoch key does not open a later epochSegment tags fail on an epoch the holder never qualified forThe holderNobody. This is section 8.2 working

Two rows deserve reading together. Releaser delivered the wrong key and publisher committed to a key that does not open the content are the two ways a release can be dishonest, and the ordering of section 5.3 is what separates them: check 2 catches the first before any decryption is attempted, and only a failure at check 3 with check 2 passing implicates the publisher. An implementation that runs the checks in a different order collapses two different accusations into one error message and destroys the property.

The third row from the bottom is the only one where a conforming client refuses to act on its own user's instruction, and it is deliberate. A payment that does not match the signed declaration is either a corrupted descriptor or a client bug, and neither is a thing to send money into.

Test Vectors

Every value below is reproduced by example.py in the conformance set, and verify_vectors.py reads them back out of this document and checks them against a fresh run.

Parties

publisher identity key   02390d541cef55960403aed2ef478e5f8c67cdafd353366a46ee8f00c723e9743e
releaser identity key    03e69fe2022f968ea1bc233f6a97ab885d16965d747fdab152dc7e71675d856d69
recipient identity key   02f409d574960001d82cfc470956b1c13aa60e98fb88101cdf69797d597745c3f7

Content key and commitment

content key scalar       0ff730f7b66f7bb6626229ca20c5b4c2c47ecc32248203a14cd247ef7ed298d8
symmetric key            42dcb945809f42a6f827c8336affc7a04c284dfd6e4b65c6663f6e864240ad0e
commitment digest        2013064f87db545aa20f273b3abc8bdb470ddc69349cac55b7b397585af15797
commitment point         02f340de300c8614f85771ed721b55637b8f729e59f50ff7edc475d5832c522372
salt                     4168fb26
segment size             64
segment 0 iv             4168fb260000000000000000
segment 1 iv             4168fb260000000000000001
segment 2 iv             4168fb260000000000000002

The symmetric key is SHA-256("metanet keyed content symmetric v1" || k) and the digest commitment is SHA-256("metanet keyed content commitment v1" || k). The two domain strings differ by one word and the outputs share nothing, which is the whole of section 2.2.3: an implementation that reuses one string for both publishes its content in the descriptor.

The point commitment is shown because section 6.4 requires it when a key is split. A descriptor carries one form or the other, never both.

Ciphertext

144 bytes of plaintext across three segments of 64, 64 and 16 bytes, each followed by its 16-byte tag, giving 192 bytes.

8866ea607a527c96ae4b7f3c6d33b7490f02117906fe18ec91398ba1481f31c5
0c08bd553b08a30f82d8a85c529e0643dfdec581b959ae72a663483fe1d12fef
3591b5c89b353d2ee894aee750f18cd76854e3398febb09eecc0a264ec1937eb
9b8d77a42422b909c9c1d59a038de392a197a21749c2ca3ccfa89fed5964b362
731af7ce106883f9948863c5268579040f3c54f880991cb25513efc18fb9724b
d0d22e75a4e177232243aee5bd797624e80320f04e50a1663da30ab3559b5c54
ciphertext sha-256       00fdc251d4d520fe8e343c471e293dfbfd20ef352cdd6cbb7b1fc4d266c8988a
uhrp locator             uhrp://XUSuDZnz4PW5xBBn68YkcJWxZhE22a1ZXraTHPpTLcxrZVXsiiE6

Descriptor

The object of section 4.1 with signature removed, canonicalised per RFC 8785, 694 bytes:

{"author":"@crumbs@nexus.app","content":{"cipher":"aes-256-gcm","hash":"00fdc251d4d520fe8e343c471e293dfbfd20ef352cdd6cbb7b1fc4d266c8988a","length":192,"locator":["uhrp://XUSuDZnz4PW5xBBn68YkcJWxZhE22a1ZXraTHPpTLcxrZVXsiiE6"],"salt":"4168fb26","segment":64},"epoch":0,"key":{"digest":"2013064f87db545aa20f273b3abc8bdb470ddc69349cac55b7b397585af15797"},"metanetKeyedContent":"1.0","release":{"condition":"payment","on":"broadcast","payees":[{"handle":"@crumbs@nexus.app","share":9300},{"handle":"@fees@nexus.app","share":700}],"price":{"amount":"1.00","currency":"USD"},"releasers":[{"delegation":"d6e8f0c2a41b5937e2c8d1f4a70b6e35c9d2f81a4b7e0c63d5a9f2e841b7c06d_1","handle":"@keys@nexus.app"}]}}
content id               4506d4b11347776160524138f6711c7c747d65e6bf0ea2d3f5947b98dec25a97
publisher signature      3045022100f31abc7a941f4fb435cffd7208dce585d1f99d619b2179718858c8d6c897786302207db4f06e22d2bf0637d34915475777a74244a0e7fb7a6ae4ce511cd5e5aece34

Release payload

invoice number           2-keyed content release-fFiOxZVLQfuY79OnswIQo8X5vER3fmI982XpY3x0s24=
shared secret            873de6bc082794ae1de8390aec85b7641fb7dd88106cd0a3230468a8e9484547
payload length           182 bytes
3310424203e69fe2022f968ea1bc233f6a97ab885d16965d747fdab152dc7e71
675d856d6902f409d574960001d82cfc470956b1c13aa60e98fb88101cdf6979
7d597745c3f77c588ec5954b41fb98efd3a7b30210a3c5f9bc44777e623df365
e9637c74b36efef88a7d999ae66f952cc1cef07af27ea4538a999ec5cd15038d
d7a8636899298eff7a4477ee7c62fbc7f177a189749f2a17a3f7c57d89bc78ba
39f6b1143ed222f3a3d1a7a71e62206123f7e998f676

The substituted key of section 5.4

The same descriptor, republished with a ciphertext encrypted under a scalar other than the committed one, and the hash updated to match so that the publication is internally consistent.

fraud ciphertext sha-256 9920f5113ce14452add1275a0ab200e148db49c9708a08d6eab707f664b16ccc
fraud content id         525bdd364e003f08322bbb5927be94b0be97983718aec1ffd43e90ef271d29a1

Running the three checks of section 5.3 in order against the committed key:

1. ciphertext hash       passes
2. key commitment        passes
3. authentication tag    FAILS

This is the case a vector set exists to pin down. The first two checks passing is what makes the third meaningful: the recipient holds the key the publisher signed, applied to the ciphertext the publisher signed, and it does not open it.

Epochs and rotation, section 8

A gated room at epoch 0, then a participant lapses and the room rotates. The epoch 1 descriptor names its predecessor, so a client can walk the chain and see which epochs it holds keys for.

epoch 0 key commitment   34971f2cd950ba94b423b0106da1a314df4e6a8a1ab0f14ea093c1dd3dd49761
epoch 0 content id       9d49a6709eba64e3c937b841f114550adf4c0e6b906d4aae71dbe718fd744f60
epoch 1 key commitment   5ea2a1a09d0d1ac0416a45366e6e5ae281898882743145fb19d033218d6a2353
epoch 1 previous         9d49a6709eba64e3c937b841f114550adf4c0e6b906d4aae71dbe718fd744f60
epoch 1 content id       c95b2132b9a1e3b88c68d5d10bd7f86eba7397fdf8d6ed00f538b82241459068

The participant who left after epoch 0 holds the epoch 0 key. Applied to epoch 1 ciphertext it fails its authentication tag, and applied to epoch 0 ciphertext it still works.

epoch 0 key on epoch 1   FAILS
epoch 0 key on epoch 0   reads

Both halves are the point. The first is section 8.2, without which a lapse is decorative. The second is section 8.2.3 and BRC-190 section 6: lapsing is not retroactive, and a participant who was present keeps what was published while they were.

Split key, section 6.4

share 1 commitment       024eef7aee40a563602d32dd10e0347e75e90daa59d95eceed3430f11cd9c544a1
share 2 commitment       02cf2329e6bbfe531b4a7a790074c370cc231f0fa38ba5ab13dcbab3dcaba8922a
sum of commitments       02f340de300c8614f85771ed721b55637b8f729e59f50ff7edc475d5832c522372

The sum equals the point commitment of the content key, by point addition, computable by anybody from the descriptor alone. This is why splitting requires the point form and accepts its cost: the same check against a digest commitment is not available, because digests do not add.

Shared secret proof, section 5.5

S                        02d9f8c1eff274d6dd1a1387c7586c1c1435e085f2741d33083893ec35372f2255
R                        0250dd7cd4de626292c56d587acefc75a650286805fc37438488e6f6ac8a168415
S prime                  025b9fa91ae7e4a067d81792027a5e3e3c07cad78a8bf6f8adfc41d7845374f1f0
z                        891fb36580433a49de271af6fa65723ffb4ea74f7236d25ca5b0da20099e0e96

Both BRC-94 equations hold for the releaser's key and fail when the publisher's key is substituted for it.

Payee division, section 7.2

At 16.755 USD per BSV, a price of 1.00 USD is 5968368 satoshis. Dividing 9300 and 700 basis points by integer satoshis leaves a remainder of one, allocated to the first payee listed.

@crumbs@nexus.app        5550583 satoshis
@fees@nexus.app          417785 satoshis
sum                      5968368 satoshis

Appendix A: A Release, End to End

The Test Vectors give the values. This walks the same publication through in order, naming the section that governs each step and what each party can check at it. Every value shown is from that vector set.

A.1 The parties

@crumbs@nexus.app publishes. @keys@nexus.app releases, under a delegation certificate whose revocation outpoint the descriptor names. @fees@nexus.app takes a declared share of each sale and does nothing else. A reader arrives later and is not known to anybody at publication time, which is the whole difficulty.

A.2 Publishing

The publisher samples a content key, 0ff730f7...98d8, from a CSPRNG (section 2.1.1). Two values are derived from it and they must not be confused:

symmetric key      42dcb945...ad0e   SHA-256("metanet keyed content symmetric v1" || k)
commitment digest  2013064f...5797   SHA-256("metanet keyed content commitment v1" || k)

The restricted portion, 144 bytes, is encrypted in three AES-256-GCM segments of 64, 64 and 16 bytes under the salt 4168fb26, each with an initialization vector of salt-then-index and its own 16-byte tag (section 2.3). The result is 192 bytes hashing to 00fdc251...988a, uploaded to content-addressed storage and located at uhrp://XUSuDZnz....

The descriptor of section 4.1 binds all of it: the ciphertext hash and length, the commitment, the price, the payees, the releaser. Canonicalised per RFC 8785 it is 694 bytes, hashing to the content id 4506d4b1...5a97, which the publisher signs. Nothing about the reader exists yet, and that is what makes the commitment worth anything: it was fixed before there was anybody to cheat.

The publisher hands the content key to @keys@nexus.app. From this moment two parties can decrypt, and section 9 is the honest statement of what that means.

A.3 The offer, before paying

The reader's client fetches the descriptor and verifies the signature against the key @crumbs@nexus.app resolves to (section 4.2.4). It then runs section 3.2 against the ciphertext, which is not secret:

  1. Query ls_uhrp for 00fdc251...988a and find at least one unspent, unexpired advertisement.
  2. Probe the advertised host and confirm it answers with 192 bytes.
  3. Fetch and hash. 00fdc251...988a matches.

The reader now knows, before spending anything, that the content exists, is retrievable, and is the bytes the publisher signed. They do not know what it says. The confirmation shows the price, both payees and what each receives, and the number of live advertisements with the nearest expiry.

Had the hash mismatched, or had no host answered, section 3.2.3 blocks the purchase rather than warning about it. Had the lookup itself been unreachable, the verdict is indeterminate under 3.2.4 and is presented as such: a host that did not answer this minute may answer next.

A.4 Paying

At 16.755 USD per BSV the price of 1.00 USD converts to 5968368 satoshis, at request time rather than publication time (section 7.1.2). The declared shares of 9300 and 700 basis points divide it by integer satoshis, leaving a remainder of one allocated to the first payee listed, per BRC-218 section 5.5.2:

@crumbs@nexus.app   5550583
@fees@nexus.app      417785
                    5968368

The client checks these against release.payees and would refuse to proceed on any mismatch (section 7.2.2). It builds one BRC-29 payment covering both payees, labels it with the content id under BRC-65, and delivers it in envelopes to each payee's messagebox and to the releaser. It does not broadcast. Each payee broadcasts on internalisation, per BRC-169 section 6.1, which is what leaves evidence if the releaser takes the money and does nothing.

A.5 Releasing

The releaser evaluates the condition itself (section 5.1.3), does not take the reader's word for it, and refuses any request naming a content id it does not serve. The descriptor says "on": "broadcast", so it waits until a payee has internalised before acting, which the reader knew before paying.

It returns the content key in a BRC-78-serialized payload encrypted to the reader's identity key, under this document's own protocol ID keyed content release and a key ID derived from the content id and the epoch, so the wrapper cannot be replayed against another descriptor. 182 bytes, carrying the 32-byte scalar and nothing else.

A.6 Reading

The reader runs the three checks of section 5.3 in order and stops at the first failure.

1. SHA-256(ciphertext) == 00fdc251...988a          passes
2. SHA-256(domain || k) == 2013064f...5797         passes
3. every segment tag verifies                      passes

They decrypt, and section 3.3 requires them to keep the copy. From here the reader needs nobody: not the releaser, not the host, not the publisher. That is the property being bought, and it is the one a stored grant cannot offer.

A.7 The branch where the publisher cheated

Suppose the publisher had encrypted under a different scalar while committing to the first, and updated the hash so the publication was internally consistent. The reader's checks then read:

1. SHA-256(ciphertext) == 9920f511...6ccc          passes
2. SHA-256(domain || k) == 2013064f...5797         passes
3. every segment tag verifies                      FAILS

The first two passing is what makes the third mean something: the reader holds the key the publisher signed, applied to the ciphertext the publisher signed, and it does not open it. Publishing the key proves this to anybody, because all three steps are reproducible from public values, and it discloses nothing the fraud has not already established. A reader who fabricated the claim would be publishing a key that visibly works.

A.8 A room, and a rotation

The gate profile differs only in the condition and in what happens next. A room publishes at epoch 0 under commitment 34971f2c...9761, content id 9d49a670...4f60. A participant lapses, so the room rotates: a fresh independent key at epoch 1, commitment 5ea2a1a0...2353, and a descriptor naming 9d49a670...4f60 as its predecessor so a client can walk the chain.

The departed participant holds the epoch 0 key. Against epoch 1 ciphertext it fails its tag. Against epoch 0 ciphertext it still reads.

Both halves are deliberate. The first is section 8.2, without which a lapse is decorative and the room is worse off than under a rendered gate. The second is BRC-190 section 6: lapsing is not retroactive, and a participant who was there keeps what was published while they were.

References

Was this helpful?

Search Beersy

Search standards by number, title, author or topic