Gate it to the holders
Drop a Sabroshi check on any site. Holders flex their way in, everyone else gets rekt. Copy the reference, ship it.
The canonical issuer
One published key is the entire trust anchor. Every authentic Sabroshi carries a BRC-42 signature from that key; anything signed by a different key is rejected on sight. Fetch the canonical issuer once from the public endpoint and cache it.
// The only key whose signatures count. Fetch once and cache.
export async function getCanonicalIssuer() {
const res = await fetch("https://sabroshi.com/api/verify")
return res.json() // { issuer: "02ab…", collection: "sabroshi-v1" }
}Verify authenticity (offline)
This function is the bouncer. It checks the BRC-42 publicly-verifiable signature and requires the signer to be the canonical issuer - no network call, no private key. Anyone can mint look-alike metadata, but forging a signature from the issuer key is computationally infeasible.
import { ProtoWallet, Utils } from "@bsv/sdk"
const ISSUANCE_PROTOCOL: [number, string] = [1, "sabroshi issuance"]
const anyone = new ProtoWallet("anyone") // verifies counterparty-"anyone" sigs, no private key
// Authentic ⇔ valid signature AND issuer === the published canonical issuer.
export async function verifySabroshi(d: SabroshiDescriptor, canonicalIssuerKey: string) {
if (d.p !== "sabroshi") return { authentic: false, reason: "not a sabroshi" }
if (d.issuer !== canonicalIssuerKey) return { authentic: false, reason: "not the canonical issuer" }
const { valid } = await anyone.verifySignature({
data: Utils.toArray(issuanceMessage(d), "utf8"),
signature: Utils.toArray(d.sig, "hex"),
protocolID: ISSUANCE_PROTOCOL,
keyID: `${d.collection}:${d.n}`,
counterparty: d.issuer,
})
return { authentic: valid, number: d.n }
}Put it together
Prove who is knocking, fetch what they hold, filter to canonical signatures. Your route returns 403 until at least one authentic Sabroshi clears all three checks.
// app/api/premium/route.ts - gate a route behind authentic ownership
import { NextResponse } from "next/server"
export async function GET(req: Request) {
const K = await requireIdentity(req) // 1. identity → verified public key
if (!K) return NextResponse.json({ error: "sign in" }, { status: 401 })
const { issuer } = await getCanonicalIssuer()
const holdings = await getHoldings(K) // 2. ownership lookup (your indexer)
const authentic = await filterAuthentic(holdings, issuer) // 3. keep only canonical-issuer sigs
if (authentic.length === 0) {
return NextResponse.json({ error: "Holders only" }, { status: 403 })
}
return NextResponse.json({ ok: true, sabroshis: authentic.map((a) => a.number) })
}Hosted verification API
POST a descriptor, get a verdict. The offline check is faster and trustless; the hosted API is a lower-friction starting point when you want to skip the crypto setup for now.
POST https://sabroshi.com/api/verify
body: { "descriptor": SabroshiDescriptor }
→ 200 { "authentic": true, "number": 7, "issuer": "02ab…" }
→ 200 { "authentic": false, "reason": "issuer is not the canonical Sabroshi issuer" }Hand it to an AI
Constants, descriptor format, signing scheme, ownership lookup options, end-to-end examples, and a security checklist - all in one paste-ready file. Copy the link and drop it into any AI agent or LLM context window.