Feat: addition of nip-05 verification (issue #261)#463
Conversation
|
@phoenix-server @cameri This pr is ready for review and checks |
There was a problem hiding this comment.
Pull request overview
Adds NIP-05 verification support to nostream to reduce spam by optionally requiring authors to have a verifiable NIP-05 identity, with persistence and periodic re-checking.
Changes:
- Introduces NIP-05 parsing/extraction/verification utilities plus a DB-backed
nip05_verificationsrepository and migration. - Integrates NIP-05 enforcement/metadata-triggered verification into
EventMessageHandlerand adds periodic re-verification toMaintenanceWorker. - Wires the new repository through factories, and documents/configures new
nip05.*settings (with unit tests added/updated).
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/utils/nip05.spec.ts | Adds unit coverage for NIP-05 parsing/extraction/domain filtering utilities. |
| test/unit/handlers/event-message-handler.spec.ts | Adds unit coverage for handler enforcement + metadata-triggered verification behavior. |
| test/unit/factories/websocket-adapter-factory.spec.ts | Updates factory tests for the new repository dependency. |
| test/unit/factories/message-handler-factory.spec.ts | Updates factory tests for the new repository dependency. |
| test/unit/app/maintenance-worker.spec.ts | Adds unit coverage for periodic NIP-05 re-verification behavior. |
| src/utils/nip05.ts | Implements NIP-05 identifier parsing, metadata extraction, domain allow/deny logic, and HTTP verification. |
| src/repositories/nip05-verification-repository.ts | Adds repository for storing and querying NIP-05 verification state. |
| src/handlers/event-message-handler.ts | Enforces NIP-05 (mode-dependent) and triggers async verification on kind-0 metadata events. |
| src/factories/worker-factory.ts | Instantiates and injects the NIP-05 verification repository into websocket adapter wiring. |
| src/factories/websocket-adapter-factory.ts | Threads NIP-05 repository into message handler construction. |
| src/factories/message-handler-factory.ts | Adds NIP-05 repository dependency to EventMessageHandler creation. |
| src/factories/maintenance-worker-factory.ts | Instantiates and injects NIP-05 repository into MaintenanceWorker. |
| src/app/maintenance-worker.ts | Adds periodic batch re-verification of stale NIP-05 records. |
| src/@types/settings.ts | Introduces Nip05Settings and nip05 config field in Settings. |
| src/@types/repositories.ts | Adds INip05VerificationRepository interface. |
| src/@types/nip05.ts | Adds app and DB-layer types for NIP-05 verification records. |
| resources/default-settings.yaml | Adds default nip05 configuration section. |
| migrations/20260409_120000_create_nip05_verifications_table.js | Creates nip05_verifications table + indexes. |
| CONFIGURATION.md | Documents new nip05.* settings. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public run(): void { | ||
| this.interval = setInterval(() => this.onSchedule(), UPDATE_INVOICE_INTERVAL) | ||
| } | ||
|
|
||
| private async onSchedule(): Promise<void> { | ||
| const currentSettings = this.settings() | ||
|
|
||
| await this.processNip05Reverifications(currentSettings) | ||
|
|
| verifyExpiration: number | ||
| verifyUpdateFrequency: number | ||
| maxConsecutiveFailures: number |
| if (!verification || !verification.isVerified) { | ||
| return 'blocked: NIP-05 verification required' | ||
| } | ||
|
|
||
| const expirationMs = nip05Settings.verifyExpiration ?? 604800000 | ||
| if (verification.lastVerifiedAt) { | ||
| const elapsed = Date.now() - verification.lastVerifiedAt.getTime() | ||
| if (elapsed > expirationMs) { | ||
| return 'blocked: NIP-05 verification expired' | ||
| } | ||
| } | ||
|
|
| const expirationMs = nip05Settings.verifyExpiration ?? 604800000 | ||
| if (verification.lastVerifiedAt) { | ||
| const elapsed = Date.now() - verification.lastVerifiedAt.getTime() | ||
| if (elapsed > expirationMs) { | ||
| return 'blocked: NIP-05 verification expired' | ||
| } |
| verifyNip05Identifier(nip05Identifier, event.pubkey) | ||
| .then((verified) => { | ||
| const now = new Date() | ||
| const verification: Nip05Verification = { | ||
| pubkey: event.pubkey, | ||
| nip05: nip05Identifier, | ||
| domain: parsed.domain, | ||
| isVerified: verified, | ||
| lastVerifiedAt: verified ? now : null, | ||
| lastCheckedAt: now, | ||
| failureCount: verified ? 0 : 1, | ||
| createdAt: now, | ||
| updatedAt: now, | ||
| } | ||
| return this.nip05VerificationRepository.upsert(verification) | ||
| }) |
| const response = await axios.get(url, { | ||
| timeout: VERIFICATION_TIMEOUT_MS, | ||
| headers: { 'Accept': 'application/json' }, | ||
| validateStatus: (status) => status === 200, | ||
| }) |
There was a problem hiding this comment.
@archief2910 good point here, let's set max redirects to 1 (not zero), and limit body and content length. This verification feature can be used to perform DoS attacks or consume the relay's bandwidth for malicious purposes.
| } catch (error: unknown) { | ||
| const message = error instanceof Error ? error.message : String(error) | ||
| debug('verification request failed for %s: %s', nip05, message) | ||
| return false |
| const updated: Nip05Verification = { | ||
| ...verification, | ||
| isVerified: verified, | ||
| lastVerifiedAt: verified ? now : verification.lastVerifiedAt, | ||
| lastCheckedAt: now, | ||
| failureCount: verified ? 0 : verification.failureCount + 1, | ||
| updatedAt: now, | ||
| } |
|
@copilot resolve the merge conflicts in this pull request |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
hey @cameri , i have resolved the conflicts you can check once or should i address the copilot comments too . |
| validateStatus: (status) => status === 200, | ||
| }) | ||
|
|
||
| const { data } = response |
There was a problem hiding this comment.
let's validate the response using zod
There was a problem hiding this comment.
but the joi to zod validation issue is not yet merged . so im using it with joi . will it work.
There was a problem hiding this comment.
Sure, but can you create an issue to migrate this to zod too?
There was a problem hiding this comment.
ok then i will update the pr accr ot new commits and migrate it to zod.
There was a problem hiding this comment.
hey @cameri , resolved the zod migration you can check once .
|
hey @cameri , made an issue for migration and resolved the review comments . can you check once . |
…archief2910/nostream into feature/261-NIP-05-verification
Description
This PR implements NIP-05 verification as a spam reduction mechanism for nostream, as described in the nostr-rs-relay reference configuration.
Changes included:
NIP-05 Verification Core:
Nip05VerificationandDBNip05Verificationtypes for application and database layers.Nip05Settingsinterface with three operational modes:enabled(require NIP-05 for publishing),passive(validate without blocking), anddisabled(no-op).parseNip05Identifier,extractNip05FromEvent,verifyNip05Identifier, andisDomainAllowedutility functions conforming to the NIP-05 protocol (https://<domain>/.well-known/nostr.json?name=<local>).Nip05VerificationRepositoryfollowing the existing repository pattern with RamdaapplySpecDB-to-app mapping.nip05_verificationstable with indexes ondomain,is_verified, andlast_checked_at.Event Handler Integration:
checkNip05VerificationintoEventMessageHandlerpipeline — blocks unverified authors when mode isenabled, always allows kind-0 (SET_METADATA) events through so users can set their NIP-05 identifier.processNip05Metadataas fire-and-forget async verification triggered on successful kind-0 event persistence. Deletes verification records when a user removes their NIP-05 from metadata.Background Re-verification:
processNip05ReverificationstoMaintenanceWorker— periodically re-checks stale verifications respectingverifyUpdateFrequencyandmaxConsecutiveFailuressettings with jittered delays between requests.Configuration:
nip05section todefault-settings.yamlwith defaults matching nostr-rs-relay: 1 week expiration, 24-hour update frequency, 20 max consecutive failures.CONFIGURATION.mdwith documentation for all six settings.Factory Wiring:
Nip05VerificationRepositorythroughworkerFactory→webSocketAdapterFactory→messageHandlerFactory→EventMessageHandlerandmaintenanceWorkerFactory→MaintenanceWorkeras a required dependency.Cleanup:
import from '../constants/base'lines inevent-message-handler.ts.sinonChaiimport from NIP-05 utility test file.Related Issue
Closes #261
Motivation and Context
Spam is a persistent problem for public Nostr relays. NIP-05 verification provides a DNS-based identity layer that ties pubkeys to domain names, allowing relay operators to require that event authors have a verifiable internet identity. This is the same approach implemented by nostr-rs-relay and requested in issue #261. The three-mode system (
enabled/passive/disabled) gives operators full control over enforcement level without requiring code changes.How Has This Been Tested?
parseNip05Identifier: 12 tests — valid identifiers, subdomains, case normalization, edge cases (null, empty, missing @, no TLD).extractNip05FromEvent: 6 tests — valid kind-0, wrong kind, missing field, bad JSON, empty string, non-string value.isDomainAllowed: 8 tests — whitelist, blacklist, empty lists, case-insensitivity, blacklist-over-whitelist precedence.checkNip05Verification: 12 tests — all three modes, relay pubkey bypass, kind-0 passthrough, missing/expired/unverified records, domain whitelist/blacklist filtering.processNip05Metadata: 10 tests — disabled/passive modes, non-kind-0, delete on missing NIP-05, unparseable identifier, blocked domain, successful/failed verification with upsert assertions, error handling, passive mode verification.processNip05Reverifications: 9 tests — disabled mode, no pending records, success/failure state updates, failure count increment, error resilience across batch, custom config values, undefined fallback defaults, passive mode operation.message-handler-factory.spec.ts,websocket-adapter-factory.spec.ts) for the new required repository parameter.--noEmit.Screenshots (if appropriate):
N/A
Types of changes
Checklist: