Skip to content

feat(nip42): restrict reads of encrypted kinds to authenticated recipients#702

Merged
cameri merged 10 commits into
cameri:mainfrom
Anshumancanrock:feat/nip42-restricted-reads
Jul 20, 2026
Merged

feat(nip42): restrict reads of encrypted kinds to authenticated recipients#702
cameri merged 10 commits into
cameri:mainfrom
Anshumancanrock:feat/nip42-restricted-reads

Conversation

@Anshumancanrock

Copy link
Copy Markdown
Collaborator

Description

We already support NIP-42 client authentication (the AUTH handshake , but nothing ever used that authentication to gate reads. Any client that is authenticated or not could REQ for kind 4 / 1059 and pull down everyone's encrypted DMs and gift wraps, ciphertext plus who-talks-to-who metadata. COUNT and live broadcasts leaked the same way.

This PR adds opt-in auth-based read filtering for a configurable set of "restricted" kinds. When enabled, events of those kinds are only delivered to clients that have authenticated as the event's author or as a pubkey listed in the event's p tags. It applies consistently across all three read paths:

  • REQ / stored events : restricted events are dropped from the stream unless the client is authorized.
  • Live broadcasts : same guard in WebSocketAdapter.onSendEvent.
  • COUNT : counts can't be filtered per event, so any filter touching a restricted kind must be scoped to the caller's own authenticated pubkeys via authors/#p, otherwise the count is refused.

When an unauthenticated client opens a subscription that exclusively targets restricted kinds (it could never return anything), the relay closes it with auth-required: reason so the client knows to authenticate and retry, instead of silently returning an empty stream. Mixed filters (e.g. kinds: [1, 4]) are still served, the unrestricted events come back and the restricted ones are filtered out per event.

Related Issue

Closes #699

Motivation and Context

A relay that supports NIP-42 but never enforces it on reads offers no real read privacy so a passive observer can harvest every user's encrypted messages and social graph. Enforcing auth on the restricted kinds is what makes NIP-42 useful for private/paid relays and for protecting DM metadata.

How Has This Been Tested?

  • New unit suite test/unit/utils/nip42.spec.ts covering the authorization primitives: author match, p-tag recipient match, stranger rejection, kind ranges, mixed vs. fully-restricted filters, COUNT scoping (authors/#p), and that the auth lookup is not consulted when the feature is disabled or the kind is unrestricted (lazy evaluation).

  • Added cases to subscribe-message-handler.spec.ts (REQ filtering + auth-required CLOSED), count-message-handler.spec.ts (COUNT scoping / refusal), and web-socket-adapter.spec.ts (live broadcast delivered only to the authenticated author/recipient).

  • Full relevant suite: 111 passing. biome lint clean on all changed files.

Screenshots (if appropriate):

N/A

Types of changes

  • Non-functional change (docs, style, minor refactor)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my code changes.
  • I added a changeset, or this is docs-only and I added an empty changeset.
  • All new and existing tests passed.

@changeset-bot

changeset-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1ec6ba4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
nostream Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coveralls

coveralls commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Coverage Status

coverage: 68.684% (+0.3%) from 68.364% — Anshumancanrock:feat/nip42-restricted-reads into cameri:main

@Anshumancanrock
Anshumancanrock force-pushed the feat/nip42-restricted-reads branch from 655f295 to 584c5c2 Compare July 20, 2026 09:36
@Anshumancanrock
Anshumancanrock requested a review from cameri July 20, 2026 09:45
Comment thread src/utils/nip42.ts Outdated
Comment thread src/utils/nip42.ts
Address review feedback: the check is about who the event is directed
to (author or p-tagged recipient), not the author alone. Also clarify
why authenticated clients fall through isSubscriptionAuthRequired.
…ed-reads

# Conflicts:
#	package.json
#	resources/default-settings.yaml
@Anshumancanrock
Anshumancanrock requested a review from cameri July 20, 2026 17:57
@cameri
cameri merged commit e172cce into cameri:main Jul 20, 2026
17 of 18 checks passed
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.

[BUG] NIP-42 auth not enforced on reads (kinds 4, 1059 leak to anyone)

3 participants