fix(nip05): add configurable limits for relays per user and identity listing
Some checks are pending
Docker Compose Actions Workflow / test (push) Waiting to run

Adds max_relays_per_user (default: 10) to prevent users from attaching
excessive relay URLs that inflate .well-known/nostr.json responses.
Enforced in both claimUsername and updateRelays.

Reduces the no-name listing limit from hardcoded 1000 to configurable
max_identities_listing (default: 100) for the /.well-known/nostr.json
endpoint when no ?name= parameter is provided.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-04-01 13:31:33 -04:00
parent 17727d3e31
commit 99d83efd56
2 changed files with 20 additions and 7 deletions

View file

@ -27,7 +27,9 @@ const DEFAULT_CONFIG: Required<Nip05Config> = {
min_username_length: 1,
reserved_usernames: ['admin', 'root', 'system', 'support', 'help', 'info', 'contact', 'abuse', 'postmaster', 'webmaster', 'hostmaster', 'noreply', 'no-reply', 'null', 'undefined', 'api', 'www', 'mail', 'ftp', 'ssh', 'test', 'demo'],
include_relays: true,
default_relays: []
default_relays: [],
max_relays_per_user: 10,
max_identities_listing: 100
}
/**
@ -99,11 +101,15 @@ function validateUsername(username: string, config: Required<Nip05Config>): User
/**
* Validate relay URLs
*/
function validateRelays(relays: string[]): UsernameValidation {
function validateRelays(relays: string[], maxRelays?: number): UsernameValidation {
if (!Array.isArray(relays)) {
return { valid: false, error: 'Relays must be an array' }
}
if (maxRelays && relays.length > maxRelays) {
return { valid: false, error: `Too many relays (max ${maxRelays})` }
}
for (const relay of relays) {
if (typeof relay !== 'string') {
return { valid: false, error: 'Each relay must be a string' }
@ -166,7 +172,7 @@ export class Nip05Manager {
// Validate relays if provided
const relays = request.relays || this.config.default_relays
if (relays.length > 0) {
const relayValidation = validateRelays(relays)
const relayValidation = validateRelays(relays, this.config.max_relays_per_user)
if (!relayValidation.valid) {
throw new Error(relayValidation.error)
}
@ -241,7 +247,7 @@ export class Nip05Manager {
request: UpdateRelaysRequest
): Promise<Nip05Identity> {
// Validate relays
const validation = validateRelays(request.relays)
const validation = validateRelays(request.relays, this.config.max_relays_per_user)
if (!validation.valid) {
throw new Error(validation.error)
}
@ -361,10 +367,11 @@ export class Nip05Manager {
}
}
} else {
// Return all active identities (with reasonable limit)
// Return all active identities (with configurable limit)
const limit = this.config.max_identities_listing
const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND is_active = 1 LIMIT 1000`,
[applicationId]
`SELECT * FROM identities WHERE application_id = ? AND is_active = 1 LIMIT ?`,
[applicationId, limit]
)
for (const row of rows) {

View file

@ -119,6 +119,12 @@ export interface Nip05Config {
/** Default relays to suggest for new users */
default_relays?: string[]
/** Maximum number of relays per user (default: 10) */
max_relays_per_user?: number
/** Maximum number of identities returned when no name query param is provided (default: 100) */
max_identities_listing?: number
}
/**