Skip to content

exclusive/paywall/premium content#2156

Open
fiatjaf wants to merge 7 commits into
masterfrom
paywalls
Open

exclusive/paywall/premium content#2156
fiatjaf wants to merge 7 commits into
masterfrom
paywalls

Conversation

@fiatjaf

@fiatjaf fiatjaf commented Dec 11, 2025

Copy link
Copy Markdown
Member

@vitorpamplona

Copy link
Copy Markdown
Collaborator

I was thinking on a crazy idea using replaceable events to reduce the amount of leaked "premium" events:

1 - Creator posts a full blog entry into the creator relay.
2 - Creator posts a created_at + 1 update with just the public part of that event and sends it to all other relays. This event also includes a "see more" tag pointing to the previous event ID and the creator relay.

Now, the other relays will not accept the old event that has the full text. They will default to the public one.

Clients will need to implement a "Load More" button that hits the creator relay and force-displays the past version of the replaceable. This will also mean that Clients using regular Nostr DBs won't save the old version in their local dbs, which will default to the public event as it should, limiting additional leaks.

@staab

staab commented Dec 11, 2025

Copy link
Copy Markdown
Member

@arkin0x @pablof7z

@arkin0x

arkin0x commented Dec 11, 2025

Copy link
Copy Markdown
Contributor

Hey guys. You may be interested in how Fanfares is doing paywalled events:

First of all, we haven't introduced any new event kinds for our paywall system. Any nostr event can be paywalled.

If the paywalled content is text, we encrypt the text and store it in the encrypted tag on the event. The content of the event serves as the preview or teaser for the event. When decrypted, the UI replaces the preview with the decrypted content (and allows the user to toggle between them as desired.)

If the paywalled content is media, we encrypt the files and store them on blossom. The event's imeta tags point to the blossom URLs and contains some extra information depending on the file type, like duration for audio files.

So whether the paywalled content is text or binary, it's just a normal event with an encrypted tag, a price tag, and zap tags defining who must get paid to unlock it.

When a user wants to publish a paywalled event, they receive the unique encryption key from our server. This is how we are able to supply the encryption key to buyers of the content.

Our server verifies zap receipts and supplies the decryption key to verified buyers of the content. The decryption happens locally in the client.

This paywall NIP is interesting for ACL for a piece of content, but I don't think we would use it for Fanfares because:

  1. This paywall NIP is interactive, meaning the content author must publish an event in response to the payment or access request. Fanfares' system is non-interactive, so the payment can be made and access given without the content author's involvement.
  2. This paywall NIP still leaves the considerably large headache of mediating access unsolved, which makes me question who would bother to build ecommerce around it.

I think that this NIP would be more appropriately named "Access Control Lists" because it has nothing to do with payment and could be used in many contexts outside of paywalls.

For what it's worth, Fanfares has managed to implement actual non-interactive zap-based paywalls without having to write any new NIPs and that's something worth considering. Our work may inform augmentations of how imeta is used, and seems to be the first to use the encrypted tag, but beyond that nothing else is needed from the protocol to make a really excellent paywall experience.

@vitorpamplona

Copy link
Copy Markdown
Collaborator

For what it's worth, Fanfares has managed to implement actual non-interactive zap-based paywalls without having to write any new NIPs and that's something worth considering.

This is not a good thing. I think you still need to create the NIP to specify how the encrypted tag works and what clients need to do about the price/zap. What encryption does it use? Where is the encryption key for files stored? Is it similar to what we do on NIP-17? What happens if there are multiple files? Once the text is decrypted what should clients do with the text? Merge with the content or replace the content? Or is it kind dependent?

Anyway, there are lots of things here for a NIP so that anyone can code not only the client but also the service.

@arkin0x

arkin0x commented Dec 11, 2025

Copy link
Copy Markdown
Contributor

