draft optional
This NIP defines a relay operating mode where operators can curate content through a three-tier publisher classification system (trusted, blacklisted, unclassified) with rate limiting, IP-based flood protection, and event kind filtering. Configuration and management are performed through Nostr events and a NIP-86 JSON-RPC API.
Public relays face challenges managing spam, abuse, and resource consumption. Traditional approaches (pay-to-relay, invite-only, WoT-based) each have limitations. Curation mode provides relay operators with fine-grained control over who can publish what, while maintaining an open-by-default stance that allows unknown users to participate within limits.
Curation mode introduces:
The relay MUST be configured with a kind 30078 replaceable event before accepting events from non-owner/admin pubkeys. This event uses the d tag value curating-config.
{
"kind": 30078,
"tags": [
["d", "curating-config"],
["daily_limit", "<number>"],
["ip_daily_limit", "<number>"],
["first_ban_hours", "<number>"],
["second_ban_hours", "<number>"],
["kind_category", "<category_id>"],
["kind", "<kind_number>"],
["kind_range", "<start>-<end>"]
],
"content": "{}",
"pubkey": "<owner_or_admin_pubkey>",
"created_at": <unix_timestamp>
}
| Tag | Description | Default |
|---|---|---|
d | MUST be "curating-config" | Required |
daily_limit | Max events per day for unclassified users | 50 |
ip_daily_limit | Max events per day from a single IP | 500 |
first_ban_hours | First offense IP ban duration (hours) | 1 |
second_ban_hours | Subsequent offense IP ban duration (hours) | 168 |
kind_category | Predefined kind category (repeatable) | - |
kind | Individual allowed kind number (repeatable) | - |
kind_range | Allowed kind range as "start-end" (repeatable) | - |
Relays SHOULD support these predefined categories:
| Category ID | Kinds | Description |
|---|---|---|
social | 0, 1, 3, 6, 7, 10002 | Profiles, notes, contacts, reposts, reactions, relay lists |
dm | 4, 14, 1059 | Direct messages (NIP-04, NIP-17, gift wraps) |
longform | 30023, 30024 | Long-form articles and drafts |
media | 1063, 20, 21, 22 | File metadata, picture, video, audio events |
lists | 10000, 10001, 10003, 30000, 30001, 30003 | Mute lists, pins, bookmarks, people lists |
groups_nip29 | 9-12, 9000-9002, 39000-39002 | NIP-29 relay-based groups |
groups_nip72 | 34550, 1111, 4550 | NIP-72 moderated communities |
marketplace_nip15 | 30017-30020, 1021, 1022 | NIP-15 stalls and products |
marketplace_nip99 | 30402, 30403, 30405, 30406, 31555 | NIP-99 classified listings |
order_communication | 16, 17 | Marketplace order messages |
Relays MAY define additional categories.
{
"kind": 30078,
"tags": [
["d", "curating-config"],
["daily_limit", "100"],
["ip_daily_limit", "1000"],
["first_ban_hours", "2"],
["second_ban_hours", "336"],
["kind_category", "social"],
["kind_category", "dm"],
["kind", "1984"],
["kind_range", "30000-39999"]
],
"content": "{}",
"pubkey": "a1b2c3...",
"created_at": 1700000000
}
"blocked: pubkey is blacklisted" noticeWhen an event is received, the relay MUST process it as follows:
- Owners and admins: always accept, bypass all limits - IP-blocked: reject with temporary block notice - Blacklisted: reject with blacklist notice - Trusted: accept, bypass rate limits - Unclassified: continue to rate limit checks
- Check pubkey's daily event count against daily_limit
- Check IP's daily event count against ip_daily_limit
When a pubkey exceeds daily_limit:
first_ban_hourssecond_ban_hoursAll management endpoints require NIP-98 HTTP authentication from an owner or admin pubkey.
| Method | Parameters | Description |
|---|---|---|
trustpubkey | [pubkey_hex, note?] | Add pubkey to trusted list |
untrustpubkey | [pubkey_hex] | Remove pubkey from trusted list |
listtrustedpubkeys | [] | List all trusted pubkeys |
| Method | Parameters | Description |
|---|---|---|
blacklistpubkey | [pubkey_hex, reason?] | Add pubkey to blacklist |
unblacklistpubkey | [pubkey_hex] | Remove pubkey from blacklist |
listblacklistedpubkeys | [] | List all blacklisted pubkeys |
| Method | Parameters | Description |
|---|---|---|
listunclassifiedusers | [limit?] | List unclassified users sorted by event count |
geteventsforpubkey | [pubkey_hex, limit?, offset?] | Get events from a pubkey |
deleteeventsforpubkey | [pubkey_hex] | Delete all events from a blacklisted pubkey |
scanpubkeys | [] | Scan database to populate unclassified users list |
| Method | Parameters | Description |
|---|---|---|
markspam | [event_id_hex, pubkey?, reason?] | Flag event as spam (hides from queries) |
unmarkspam | [event_id_hex] | Remove spam flag |
listspamevents | [] | List spam-flagged events |
deleteevent | [event_id_hex] | Permanently delete an event |
| Method | Parameters | Description |
|---|---|---|
listblockedips | [] | List currently blocked IPs |
unblockip | [ip_address] | Remove IP block |
| Method | Parameters | Description |
|---|---|---|
getcuratingconfig | [] | Get current configuration |
isconfigured | [] | Check if relay is configured |
supportedmethods | [] | List available management methods |
POST /api HTTP/1.1
Host: relay.example.com
Authorization: Nostr <base64_nip98_event>
Content-Type: application/json
{
"method": "trustpubkey",
"params": ["a1b2c3d4...", "Trusted friend"]
}
{
"result": {
"success": true,
"message": "Pubkey added to trusted list"
}
}
| Viewer | Sees Trusted Events | Sees Blacklisted Events | Sees Spam-Flagged Events |
|---|---|---|---|
| Owner/Admin | Yes | Yes | Yes |
| Regular User | Yes | No | No |
Relays implementing this NIP SHOULD advertise it in their NIP-11 relay information document:
{
"supported_nips": [11, 86, "XX"],
"limitation": {
"curation_mode": true,
"daily_limit": 50,
"ip_daily_limit": 500
}
}
Daily counters SHOULD reset at UTC midnight (00:00:00 UTC).
Implementations SHOULD cache trusted/blacklisted status and allowed kinds in memory for performance, refreshing periodically (e.g., hourly).
Suggested key prefixes for persistent storage:
CURATING_ACL_CONFIG - Current configurationCURATING_ACL_TRUSTED_PUBKEY_{pubkey} - Trusted publishersCURATING_ACL_BLACKLISTED_PUBKEY_{pubkey} - Blacklisted publishersCURATING_ACL_EVENT_COUNT_{pubkey}_{date} - Daily event countsCURATING_ACL_IP_EVENT_COUNT_{ip}_{date} - IP daily event countsCURATING_ACL_IP_OFFENSE_{ip} - IP offense trackingCURATING_ACL_BLOCKED_IP_{ip} - Active IP blocksCURATING_ACL_SPAM_EVENT_{event_id} - Spam-flagged eventsX-Forwarded-For or X-Real-IP headers carefully, only trusting them from known reverse proxiesThis NIP is compatible with: