{"id":"cb26171e296860d1662f057b35acf7c97edfef22eb66e3d06f17a603afad6c28","pubkey":"2c917bfcfe4f3777ccacb4c968d6a3e9266d39a22db65c2cf2ca0c09fddf8638","created_at":1783579033,"kind":30817,"tags":[["d","nip-xx-recurring-subscriptions-treasury-events"],["title","NIP-XX: Recurring Subscriptions & Treasury Events"],["alt","Nostr Implementation Possibility: NIP-XX: Recurring Subscriptions & Treasury Events"],["client","NostrHub"]],"content":"# NIP-XX: Recurring Subscriptions & Treasury Events\n\n`draft` `optional`\n\nThis NIP defines event kinds for managing recurring subscriptions over Lightning and automated infrastructure payments within the Nostr ecosystem.\n\n## Motivation\n\nCreators and app operators need a protocol-native way to:\n1. Offer recurring subscriptions paid via Lightning (NWC/NIP-47)\n2. Verify subscription status from relay data without proprietary APIs\n3. Manage infrastructure payments (relays, media hosts, compute) under auditable rules\n\n## Design Goals\n\n- **Non-custodial**: No event kind implies custody of funds. Only wallet connections (NWC URIs) and rules are stored.\n- **Composable**: Builds on NIP-47 (Wallet Connect), NIP-57 (Zaps), NIP-17 (Private DMs), NIP-44 (Encrypted Payloads).\n- **Minimal**: Five event kinds cover the full lifecycle.\n- **Verifiable**: Any client can verify an active subscription by checking receipts signed by the plan publisher.\n\n## Event Kinds\n\n### `kind 31039` — Subscription Plan (Addressable)\n\nPublished by the **provider** (creator/app). Defines a subscription tier.\n\n**Tags:**\n- `d` — unique plan identifier (e.g. `\"supporter\"`)\n- `title` — human-readable plan name\n- `description` — plan description\n- `amount` — price in millisatoshis per period\n- `period` — ISO-8601 duration (`P7D`, `P1M`, `P1Y`)\n- `grace` — grace period as ISO-8601 duration (e.g. `P8D`)\n- `perk` — (repeatable) one perk per tag\n- `alt` — human-readable description for NIP-31 compatibility\n- `currency` — (optional) fiat reference currency code\n\n```json\n{\n  \"kind\": 31039,\n  \"pubkey\": \"<provider-pubkey>\",\n  \"tags\": [\n    [\"d\", \"supporter\"],\n    [\"title\", \"Supporter Tier\"],\n    [\"description\", \"Full access plus exclusive content\"],\n    [\"amount\", \"21000000\"],\n    [\"period\", \"P1M\"],\n    [\"grace\", \"P8D\"],\n    [\"perk\", \"All articles\"],\n    [\"perk\", \"Monthly Q&A\"],\n    [\"alt\", \"Subscription plan: Supporter Tier — 21,000 sats/month\"]\n  ],\n  \"content\": \"\"\n}\n```\n\n### `kind 1359` — Subscription Agreement\n\nPublished by the **subscriber**, referencing the plan. This is the signed, timestamped \"contract\".\n\n**Tags:**\n- `a` — plan coordinate (`31039:<provider-pubkey>:<plan-d-tag>`)\n- `p` — provider pubkey\n- `alt` — human-readable description\n\n**Content:** May contain a NIP-44 encrypted payload with subscriber contact preferences (email hash, notification channel).\n\n```json\n{\n  \"kind\": 1359,\n  \"pubkey\": \"<subscriber-pubkey>\",\n  \"tags\": [\n    [\"a\", \"31039:<provider-pubkey>:supporter\"],\n    [\"p\", \"<provider-pubkey>\"],\n    [\"alt\", \"Subscription agreement for Supporter Tier\"]\n  ],\n  \"content\": \"<optional NIP-44 encrypted contact preferences>\"\n}\n```\n\n### `kind 6301` — Payment Receipt\n\nPublished by the **provider** (or its delegated billing key) after each successful payment cycle. Receipts are the public, verifiable proof of active subscription.\n\n**Tags:**\n- `a` — plan coordinate\n- `p` — subscriber pubkey\n- `period_start` — unix timestamp of period start\n- `period_end` — unix timestamp of period end\n- `amount` — amount paid in millisatoshis\n- `bolt11` — (optional) the BOLT11 invoice reference\n- `preimage` — (optional) payment preimage as proof\n- `alt` — human-readable description\n\n```json\n{\n  \"kind\": 6301,\n  \"pubkey\": \"<provider-pubkey>\",\n  \"tags\": [\n    [\"a\", \"31039:<provider-pubkey>:supporter\"],\n    [\"p\", \"<subscriber-pubkey>\"],\n    [\"period_start\", \"1719792000\"],\n    [\"period_end\", \"1722470400\"],\n    [\"amount\", \"21000000\"],\n    [\"alt\", \"Subscription receipt: Supporter Tier — Jul 2026\"]\n  ],\n  \"content\": \"\"\n}\n```\n\n### `kind 4739` — Cancellation / Lapse Notice\n\nPublished by **either party** to record termination or lapse.\n\n**Tags:**\n- `a` — plan coordinate\n- `p` — the other party's pubkey\n- `reason` — `\"canceled\"`, `\"lapsed\"`, or `\"expired\"`\n- `alt` — human-readable description\n\n```json\n{\n  \"kind\": 4739,\n  \"pubkey\": \"<subscriber-or-provider-pubkey>\",\n  \"tags\": [\n    [\"a\", \"31039:<provider-pubkey>:supporter\"],\n    [\"p\", \"<other-party-pubkey>\"],\n    [\"reason\", \"canceled\"],\n    [\"alt\", \"Subscription canceled: Supporter Tier\"]\n  ],\n  \"content\": \"\"\n}\n```\n\n### `kind 39729` — Treasury Rule (Addressable, Private)\n\nPublished by the **creator** to define automated payment rules for infrastructure.\nContent SHOULD be NIP-44 encrypted so configuration is portable but private.\n\n**Tags:**\n- `d` — unique rule identifier\n- `alt` — human-readable description\n\n**Encrypted content (JSON):**\n```json\n{\n  \"payee\": \"<lightning-address-or-npub>\",\n  \"maxAmount\": 15000,\n  \"interval\": \"P1M\",\n  \"autoPay\": true,\n  \"category\": \"relay\"\n}\n```\n\n## Verification Rules\n\nA reader validates an **active subscription** for subscriber S on plan P as:\n\n1. Find the latest `kind 6301` event where `pubkey` = plan provider AND `#p` contains S AND `#a` contains P's coordinate.\n2. Parse `period_end` tag.\n3. If `period_end` > `now`, the subscription is active.\n4. Additionally check no `kind 4739` event exists from either party more recent than the receipt.\n\n## Privacy Considerations\n\n- **Email addresses** MUST NOT appear in plaintext on relays. Use NIP-44 encryption in `kind 1359` content or SHA-256 hashes.\n- **Treasury rules** (`kind 39729`) SHOULD encrypt their content with NIP-44.\n- **NWC connection strings** are NEVER published as events. They are stored locally and encrypted at rest.\n\n## Interaction with NIP-47\n\nNWC connections used for subscription pull payments SHOULD:\n- Have explicit budget caps matching `amount × 1` per period\n- Include expiry dates\n- Be revocable by the subscriber at any time\n\nThe billing engine MUST be idempotent: for a given (agreement, period) pair, at most one pull payment attempt succeeds. Retries use the same period identifier to prevent double-charging.\n\n### `kind 7012` — Multi-Rail Payment Receipt (Module E Extension)\n\nPublished by the **provider** after a payment settles via any rail (Stripe, NWC, Zaprite). Extends `kind 6301` with rail identification and split allocation transparency.\n\n**Tags:**\n- `a` — plan coordinate (if applicable)\n- `p` — subscriber pubkey (if known)\n- `amount` — gross amount in claim currency\n- `currency` — ISO 4217 currency code (CHF, EUR, USD, or SAT for native Lightning)\n- `rail` — settlement rail identifier: `stripe`, `nwc`, or `zaprite`\n- `settled_at` — unix timestamp of confirmed settlement\n- `external_ref` — external system reference (Stripe invoice ID, BOLT11, Zaprite order ID)\n- `purpose` — `subscription`, `share`, `ticket`, `crowdfund`, `auction-clear`, `one-time`\n- `period` — (optional) billing period identifier for recurring payments\n- `split` — (repeatable) allocation tag: `[\"split\", \"<label>\", \"<amount>\", \"<percentage>\", \"<pubkey?>\"]`\n- `fee` — estimated processing fee amount\n- `alt` — human-readable description\n\n```json\n{\n  \"kind\": 7012,\n  \"pubkey\": \"<provider-pubkey>\",\n  \"tags\": [\n    [\"a\", \"31039:<provider-pubkey>:supporter\"],\n    [\"p\", \"<subscriber-pubkey>\"],\n    [\"amount\", \"19.00\"],\n    [\"currency\", \"CHF\"],\n    [\"rail\", \"stripe\"],\n    [\"settled_at\", \"1720454400\"],\n    [\"external_ref\", \"in_live_abc123\"],\n    [\"purpose\", \"subscription\"],\n    [\"period\", \"1720454400-1723132800\"],\n    [\"split\", \"Autorin\", \"14.44\", \"80\"],\n    [\"split\", \"Gemeinsame Kasse\", \"2.71\", \"15\"],\n    [\"split\", \"Abwicklung\", \"0.90\", \"5\"],\n    [\"fee\", \"0.95\"],\n    [\"alt\", \"Payment receipt: 19.00 CHF via stripe for subscription\"]\n  ],\n  \"content\": \"\"\n}\n```\n\n**Design Notes:**\n- `kind 7012` complements `kind 6301` (which remains the Lightning-native receipt). Applications SHOULD query both kinds for complete payment history.\n- The `rail` tag enables clients to filter/display receipts by payment method without inspecting rail-specific metadata.\n- `split` tags make the payout breakdown verifiable by any third party — this transparency is a feature, not a leak (Bajour logic for community-funded media).\n- The `fee` tag shows the *displayed* estimate. The platform may absorb higher actual fees (for small card payments) to give authors a predictable rate.\n\n## Prior Art\n\nThis NIP was designed with awareness of earlier NIP-88 (recurring subscription) proposals. Key divergences:\n- Uses generated, conflict-free kind numbers rather than assumed ranges\n- Separates agreement (subscriber-signed) from receipt (provider-signed) for bilateral auditability\n- Adds treasury management events for infrastructure spend\n- Specifies verification rules for third-party status checks (enabling Module C embeddable primitives)\n\n---\n\n# NIP-XX — Stablezaps: Asset-Denominated Zaps & Recurring Subscriptions\n\n`draft` `optional`\n\n**Status:** Draft v0.1 (skeleton for review) · **Author:** Pareto / Athanor Studio · **License:** Public domain\n**Depends on:** NIP-01, NIP-57 (Lightning Zaps), NIP-47 (Nostr Wallet Connect) · **Relates to:** BOLT11, BOLT12, Taproot Assets (Lightning Labs), RGB\n**Kind numbers are provisional** pending the NIP review process.\n\n---\n\n## Abstract\n\nThis NIP extends NIP-57 zaps with **asset denomination** (e.g., USDT) and defines a minimal, ledger-derived **subscription** scheme on top of them. It allows a payment request to be denominated in a stable unit, settled over Lightning either natively (Taproot Assets / RGB asset invoices) or in BTC at a disclosed rate, and it allows recurring creator revenue (\"$5/month\") whose entire state — active, lapsed, cancelled — is derivable from public zap receipts alone. No custodian, no subscription database, no platform.\n\n## Motivation\n\nZaps made value transfer a native gesture of the social web, but they are denominated in a volatile unit. Tips work in sats; **income does not**: the proven revenue model of independent publishing is the fixed-price monthly subscription, and it cannot be priced in an asset that moves 10% in a week. Since USDT became transactable over Lightning via Taproot Assets (March 2026), stable-denominated settlement on open rails is possible — what is missing is the Nostr convention that lets any client display, request, verify, and renew such payments interoperably. This NIP is that convention.\n\nDesign goals, in order: (1) **auditability** — every claim about who paid whom, how much, in what unit, must be verifiable from public events and the embedded invoice, never from a self-declared tag alone; (2) **backwards compatibility** — a NIP-57-only client must degrade gracefully; (3) **settlement neutrality** — BTC remains the universal settlement asset; asset-denominated requests MUST remain payable in BTC; (4) **issuer honesty** — clients implementing this NIP MUST NOT present issuer-backed assets as trustless (see Security Considerations).\n\n## Terminology\n\n- **Stablezap** — a zap whose requested amount is denominated in a non-BTC unit.\n- **Asset invoice** — a BOLT11/BOLT12 invoice carrying a Taproot Assets or RGB asset transfer.\n- **Minor units** — integer amounts in the smallest customary unit of the denomination (cents for USD-pegged assets).\n- **Offer** — a creator-published subscription product.\n- **Cycle** — one billing period of an offer.\n\n---\n\n## 1. Asset-denominated zap requests (extends kind `9734`)\n\nA stablezap request is a standard NIP-57 zap request with the following additional tags:\n\n```json\n{\n  \"kind\": 9734,\n  \"tags\": [\n    [\"relays\", \"...\"],\n    [\"p\", \"<recipient-pubkey>\"],\n    [\"e\", \"<zapped-event-id>\"],\n    [\"amount\", \"<msats-equivalent>\"],\n    [\"unit\", \"USDT\"],\n    [\"uamount\", \"500\", \"2\"],\n    [\"asset\", \"<asset-id>\", \"taproot\"],\n    [\"settle\", \"asset\", \"btc\"]\n  ],\n  \"content\": \"\"\n}\n```\n\nRules:\n\n- `unit` + `uamount` together define the **denominated amount**. `uamount` MUST be an integer string; the second element is the decimal count. Clients MUST NOT use floats anywhere.\n- `asset` identifies the concrete on-Lightning asset when native settlement is desired. If absent, the request is denomination-only and settlement is expected in BTC at a disclosed rate.\n- `settle` lists acceptable settlement paths. `[\"settle\", \"btc\"]` alone makes this a display-denominated zap (\"$5, paid in sats\"). `[\"settle\", \"asset\"]` alone demands native asset settlement; wallets that cannot comply MUST fail rather than silently settle in BTC.\n- If `amount` (msats) is present alongside `uamount`, it is a **courtesy fallback** for NIP-57-only wallets; the denominated amount is authoritative for stablezap-aware clients.\n\n### 1.1 LNURL / lud16 extension\n\nThe recipient's LNURL-pay endpoint advertises stablezap capability by adding to its response:\n\n```json\n{\n  \"allowsNostr\": true,\n  \"nostrPubkey\": \"...\",\n  \"stablezaps\": {\n    \"units\": [\"USDT\"],\n    \"assets\": [{\"id\": \"<asset-id>\", \"proto\": \"taproot\", \"unit\": \"USDT\"}],\n    \"settlement\": [\"asset\", \"btc\"]\n  }\n}\n```\n\nWhen the payer requests an invoice for a stablezap, the endpoint MUST return either (a) an **asset invoice** matching `asset`/`uamount` exactly, or (b) a BTC invoice whose msat amount reflects the denominated amount at the endpoint's current rate, in which case the endpoint MUST also return `\"rate\": \"<msats-per-minor-unit>\"` so the payer can verify the conversion before paying. Payers SHOULD reject rates deviating more than a client-configured tolerance from an independent reference.\n\n## 2. Stablezap receipts (extends kind `9735`)\n\nThe zap receipt mirrors NIP-57 and adds:\n\n```json\n{\n  \"kind\": 9735,\n  \"tags\": [\n    [\"p\", \"...\"], [\"e\", \"...\"],\n    [\"bolt11\", \"<invoice>\"],\n    [\"description\", \"<zap-request-json>\"],\n    [\"preimage\", \"...\"],\n    [\"unit\", \"USDT\"],\n    [\"uamount\", \"500\", \"2\"],\n    [\"asset\", \"<asset-id>\", \"taproot\"],\n    [\"rate\", \"<msats-per-minor-unit>\"]\n  ]\n}\n```\n\n### 2.1 Verification rules (normative)\n\nA receipt claims a stable amount **only if it survives all of the following**; clients computing totals, leaderboards, or subscription state MUST apply them:\n\n1. The embedded zap request (in `description`) parses, is validly signed, and its `p`/`e`/`a` targets match the receipt's tags.\n2. **Native settlement:** the decoded invoice is an asset invoice whose asset id equals the `asset` tag and whose asset amount equals `uamount`. The self-declared tags are display hints; the invoice is the truth.\n3. **BTC settlement:** the decoded invoice's msat amount equals `uamount × rate` within rounding of one minor unit. Clients SHOULD flag receipts whose `rate` deviates grossly from historical reference rates for the receipt's timestamp.\n4. The receipt is published by the recipient's LNURL provider pubkey as declared in their profile metadata, where verifiable (as in NIP-57).\n5. Deduplication by receipt id; one receipt counts once.\n\nReceipts failing 1–4 MUST be treated as unverified and MUST NOT contribute to any displayed stable-denominated total.\n\n## 3. Subscription offers (addressable kind `37555`, provisional)\n\nA creator publishes an offer:\n\n```json\n{\n  \"kind\": 37555,\n  \"tags\": [\n    [\"d\", \"<offer-id>\"],\n    [\"title\", \"Freischwebende Intelligenz — Supporter\"],\n    [\"unit\", \"USDT\"],\n    [\"uamount\", \"500\", \"2\"],\n    [\"period\", \"2592000\"],\n    [\"grace\", \"259200\"],\n    [\"asset\", \"<asset-id>\", \"taproot\"],\n    [\"settle\", \"asset\", \"btc\"],\n    [\"benefit\", \"Full archive access\"],\n    [\"visibility\", \"public\"]\n  ],\n  \"content\": \"<markdown description>\"\n}\n```\n\n## 4. Subscription state — derived from the ledger, not declared\n\nThere is deliberately **no \"subscription status\" event signed by anyone**. State is computed:\n\n- A **cycle payment** is a verified stablezap receipt (per §2.1) whose embedded request contains an `a` tag referencing the offer (`37555:<pubkey>:<offer-id>`) and a `cycle` tag: `[\"cycle\", \"<unix-start-of-cycle>\"]`.\n- A subscriber is **active** for cycle *N* iff a verified cycle payment exists for *N*, or for *N−1* within the offer's `grace` window.\n- A subscriber **lapses** by simply not paying. Cancellation requires no permission and no event — though a client MAY publish kind `7556` (`[\"a\", <offer-address>]`, empty content) as a courtesy signal to stop renewal reminders.\n- Creators verify entitlement by querying receipts against their own offer address — one relay query, no database. Any third party can audit a creator's subscriber revenue the same way. **The engagement report and the payment ledger are the same document.**\n\n### 4.1 Renewal execution\n\nNon-custodial auto-renewal is delegated to **NIP-47 budgets**: the subscriber grants their NWC-connected wallet a standing permission scoped to `(offer-address, uamount, unit, period)`; the client (or the wallet itself) executes the cycle payment when due. Where no NWC budget exists, clients fall back to **reminder-based renewal** (a local or DM notification with a one-tap pay action). Implementations MUST NOT build custodial pull-payment schemes to work around this; the inconvenience is the cost of self-custody and is accepted by design.\n\n## 5. Privacy modes\n\n- `visibility: public` (default): receipts are ordinary public events. Maximal auditability; the subscriber list is effectively public. Appropriate for patronage.\n- `visibility: private`: cycle payments are wrapped per NIP-59 (gift wrap) to the creator; the creator's entitlement checks work as in §4 over unwrapped receipts, but third-party auditability is lost and totals become claims, not proofs. Clients MUST label privately-settled totals as unaudited.\n\nThe trade-off is stated rather than hidden: this NIP considers public-by-default the honest configuration for creator revenue and private mode the concession to subscriber privacy.\n\n## 6. Backwards compatibility\n\n- NIP-57-only clients see stablezap receipts as ordinary zaps (and, where the `amount` fallback was set, display a sats value). Nothing breaks.\n- NIP-57-only wallets can pay denomination-only requests via the BTC fallback path (§1.1 b).\n- Offers (`37555`) are invisible to legacy clients; entitlement checks are the publisher's concern, so degradation is graceful.\n\n## 7. Security & trust considerations\n\n1. **Issuer risk is not abstracted away.** USDT and comparable assets are centrally issued and freezable. Clients implementing this NIP MUST NOT describe asset-settled balances or streams as censorship-resistant, and SHOULD display an issuer-risk notice at the point where a user first opts into asset settlement. Sats remain the base layer; this NIP standardizes the pragmatic layer above it — the distinction is part of the spec, not marketing.\n2. **Rate honesty.** BTC-settled denominated zaps are the main manipulation surface (inflated `rate` → inflated displayed dollar totals). Hence §2.1(3): the invoice arithmetic, not the tag, is authoritative, and gross rate outliers are flagged.\n3. **Fake-asset invoices.** Wallets MUST verify asset ids against the offer/request before paying; a matching ticker string is not a matching asset.\n4. **Replay/duplication.** Receipt-id dedupe plus cycle-tag uniqueness (one counted payment per subscriber per cycle; extras are tips).\n5. **Regulatory surface.** A subscription is a payment for published content; this NIP defines no custody, no pooling, no exchange function. Implementers adding fiat on/off-ramps or custodial wallets import their own regulatory obligations, outside this spec's scope.\n\n## 8. Reference flows (informative)\n\n**A. One-off stablezap, native settlement:** client builds 9734 with `unit/uamount/asset` → LNURL endpoint returns taproot asset invoice → wallet (asset-capable) pays → provider publishes 9735 with `asset` tag → verifiers apply §2.1(2).\n\n**B. One-off stablezap, BTC settlement:** as above, endpoint returns BTC invoice + `rate` → any NIP-57 wallet pays → 9735 carries `rate` → verifiers apply §2.1(3).\n\n**C. Subscription lifecycle:** creator publishes 37555 → subscriber's first cycle payment (receipt with `a` + `cycle`) → NWC budget authorized → wallet auto-pays each cycle → subscriber stops payment or publishes 7556 → state lapses after `grace`. No party ever asked permission of a platform.\n\n## 9. Open questions for review\n\n1. Kind numbers (`37555`, `7556`) — collision review against deployed kinds required before merge.\n2. BOLT12 offers as the renewal primitive (recurrence field) once wallet support matures — could replace NWC budgets as the preferred path.\n3. Multi-asset offers (same price, several acceptable assets) — repeatable `asset` tags vs. separate offers.\n4. Proration and price changes mid-subscription — current position: a price change is a new offer; old offers are honored until lapse.\n5. Whether `rate` should additionally cite a signed oracle attestation rather than relying on outlier detection alone.\n\n---\n\n*Skeleton v0.1 — circulated for comment. Reference implementation planned in Conciergstr (billing engine) and the shared zap-validation module used across Pareto/Athanor clients.*","sig":"e68520170eeba24a608375ff7e0ed15d93ecba77add390d50424a1f17ab77c68db7502b16b04af3c779615f40e00ea1941b1fd5a9a76b110506da92b58dd2ac7"}