Those are all great questions. I'd be happy to write a NIP. Nobody had been interested up to this point. Anyway, Fanfares is early on and I didn't see any point in defining a NIP when our POC wasn't yet complete. But it is probably 90% at this point, so I could start speccing it all out.

@vitorpamplona

Copy link
Copy Markdown
Collaborator

For now, do you just share one secret with all of the members or do you create one encrypted tag for each member?

@staab

staab commented Dec 11, 2025

Copy link
Copy Markdown
Member

Yeah, write a NIP. The whole point of nostr is network effect, and a NIP makes network effect over a feature set possible.

@arkin0x

arkin0x commented Dec 11, 2025

Copy link
Copy Markdown
Contributor

For now, do you just share one secret with all of the members or do you create one encrypted tag for each member?

Each event gets its own encryption key. Each person who buys the event receives the key in order to decrypt the event. Each buyer gets the same key given the same event.

The encrypted tag on the purchasable event is formatted like this:

["encrypted", "<encryption type>", "<optional ciphertext to replace content>"]

The key will decrypt the ciphertext.

If the event does not have ciphertext and instead has imeta tags pointing to encrypted files, the key will decrypt those files after fetching them. (The imeta has an encrypted property indicating that the fetched file is encrypted)

I hope that answers your question?

Yeah, write a NIP. The whole point of nostr is network effect, and a NIP makes network effect over a feature set possible.

It's time to write a NIP 😅

@vitorpamplona

Copy link
Copy Markdown
Collaborator

On NIP-17, we force just one encryption type to keep things easy for implementers (no random encryption schemes we have never seen). It would be worth doing the same here.

Each person who buys the event receives the key in order to decrypt the event.

How do they receive it? Do clients need to code allow them to paste/type the key into the app?

@arkin0x

arkin0x commented Dec 11, 2025

Copy link
Copy Markdown
Contributor

How do they receive it? Do clients need to code allow them to paste/type the key into the app?

The buyer makes a NIP-98 request to our server's /request-key route, and if their payment is validated, they receive the key in response.

There is a very deep discussion to be had regarding how other clients should interact with Fanfares instances. I don't know if this is the right place to dig into it. But it's a discussion I want to have. We are eager to assist other clients in integrating paywall unlocks. Just not sure how it should be done quite yet.

UPDATE: I've started writing the NIPs 📝

@pablof7z

Copy link
Copy Markdown
Member

When I implemented something like this in Highlighter I used NIP 29; the creator would publish a preview event and the full content event. The paid event with a roles tag of the NIP-29 membership roles (eg "supporter", "ultrahigh")

The NIP-29 relay enforces who gets what depending on their membership role in the relay.

This allows any client that is not aware of this paid content stuff to be able to, ie, read a paid-content 30023 long form.

@dtonon

dtonon commented Dec 12, 2025

Copy link
Copy Markdown
Contributor

@fiatjaf damn you are fast!
All good, I also like that payments are handled off-protocol.
It would be interesting to make this NIP compatible with a “pay-per-view on individual events" NIP, so that the relay could potentially implement both subscription mode and access to specific content.

@arkin0x

This paywall NIP is interactive, meaning the content author must publish an event in response to the payment or access request. Fanfares' system is non-interactive, so the payment can be made and access given without the content author's involvement

The buyer makes a NIP-98 request to our server's /request-key route, and if their payment is validated, they receive the key in response.

I don't see any major differences; in this PR, the list is updated automatically by the payment processor that interacts with the relay, so the “content author” does not need to take any direct action.

@vitorpamplona

I was thinking on a crazy idea using replaceable events to reduce the amount of leaked "premium" events:

Cool, although I'm not sure if it's a good idea to use a “side effect” as a specification.

@vitorpamplona

Copy link
Copy Markdown
Collaborator

Cool, although I'm not sure if it's a good idea to use a “side effect” as a specification.

There are no specs. Everything in nostr is a side effect. :)

@arkin0x

