NIP-42 walkthrough: reading gated Nostr content from any client

A concrete code walkthrough of how any NIP-42-aware Nostr client can authenticate against Nostreon's gated relay and read premium content on behalf of its user. Handshake, filter rules, and integration notes.

Anyone with a NIP-42-aware Nostr client and a valid Nostreon subscription can read premium content from any other Nostr client. No Nostreon SDK. No special integration. This post is the concrete walkthrough for developers building or extending Nostr clients that want to support gated subscription content. We’ll look at the handshake, the filter rules, and what a client needs to implement to work end-to-end.

Context

Nostreon runs a gated relay at wss://premium.nostreon.com. Subscribers authenticate with NIP-42 and read premium content published by the creators they’ve paid for. The same content is accessible from any Nostr client that speaks NIP-42, not just the Nostreon web app.

The public relay at wss://relay.nostreon.com carries teasers, NIP-63 announcement events, and NIP-88 tier metadata for discoverability. The gated relay carries the full content and gates reads per subscription state.

The handshake

On connect, the gated relay immediately issues a NIP-42 AUTH challenge. The client signs a kind-22242 event with the matching challenge and relay URL and sends it back.

const ws = new WebSocket("wss://premium.nostreon.com");

ws.on("message", (raw) => {
  const [verb, data] = JSON.parse(raw);
  if (verb === "AUTH") {
    // Sign a kind 22242 event with matching challenge + relay tag
    const auth = signEvent({
      kind: 22242,
      created_at: Math.floor(Date.now() / 1000),
      tags: [
        ["relay", "wss://premium.nostreon.com"],
        ["challenge", data],
      ],
      content: "",
    });
    ws.send(JSON.stringify(["AUTH", auth]));
  }
});

The relay verifies the signature, the created_at window, the relay-tag match, and the challenge-tag match. On success it responds ["OK", event_id, true, ""]. On failure it responds ["OK", event_id, false, "auth-required: <reason>"].

After auth: REQ with per-subscriber filtering

Once authenticated, REQ works normally but the relay filters every event before delivering it. A given event is delivered to the authenticated connection if any of these rules match:

  • The authed pubkey is the event author. Creators always see their own content.
  • The event author is a creator the authed pubkey has an active subscription to. Resolved server-side against the subscriptions database and cross-checked against NIP-63 kind 1163 membership events on the relay itself.
  • The event is a NIP-63 kind 1163 membership or NIP-88 kind 7003 payment receipt where a p tag matches the authed pubkey. This lets any third-party client verify the user’s own subscription state by reading from the relay directly.

Events that don’t match any rule are dropped silently. A non-subscriber who completes the NIP-42 handshake can’t read anything they haven’t paid for. A subscriber to creator A can’t read content published by creator B unless they also subscribe to B.

What your client needs to handle

Minimum viable integration for a client that wants to support reading gated Nostreon content for its users:

  1. Respond to ["AUTH", challenge] with a signed kind-22242 event carrying the relay URL and the echoed challenge.
  2. Accept that REQ results are filtered. Missing events that you might have expected based on a NIP-65 relay list or a note’s relay hint are not a bug; they may be a subscription boundary. Render a subscribe affordance rather than showing the event as broken.
  3. When rendering an addressable event that includes a ["nip63"] tag or an ["a", "37001:<creator>:<tier>"] tier reference, you can check the user’s NIP-63 kind 1163 membership events for that creator to decide whether to render the full content or a paywall. Both the tier events (kind 37001) and the membership events (kind 1163) are public information, available on the relay the user is already connected to.
  4. If you want to discover subscription requirements before connecting, query the relay’s NIP-11 document. The proposed access_control field describes what authentication is required and what kinds are gated.

NIP-11 access_control proposal

Nostreon’s gated relay ships a reference implementation of the proposed access_control field in NIP-11. You can see it live:

curl -H "Accept: application/nostr+json" https://premium.nostreon.com/

The response includes an access_control object describing the authentication requirement and the kinds that are gated, plus an info_url that clients can surface as a “subscribe” link when the current user doesn’t have access.

The proposal is at https://github.com/nostr-protocol/nips/pull/2318 and currently in review. Feedback welcome.

Why this matters

Subscription-gated content on the protocol without a platform lock-in. A reader pays once on nostreon.com; their subscription is recorded as a NIP-63 membership event on the gated relay; any NIP-42-aware client they use later can verify that membership and deliver the content. The creator gets paid via Lightning at settlement time. No custody, no platform rent, no client-specific integration.

The full relay-gate reference implementation is open source: https://github.com/Nostreon/relay-auth


No comments yet.