Python implementations of BSV BRC protocols.
The higher-level protocol companion to py-sdk (bsv-sdk on PyPI).
While bsv-sdk handles transactions, keys, and SPV — bsv-brc implements the
protocol layer: identity certificates, verifiable key linkage, HTTP mutual
auth + micropayments, and the server-side overlay machinery py-sdk leaves
open (it ships only the overlay client side).
| Package | BRC | What |
|---|---|---|
bsv_brc.brc052 |
BRC-42/43/52/53 | Identity certificates, ECDH key derivation, AES-256-GCM |
bsv_brc.brc094 |
BRC-94 | Verifiable ECDH shared secrets via Schnorr proof |
bsv_brc.brc104 |
BRC-103/104 | ASGI mutual-auth adapter over bsv.auth |
bsv_brc.brc105 |
BRC-105 | HTTP 402 micropayment middleware + client |
bsv_brc.brc22 |
BRC-22 | Server-side overlay topic submission (/submit) |
bsv_brc.brc24 |
BRC-24 | Server-side lookup services — the feed (/lookup) |
bsv_brc.brc87 |
BRC-87 | tm_/ls_ overlay name validation |
bsv_brc.overlay |
— | OverlayEngine (run a node) + OverlayClient (talk to one) |
bsv_brc.integration |
— | build_brc_app: auth + payments pre-stacked |
pip install bsv-brcFor Starlette/FastHTML middleware:
pip install "bsv-brc[starlette]"from bsv_brc import build_brc_app, PathPricing
from bsv.keys import PrivateKey
from bsv.wallet.wallet_impl import ProtoWallet
wallet = ProtoWallet(PrivateKey()) # load a persisted key in production
app = build_brc_app(
your_asgi_app,
wallet=wallet, # server identity + payment settle
pricing=PathPricing({"/premium": 100}), # 100 sats on /premium, rest free
)
# Stacks BRC-103/104 auth under BRC-105 payments. The default
# verify_payment maps the client's BRC-29 derivation prefix/suffix to
# wallet.internalize_action() for you — no hand-rolling.See examples/full_app.py for a complete,
fake-free server with auth, payments and a BRC-22 overlay topic.
bsv-sdk lets you talk to an overlay (LookupResolver,
TopicBroadcaster); bsv-brc lets you be one. Define a topic
(what gets admitted) and a lookup service (the feed), and serve both:
from bsv_brc.brc22 import TopicManager, AdmittanceInstructions
from bsv_brc.brc24 import LookupService, OutputRef
from bsv_brc.overlay import OverlayEngine
from bsv_brc.overlay.adapters.asgi import create_overlay_app
class PostsTopic(TopicManager):
def identify_admissible_outputs(self, beef, previous_coins):
# Decode with Transaction.from_beef(beef); pick which outputs to admit.
return AdmittanceInstructions(outputs_to_admit=[0], coins_to_retain=[])
class PostsFeed(LookupService):
def __init__(self): self.posts = []
def output_admitted(self, topic, txid, output_index, locking_script, satoshis):
self.posts.insert(0, OutputRef(txid, output_index, context=locking_script))
def lookup(self, question):
return self.posts[: (question.query or {}).get("limit", 20)]
engine = OverlayEngine(
topic_managers={"tm_posts": PostsTopic()},
lookup_services={"ls_posts": PostsFeed()},
)
app = create_overlay_app(engine) # POST /submit, POST /lookupSee examples/social_feed.py for a complete
~150-line social feed (the seed of an open-source peck.to).
The consumer side, defaulting to overlay.peck.to (swappable):
from bsv_brc import OverlayClient
overlay = OverlayClient() # defaults to https://overlay.peck.to
result = overlay.submit(beef_bytes, ["tm_posts"], require_admission=True)
# result.admitted -> bool; NoAdmissionError on a 200-but-rejected submit
posts = overlay.lookup("ls_posts", {"limit": 20}) # -> [LookupOutput(txid, beef, ...)]
state = overlay.state() # [{topic, count, stateRoot}]
ok = overlay.verify_state("tm_posts", outpoints) # local state_root == node'sAsync apps can use the pure build_submit_headers / parse_steak /
parse_lookup_answer / parse_state helpers with their own HTTP client.
from starlette.applications import Starlette
from bsv_brc.brc105 import PaymentMiddleware, NonceManager, StaticPricing
nonce_manager = NonceManager(secret=b"your-server-secret")
async def verify_payment(payment, identity_key):
# Call wallet.internalize_action() or your own verification
...
app = Starlette(routes=[...])
app.add_middleware(
PaymentMiddleware,
nonce_manager=nonce_manager,
pricing=StaticPricing(100), # 100 satoshis per request
verify_payment=verify_payment,
)from bsv_brc.brc094 import generate_proof, verify_proof
# Prover side
shared_secret, R, S_prime, z = generate_proof(my_private_key, their_public_key)
# Verifier side — no private keys needed
is_valid = verify_proof(prover_public_key, counterparty_public_key, shared_secret, R, S_prime, z)from bsv_brc.brc052 import issue, make_certificate_type
cert = issue(
certifier_private_key=certifier_key,
cert_type=make_certificate_type("example.com/identity/v1"),
subject_key=subject_pubkey_hex,
field_values={"email": "alice@example.com"},
serial_number=serial,
encrypted_field_keys=encrypted_keys_from_client,
)- BRC-103/104 — Mutual authentication (ASGI adapter over
bsv.auth) - BRC-22 — Server-side overlay topic submission (
/submit) - BRC-24 +
OverlayEngine— Lookup services + a runnable overlay node - BRC-35 — Global KVStore over overlay (pending byte-exact interop check)
- BEEF-backed lookup answers (JSON) +
SqliteOverlayStorage - BRC-87 —
tm_/ls_name validation - Per-topic state root +
GET /stateendpoint — matchesoverlay.peck.to/state - Overlay
OverlayClient(submit / lookup / state / verify) — defaults tooverlay.peck.to - Optional SPV verify on submit (
OverlayEngine(verify_tx=...), injectable ChainTracker) - paymail (→ bsv-compat), peck-anchor client + headers.peck.to ChainTracker (peck-infra lib)
- GASP-style peer sync + on-chain root anchoring
Deliberately out of scope (see CHANGELOG.md): BRC-101 (aspirational,
no normative behavior), BRC-108 (no Mandala/BRC-92/107 token base),
BRC-116 (needs an external sCrypt toolchain).
git clone https://github.com/datamynt/bsv-brc-python.git
cd bsv-brc-python
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[starlette,dev]"
pytest -v # 197 tests