arkin0x commented Dec 12, 2025

Copy link
Copy Markdown
Contributor

I don't see any major differences; in this PR, the list is updated automatically by the payment processor that interacts with the relay, so the “content author” does not need to take any direct action.

As I read it, the content author must publish the kind 1163 to grant access to a new user @dtonon

@dtonon

dtonon commented Dec 12, 2025

Copy link
Copy Markdown
Contributor

@arkin0x the content author means the payment gateway acting on behalf of the author (e.g. using a FROST bunker with a limited kind scope).

@arkin0x

arkin0x commented Dec 12, 2025

Copy link
Copy Markdown
Contributor

@arkin0x the content author means the payment gateway acting on behalf of the author (e.g. using a FROST bunker with a limited kind scope).

It could mean that, depending on an individual's implementation of what is written. The spec does not include FROST.

@dtonon

dtonon commented Dec 12, 2025

Copy link
Copy Markdown
Contributor

@arkin0x FROST is not mandatory, it's just a good option. The point is that the payment gateway needs to be able to sign events on behalf of the author, or have a private communication channel with the relay that creates the events itself using the author's key.
In any case it's an automatic procedure.

@nsnjx

nsnjx commented Dec 13, 2025

Copy link
Copy Markdown

Very interesting! I'm working on a project called chuchu (still in alpha stage), which is a paid content subscription platform built on NIP 29

This NIP seems very promising. However, I have a concern that aligns with what @arkin0x mentioned earlier: if the Membership Event must be published by the content-creator, it becomes difficult to achieve a scenario where subscribers can automatically unlock content upon payment without requiring the creator's active participation.

If this NIP can address this concern, I would be interested in switching to this NIP for implementation.

@fiatjaf

fiatjaf commented Dec 14, 2025

Copy link
Copy Markdown
Member Author

You can't have your cake and eat it too. Either the thing is centralized in one provider or not, and life has tradeoffs. The limited bunker solution as @dtonon suggests is pretty good to me. Even Amber users can supply it to the payment provider as long as the payment provider has some logic to try again after some time if the bunker is unresponsive or something like that.


Well, actually you can have your cake as long as you only eat one part of it: if you are using a single relay for the premium content and that relay is also responsible for collecting the payments (or counting the zaps you receive) then that relay may start serving your content to a user that has just paid even though you haven't yet updated your list. Then later when you come back online you contact the relay and get the new data you can publish your updated list (so maybe other relays that you may also be using can get the update).

@dtonon

dtonon commented Dec 14, 2025

Copy link
Copy Markdown
Contributor

if you are using a single relay for the premium content and that relay is also responsible for collecting the payments

