Problem
Nostream currently supports one-time pay-to-relay admission , not recurring subscription billing.
Today, a user pays a Lightning invoice once, gets is_admitted = true permanently, and keeps write access forever. There is no plan lifecycle, no renewal flow, no grace period, and no tiered benefits tied to payment.
What exists but is incomplete:
One-time admission invoices only (feeSchedule: admission)
Permanent admission flag in users.is_admitted
Global retention and rate limits not tied to paid status
Kind 402 notifications only on payment, not for renewal or expiry
No admin view for subscribers, revenue, or dunning ([Admin Console] Phase 6: Payments and Admissions View #636 )
This is enough for a simple paid relay gate, but not for relay operators who want recurring billing with plans, renewals, and tiered access.
This is relay-operator billing (users pay the relay for access). It is not the same as creator subscriptions (NIP-88).
Solution
Add a subscription billing layer on top of the existing Lightning payment flow:
Define subscription plans in settings
Store subscription state in PostgreSQL (user_subscriptions, extended invoices)
Reuse PaymentsService for renewal invoices
Add a SubscriptionWorker for renewals, expiry, and grace handling
Enforce write access, retention, and rate limits based on active plan
Notify subscribers via extended kind 402 events
Expose plan and status info through NIP-11 and HTTP API
Extend the admin console for subscriber and payment operations ([Admin Console] Phase 6: Payments and Admissions View #636 , [Admin Console] Phase 7: Webhooks and Notifications #637 )
Existing admitted users will be migrated to a grandfathered plan so current access is preserved.
Scope
Database and settings
Add subscription_plans and user_subscriptions tables
Extend invoices with plan_id, period_start, period_end, subscription_id
Add payments.subscriptionPlans[] to settings (amount, interval, grace, benefits)
Migration for existing is_admitted users
Subscription core
SubscriptionService for subscribe, renew, cancel, and status transitions
SubscriptionWorker for renewal invoice generation and expiry handling
Subscription lifecycle: active -> renewal_pending -> past_due -> expired (+ cancel at period end)
Enforcement
Replace boolean is_admitted check with active subscription in event-message-handler
Tier-based retention, rate limits, and connection limits per plan
Tier-aware purge in maintenance worker
API and user-facing
POST /subscriptions, GET /subscriptions/status
Extend kind 402 with subscription tags (status, plan_id, period_end, bolt11)
Extend NIP-11 relay info with plans and access requirements
Extend resources/invoices.html for subscription checkout and status
Admin console
NIPs involved
Reuse (already in Nostream):
NIP-01 — base protocol, NOTICE/CLOSED messages
NIP-11 — relay info, plans, payment requirements
NIP-40 — expiration on membership events
NIP-47 — NWC invoice creation
Kind 402 — invoice and subscription status events
Align with drafts:
NIP-11 access_control (#2311 )
NIP-63 membership lists (#2156 ) — optional interop
Out of scope for v1:
NIP-88 (creator zap subscriptions)
NIP-57 (zap renewals)
NIP-61 / Cashu
Publication metering via charge_user()
Key files to extend
payments-service.ts — extend period on invoice confirm, not permanent admit
event-message-handler.ts — subscription write gate
maintenance-worker.ts — renewal/expiry cron
user-repository.ts — subscription CRUD
root-request-handler.ts — NIP-11 plan advertisement
resources/invoices.html — subscription UI
Success criteria
Operator can define multiple plans with different benefits
Subscriber gets a renewal invoice before period ends
Expired subscriptions lose write access
Kind 402 notifies renewal due, past due, and expired states
NIP-11 advertises available plans
Admin can view and manage subscribers
Existing admitted users migrate without losing access
CC: @cameri
Problem
Nostream currently supports one-time pay-to-relay admission, not recurring subscription billing.
Today, a user pays a Lightning invoice once, gets
is_admitted = truepermanently, and keeps write access forever. There is no plan lifecycle, no renewal flow, no grace period, and no tiered benefits tied to payment.What exists but is incomplete:
feeSchedule: admission)users.is_admittedThis is enough for a simple paid relay gate, but not for relay operators who want recurring billing with plans, renewals, and tiered access.
This is relay-operator billing (users pay the relay for access). It is not the same as creator subscriptions (NIP-88).
Solution
Add a subscription billing layer on top of the existing Lightning payment flow:
user_subscriptions, extendedinvoices)PaymentsServicefor renewal invoicesSubscriptionWorkerfor renewals, expiry, and grace handlingExisting admitted users will be migrated to a grandfathered plan so current access is preserved.
Scope
Database and settings
subscription_plansanduser_subscriptionstablesinvoiceswithplan_id,period_start,period_end,subscription_idpayments.subscriptionPlans[]to settings (amount, interval, grace, benefits)is_admittedusersSubscription core
SubscriptionServicefor subscribe, renew, cancel, and status transitionsSubscriptionWorkerfor renewal invoice generation and expiry handlingactive -> renewal_pending -> past_due -> expired(+ cancel at period end)Enforcement
is_admittedcheck with active subscription inevent-message-handlerAPI and user-facing
POST /subscriptions,GET /subscriptions/statusstatus,plan_id,period_end,bolt11)resources/invoices.htmlfor subscription checkout and statusAdmin console
subscriptionPlans[]([Admin Console] Phase 3: Visual Settings Editor #633)NIPs involved
Reuse (already in Nostream):
Align with drafts:
access_control(#2311)Out of scope for v1:
charge_user()Key files to extend
payments-service.ts— extend period on invoice confirm, not permanent admitevent-message-handler.ts— subscription write gatemaintenance-worker.ts— renewal/expiry cronuser-repository.ts— subscription CRUDroot-request-handler.ts— NIP-11 plan advertisementresources/invoices.html— subscription UISuccess criteria
CC: @cameri