Protocol v1
Machine-oriented companion to SCOPE.md and PROTOCOL_IDS.md.
On-chain fields
Subscribe (txpass / v1 / sub):
| Field | Meaning |
|---|---|
seller | Seller identity key or paymail |
product | Product slug (e.g. imprintai-api) |
plan | Plan slug (pro-month | pro-year | enterprise-month) |
identity | Commitment SHA256(I_compressed || salt_32) hex, or bare compressed pubkey (dogfood flag) |
valid_from | Unix seconds |
valid_until | Unix seconds |
nonce | 32-byte hex |
revoke_out | Outpoint txid:vout or none |
Revoke (txpass / v1 / revoke): references receipt_txid (and optional fields). The facilitator accepts it only from the receipt's identity I, an owner of the product, or a configured seller key. Spending revoke_out also marks the entitlement dead. When a Txpass-issued receipt is revoked, Txpass publishes the canonical record txpass / v1 / revoke + \n + receipt_txid <64 hex> in a transaction funded by the issuer key (no reason or business detail on chain).
Fee: txpass / fee / <sats> — exact sats set when pricing is frozen; changes only on version bump.
Transaction shape
A subscribe receipt is a record of a subscription the publisher has already billed (Txpass is not part of the billing). It is one BSV transaction with:
- exactly one txpass OP_RETURN output:
OP_FALSE OP_RETURN <single push of the payload bytes>. The legacy formOP_RETURN <push>is also accepted.- The payload is UTF-8 text in canonical form: the lines
txpass / v1 / sub,seller …,product …,plan …,identity …,valid_from …,valid_until …,nonce …,revoke_out …, in that order. - Lines are separated by
\n, with a single space after each key, and there are no extra lines. nonceis 64 hex.revoke_outisnoneor<64 hex txid>:<vout>.
- The payload is UTF-8 text in canonical form: the lines
- either an input that spends a P2PKH output of an accepted Txpass receipt issuer key (a Txpass-issued receipt: Txpass built and funded it at the publisher's request; this is the normal form), or, legacy, one or more outputs paying the seller's configured pay-to locking script;
- any other outputs (e.g. change), which are ignored. There are no prices on the receipt.
Verification rules (portable)
Any operator running @txpass/verify with the same public config (GET /v1/pricing: seller keys, plan durations, grace, legacy pay-to scripts, and the receipt issuer keys and per-product issued plans from GET /v1/receipts/issuer) reaches the same answer. The facilitator applies these rules before activating a paid entitlement (verifyReceiptTx + verifyReceiptInclusion):
- The txid of the transaction equals the claimed
receipt_txid. - There is exactly one txpass OP_RETURN, and it is byte-identical to the submitted payload.
productis configured,selleris one of its seller keys, andplanis one of its plans. For a Txpass-issued receipt (rule 5, issuer input) the seller is the publisher identity Txpass authorised (a product owner), so no seller list is needed; the plan follows the product's issued-receipt plans.identity=SHA256(I_compressed || salt)for the revealedI, andIis the authenticated caller.- Issuer or pay-to: one input spends a P2PKH output whose public key (the key pushed in the unlocking script, matched against the parent output's
hash160) is an accepted Txpass receipt issuer key; or (legacy) at least one output pays a configured pay-to locking script. There is no plan-price check; optional legacyprice_satsin config is ignored. - Window:
valid_until > nowvalid_until − valid_from ≤ duration_days·86400 + grace_seconds(default grace 86400)- legacy pay-to receipts only:
valid_from ≤ now + skew(default 600 s) andvalid_until − valid_from ≥ 30·86400 - Txpass-issued receipts may start in the future (a renewal chained to the end of the current receipt; it is
not_yet_validuntil then, and becomes the live one when the current one ends). The issuer keeps the 30-day minimum except for the receipt that replaces another after a plan change, which may cover just the rest of the replaced receipt's period; the on-chain payload has no plan-change marker, so verifiers accept issuer-attested receipts shorter than 30 days - Paid plan config
duration_daysmust be an integer 30..3660. Free auto-enroll is unchanged and is not an on-chain paid receipt. - Agent sessions stay short (15–60 minutes) and are minted from a live entitlement; they are not short paid receipts.
- Inclusion: a merkle path (BUMP, BRC-74, either inside a BEEF or from a chain provider) whose root matches a block header (ChainTracker). The default minimum is 1 confirmation. Unconfirmed receipts are pending, not active.
revoke_out:none, or an outpoint that is unspent. Spending it revokes the entitlement.
Txpass-issued receipts: who is trusted
The issuer key only proves Txpass recorded this subscription for the publisher named in its seller field, at that publisher's signed request. Txpass issues only for an active owner of the live product (or a configured seller key), so verifiers need no per-product seller list for these receipts: the issuer key is the trust anchor. The current and retired issuer public keys are published at GET /v1/receipts/issuer and in GET /v1/pricing (receipt_issuers). A verifier that does not want to call Txpass pins those public keys in its own config; a fork of the verifier can accept a different issuer key (or none, which keeps the legacy pay-to rule). Publishers ask Txpass to issue with requests signed by a product owner identity key (txpass/v1/publisher-request, key ID v1 publisher); see the Facilitator API.
Billing/pricing is the publisher’s off-chain concern (not a protocol constant). The table in @txpass/protocol plans.ts may hold optional legacy price_sats placeholders for diagnostics only; they are unused for receipt verify.
Identity
- Prefer commitment form; reveal
I+saltat ingest over AuthFetch. - Dogfood may allow bare compressed pubkey behind a feature flag.
BRC-43
| Attribute | Value |
|---|---|
| Originator | txpass.app |
| Protocol ID | txpass |
| Key IDs | v1 auth, v1 delegate, v1 reveal, v1 publisher (signed publisher receipt requests) |
Descriptor
GET /v1/protocol returns JSON: prefix, version, originator, protocolId, keyIds, feeSats, commitmentDefault, verifierPackage, docsUrl. Built by @txpass/protocol buildProtocolDescriptor().
HTTP auth (BRC-103 / BRC-104)
Mutual authentication for protected facilitator routes uses the standard Metanet stack — not a Txpass-specific header dialect:
| Layer | Spec / package |
|---|---|
| Mutual auth | BRC-103 peer session |
| HTTP transport | BRC-104 — x-bsv-auth-* headers, POST /.well-known/auth |
| Server | createAuthMiddleware({ wallet }) → req.auth.identityKey |
| Client | @bsv/sdk AuthFetch |
| Entitlement | @txpass/auth-middleware requireEntitlement after identity is known |
Public routes (GET /health, GET /v1/protocol, GET /v1/pricing) remain unauthenticated. Protected routes (GET /v1/entitlements, POST /v1/receipts) require a live session identity (or dogfood stub header when TXPASS_AUTH_MODE=stub).
Originator domain: txpass.app. Protocol / key IDs: see PROTOCOL_IDS.md.