Maybe this overlap is not even necessary.
If the relay(s) have a list of pubkeys with permission to write membership events, the payment gateway can be independent with real time updates. Or, as said, the relay can trust the encrypted payment channel and sign itself the updates with its own key (different from the author's one).

@fiatjaf

fiatjaf commented Feb 2, 2026

Copy link
Copy Markdown
Member Author

The force-push is just because of the rebase because GitHub is stupid.

The last commit introduces the possibility of having your cake and eating it too: if instead of only referencing pubkeys we can also reference pre-existing lists of pubkeys we can allow creators to easily:

  • give premium access to all the people they follow automatically
  • delegate giving access to a list managed by someone else (a payment provider, for example)
  • other stuff

This only complicates premium-capable relays a little bit, almost nothing, and is optional.

Let me know if this (or anything else about this NIP) is dumb.

@barrydeen

barrydeen commented Feb 20, 2026

Copy link
Copy Markdown

i think this should be called gated content. both paywalled and premium don't really describe it imo. All we are describing is the conditions upon which the gate is opened or closed for someone to receive the event

@fiatjaf

fiatjaf commented Feb 20, 2026

Copy link
Copy Markdown
Member Author

What about

  • LOCKED CONTENT
  • EXCLUSIVE CONTENT
  • PRIVILEGED INFORMATION
  • REDACTED

@barrydeen

Copy link
Copy Markdown

+1 exclusive content

@fiatjaf

fiatjaf commented Mar 2, 2026

Copy link
Copy Markdown
Member Author

This is implemented in fiatjaf/pyramid@00627d0 fiatjaf/pyramid@ce8ecf0. Seems to work correctly.

@fiatjaf fiatjaf changed the title paywall/premium content exclusive/paywall/premium content Mar 4, 2026
@fiatjaf fiatjaf marked this pull request as ready for review March 4, 2026 04:00
@TheIcarusWings

Copy link
Copy Markdown

Thanks @fiatjaf for pointing me here from PR #866. This is very relevant to what I'm building. I'm building a subscription-based creator platform on Nostr and this spec addresses many of the same problems I've been working through.

I like the approach. Membership as Nostr events (kind 1163) is more portable than storing it in a database, and the "a" tag delegation is a great pattern for letting a payment provider add readers without the creator being online. Keeping it payment-agnostic is the right call for a protocol spec.

Some thoughts after reading the spec and the discussion here:

1. Expiry. In my implementation subscriptions have an expires_at and access ends naturally without anyone taking action. With kind 1163 per reader, revoking access requires actively publishing a NIP-09 deletion. For recurring subscriptions this means someone (the creator or a payment provider) needs to delete the membership event when the subscription lapses. Would a valid_until tag on the kind 1163 event make sense? Relays could auto-expire access without requiring a deletion event.

2. Tiers. I see this is mentioned in "Future additions." From building this out, tiers are needed from day one. Every creator I've talked to wants at least two price points with different content for each. @pablof7z's NIP-29 roles approach and the ["tier", "a"] idea both seem workable. I'd push for including this in the initial spec rather than deferring it, since it shapes how kind 1163 events are structured.

3. Teaser pattern. @vitorpamplona's suggestion of publishing a created_at + 1 update with just the public portion to regular relays is exactly what I've implemented (teaser with title + summary on the public relay, full content on the gated relay). It works well for discovery. Worth including as a recommended pattern in the spec.

I think this spec and PR #866 are complementary. NIP-63 handles relay-side access control (who can read what), and #866 handles the subscription flow (what tiers exist, who subscribed). The kind 1163 event becomes the bridge between payment and access.

I've open-sourced my subscription implementation at Nostreon/nip88-subscriptions if it's useful as a reference. Happy to help test or contribute.

@fiatjaf

fiatjaf commented Apr 11, 2026

Copy link
Copy Markdown
Member Author
  1. There is no expiration that happens magically in the world, in any case you're still relying on someone to act on some specific time to start denying access to content. Payment providers or gated relays that also manage these lists are in a very suitable position to do this regardless of adding another tag to the events, so I don't think we should. Still, if you really want it you can already use the generic "expiration" tag (it's in some NIP I forgot) and it's already supported by default in a bunch of relays and libraries.

  2. The tiers are already mostly specified, you just give them arbitrary id/names and add them to the kind 1163 lists and to the "nip63" tags on exclusive events. But I didn't fully write them because we needed some more collective experience with the design before settling on the specifics (also if the NIP text is too big no one reads).

  3. I don't know what is Vitor's suggestion, but the teaser pattern is also more-or-less specified there, right? You can publish stuff to public relays, but that has the downside of the paying users also seeing the teasers (together with the exclusive content). I guess that is ok too. Better than requiring relays to implement the negative version of gated content.

About the recurring subscriptions NIP, I don't know, it may be complimentary to this. But honestly I think payments are too hard to standardize. As you said, that NIP only focuses on zaps (if I'm not mistaken). If you try to account for all the other forms of payments in the world we'll go crazy.

So my proposed solution is much simpler: a payment provider has a webpage where people can pay and they get added to the list.

The specifics of how much has to be paid and by what means and what happens when you pay: these are all specific to the content creator and the payment providers he chooses, they can be arbitrary. There's almost nothing to be gained here from standardizing this part.

Nostreon could be one such payment provider, host pages explaining the plans and terms for each creator, and issue the NIP-63 lists -- then people can pick other relays to host their gated content (and they can use other providers besides Nostreon at the same time) and followers can get the content on any client (regardless of whether they are aware of this NIP or not), while creators can also publish from any client that implements adding the "nip63" tag.

My NIP-63 implementation is here: https://github.com/fiatjaf/pyramid/tree/master/paywall

Of course Nostreon can also host a relay for the exclusive content, and offer a client that can publish the exclusive content, but I think these things should be totally independent of each other, interoperable and replaceable.

(Of course I don't know at all what are you planning for Nostreon, but I hope you do it in this modular architecture.)

@TheIcarusWings

Copy link
Copy Markdown

Thanks for the detailed response and for sharing the Pyramid implementation.

The modular architecture you're describing makes a lot of sense. Right now Nostreon is more tightly coupled (our own relays, our own client), but NIP-63 gives a clear path toward that modularity. The first step for me would be issuing kind 1163 membership events when someone subscribes, so even if gated content lives on Nostreon's relay today, the membership data is portable and other relays could use it.

On expiry: I agree someone always has to act, but NIP-40 expiration on kind 1163 events gives a useful safety net. If a payment provider has downtime or a bug and fails to delete a lapsed membership, without expiration that user keeps access indefinitely. With an expiration tag set to the subscription end date, the relay stops serving content automatically and the payment provider just needs to publish a new kind 1163 when they renew. Failure mode becomes "access stops" instead of "access leaks." I'll use NIP-40 for this rather than inventing something new.

On tiers and teasers, understood. I'll work with the existing design.

Going to start integrating NIP-63 into Nostreon. Will share what I learn from the implementation.

@TheIcarusWings

Copy link
Copy Markdown

Quick update on the NIP-63 integration I mentioned, plus some related work that came out of it.

NIP-63 (kind 1163 membership events)
When a subscriber pays, the platform publishes a kind 1163 event with a ["p", <subscriber-pubkey>] tag and a NIP-40 ["expiration", <unix-ts>] tag set to the subscription end date. The relay-auth service checks both the database and the kind 1163 events on the gated relay before granting access. strfry doesn't implement NIP-63 natively, so we bridge it. If we later switch to a relay that does (Pyramid, etc.), the membership events are already in place. Premium articles and live events also carry ["-"] and ["nip63"] tags so any NIP-63-aware client can recognize them.

NIP-88 event publishing
Since you pointed me from PR #866 to here, I went back and implemented the full NIP-88 event flow as well: kind 37001 (tier definition) signed by the creator, kind 7001 (subscribe) signed by the subscriber at checkout, kind 7002 (unsubscribe) on cancel, kind 7003 (payment receipt) signed by the platform on settlement. All four are now published to public relays for any subscription on the platform. Signing works for both NIP-07 extensions and clients that hold the subscriber's nsec in memory.

A small thing that came out of this
With the membership and receipt events on relays, subscription state lives on Nostr and payments become a backend concern. That made me realize a third-party client (Primal, Damus, Amethyst) could offer subscriptions natively if there were an HTTP API for the payment side. So I shipped one: POST /api/v1/subscribe with NIP-98 auth lets a client create a Lightning invoice on behalf of a user, with an optional hosted checkout page for clients that don't want to render their own QR. The webhook still publishes the same NIP-88/NIP-63 events on settlement, so the relay-side experience is identical regardless of which client originated the subscription.

Loosely related
While I was working on the relay-side enforcement, I also drafted a small NIP-11 extension (originally posted as #2311) so clients can discover up front whether a relay has access requirements, rather than discovering them via a failed AUTH. The original framing of that proposal was a relay-to-backend callback API and staab rightly pushed back on that as an implementation detail. The revised version is just a client-facing access_control field in the NIP-11 document with a short list of mechanisms (["nip42", "nip63", "payment", ...]). I haven't posted the revision yet because I wasn't sure if it was worth opening a new PR vs reviving #2311. Happy to share the draft if it sounds useful.

Reference implementations & docs

One question
Does the kind 7003 receipt shape feel right to you? Production currently emits these tags:

["p", <subscriber-pubkey>]
["P", <tier-creator-pubkey>]
["a", "37001:<creator>:<tier-id>"]
["amount", <cents>, "USD", "monthly"|"yearly"]
["valid_until", <unix-ts>]
["invoice_id", <btcpay-invoice-id>]

Signed by the platform key acting as payment verifier. The valid_until is what makes this valuable as a verification primitive: any client can check subscription validity with a single timestamp comparison instead of scanning zap history. If you'd rather see different tags, a different signer, or a different shape entirely, now's a good time for me to change it.

@mostafa-khaldi

Copy link
Copy Markdown

You may want to check this complementary NIP for defining payment gateways, prices and more, please give it a look
#2315

@zhub9006

zhub9006 commented Jun 6, 2026

Copy link
Copy Markdown

Thanks for this rich discussion on paywall/premium content at the protocol level — it's one of the most substantive open conversations on this topic in the media technology ecosystem. For those designing paywall mechanics here, some academic context may be useful:

Subscription Fatigue: Research by Bindra et al. (2023, "The Subscription Economy and Consumer Fatigue") and Chu et al. (2022, Harvard Business Review) documents that consumers now average 12+ active subscriptions and exhibit increasing churn as perceived marginal value per subscription declines. This is especially relevant for decentralized media — if every creator runs their own paywall, users face a fragmented subscription landscape far worse than the "one subscription for everything" model of traditional publishers. Protocol designers should consider mechanisms that reduce per-creator friction (e.g., bundling, universal credit systems, or aggregated passes) to mitigate this fatigue.

Metered Paywall Effectiveness: The landmark study by Chiou & Tucker (2017, "Paywalls and the Demand for Online News") at MIT found that metered paywalls (allowing N free articles before gating) significantly outperform hard paywalls in converting readers to subscribers, because they preserve discovery and habituation. A 2023 follow-up by Athey et al. ("Consumer Heterogeneity and Paywall Design") confirmed that the optimal meter count varies by audience segment — lightweight readers need fewer free articles to convert, while heavy readers need more. This directly informs the "preview vs. full content" debate in this thread: a protocol-level meter mechanism (e.g., time-limited access, partial content reveals, or credit-based sampling) could give implementers flexibility to tune the "free sample" threshold.

Freemium-to-Paid Conversion: Böckermann & Haucap (2024, "Freemium Models in Digital Markets") show that freemium conversion rates typically hover at 2–5%, but can reach 10–15% when the free tier delivers genuine recurring value (not just a one-time taste). The key driver is what they call "escalating utility" — features that become more valuable as usage accumulates (e.g., saved collections, personalized feeds, social features). For Nostr paywalls, this suggests that gating purely content-based features may yield lower conversion than gating accumulation-based features (e.g., bookmarking, curated feeds, creator-specific dashboards).

Practical Implications for this NIP: Combining these findings, a well-designed paywall protocol should:

  1. Support metered/graduated access (not just binary locked/unlocked) to reduce subscription fatigue and improve conversion.
  2. Enable bundling or aggregated payment mechanisms so users don't face per-creator subscription overload.
  3. Distinguish between content gates (hard paywall) and experience gates (freemium utility), allowing creators to choose the model that matches their audience.

The discussion here about interactive vs. non-interactive, encrypted tags, and access control is exactly where these design decisions get made at the protocol layer. Hope this research context is useful as the NIP evolves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.