diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/reference/relay-limits.md | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/docs/reference/relay-limits.md b/docs/reference/relay-limits.md new file mode 100644 index 0000000..bb15e20 --- /dev/null +++ b/docs/reference/relay-limits.md | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | # nostr-relay-builder Limits | ||
| 2 | |||
| 3 | This document describes the rate limiting, throttling, and query limits in `nostr-relay-builder` version 0.44. These are the limits that apply to ngit-grasp and any relay built with this crate. | ||
| 4 | |||
| 5 | **Note:** Other relay implementations (strfry, nostream, etc.) have different limits. This document focuses on `nostr-relay-builder` specifically. | ||
| 6 | |||
| 7 | ## Hard Limits (Cannot Be Changed) | ||
| 8 | |||
| 9 | These limits are enforced and cannot be overridden by configuration: | ||
| 10 | |||
| 11 | ### WebSocket Message Limits (from tungstenite) | ||
| 12 | |||
| 13 | | Limit | Default Value | Source | | ||
| 14 | |-------|--------------|--------| | ||
| 15 | | `max_message_size` | **64 MB** (67,108,864 bytes) | tungstenite default | | ||
| 16 | | `max_frame_size` | **16 MB** (16,777,216 bytes) | tungstenite default | | ||
| 17 | |||
| 18 | nostr-relay-builder does **not** override these tungstenite defaults. | ||
| 19 | |||
| 20 | **Practical impact:** A single REQ message or EVENT with extremely large content could hit these limits. A filter with ~1,000,000 32-byte event IDs (~32MB in JSON) would fit, but 2 million would not. | ||
| 21 | |||
| 22 | ### Negentropy Frame Limit | ||
| 23 | |||
| 24 | | Limit | Value | Source | | ||
| 25 | |-------|-------|--------| | ||
| 26 | | `frame_size_limit` | **60,000 bytes** (60KB) | Hardcoded in inner.rs | | ||
| 27 | |||
| 28 | ```rust | ||
| 29 | let mut negentropy = Negentropy::owned(storage, 60_000)?; | ||
| 30 | ``` | ||
| 31 | |||
| 32 | If reconciliation needs more data, negentropy splits across multiple NEG-MSG round-trips automatically. | ||
| 33 | |||
| 34 | ### No Hard Limits On | ||
| 35 | |||
| 36 | nostr-relay-builder does **NOT** enforce hard limits on: | ||
| 37 | |||
| 38 | | Item | Hard Limit? | Notes | | ||
| 39 | |------|-------------|-------| | ||
| 40 | | Tag values per filter (`#e`, `#p`, etc.) | ❌ None | Only limited by message size | | ||
| 41 | | Filters per REQ array | ❌ None | Only limited by message size | | ||
| 42 | | Filter JSON size | ❌ None | Only limited by WebSocket message | | ||
| 43 | | Authors per filter | ❌ None | Only limited by message size | | ||
| 44 | | Kinds per filter | ❌ None | Only limited by message size | | ||
| 45 | | IDs per filter | ❌ None | Only limited by message size | | ||
| 46 | |||
| 47 | ## Configurable Limits (Server-Side) | ||
| 48 | |||
| 49 | These limits have defaults but can be configured via `RelayBuilder`: | ||
| 50 | |||
| 51 | ### Query/Response Limits | ||
| 52 | |||
| 53 | | Setting | Default | Builder Method | | ||
| 54 | |---------|---------|----------------| | ||
| 55 | | `default_filter_limit` | **500** | `.default_filter_limit(n)` | | ||
| 56 | | `max_filter_limit` | `None` (no cap) | `.max_filter_limit(n)` | | ||
| 57 | |||
| 58 | **Behavior:** | ||
| 59 | 1. If filter has no `limit` field → server applies `default_filter_limit` (500) | ||
| 60 | 2. If filter has `limit > max_filter_limit` → clamped to `max_filter_limit` | ||
| 61 | 3. If filter has specific `ids` → uses `ids.len()` as limit | ||
| 62 | |||
| 63 | ### Rate Limiting | ||
| 64 | |||
| 65 | | Setting | Default | Description | | ||
| 66 | |---------|---------|-------------| | ||
| 67 | | `max_reqs` | **500** | Max active subscriptions per session | | ||
| 68 | | `notes_per_minute` | **60** | Token bucket rate for EVENT writes | | ||
| 69 | |||
| 70 | Rate limiting uses a token bucket: tokens regenerate proportionally over time, each EVENT consumes 1 token. | ||
| 71 | |||
| 72 | ### Connection/Session Limits | ||
| 73 | |||
| 74 | | Setting | Default | Description | | ||
| 75 | |---------|---------|-------------| | ||
| 76 | | `max_connections` | `None` | Max concurrent WebSocket connections | | ||
| 77 | | `max_subid_length` | **250** | Max characters in subscription ID | | ||
| 78 | |||
| 79 | ## REQ vs Negentropy Limits | ||
| 80 | |||
| 81 | | Aspect | REQ (NIP-01) | Negentropy (NIP-77) | | ||
| 82 | |--------|--------------|---------------------| | ||
| 83 | | Events returned | Limited (default: 500) | **Unlimited** (all IDs returned) | | ||
| 84 | | Filter limit applies? | ✅ Yes | ❌ No | | ||
| 85 | | Returns full events? | ✅ Yes | ❌ No (only EventId + Timestamp) | | ||
| 86 | | Message size limit | 64MB (WebSocket) | 60KB per frame | | ||
| 87 | |||
| 88 | **Why negentropy returns all:** It only returns ~40 bytes per event (ID + timestamp) for set reconciliation. Full events are fetched separately after identifying what's missing. | ||
| 89 | |||
| 90 | ## Quick Reference | ||
| 91 | |||
| 92 | | Limit | Value | Type | | ||
| 93 | |-------|-------|------| | ||
| 94 | | **WebSocket message** | 64 MB | Hard (tungstenite) | | ||
| 95 | | **WebSocket frame** | 16 MB | Hard (tungstenite) | | ||
| 96 | | **Negentropy frame** | 60 KB | Hard (hardcoded) | | ||
| 97 | | Tags per filter | **None** | Soft (message size only) | | ||
| 98 | | Filters per REQ | **None** | Soft (message size only) | | ||
| 99 | | Events per REQ | 500 | Configurable default | | ||
| 100 | | Max subscriptions | 500 | Configurable default | | ||
| 101 | | Write rate | 60/min | Configurable default | | ||
| 102 | |||
| 103 | ## ngit-grasp Configuration | ||
| 104 | |||
| 105 | ngit-grasp uses defaults (no custom limits configured): | ||
| 106 | |||
| 107 | ```rust | ||
| 108 | let builder = RelayBuilder::default() | ||
| 109 | .database(database.clone()) | ||
| 110 | .write_policy(write_policy.clone()); | ||
| 111 | ``` | ||
| 112 | |||
| 113 | Additionally, ngit-grasp's memory database limits to **100,000 events** (LMDB has no such limit). | ||
| 114 | |||
| 115 | ## Related | ||
| 116 | |||
| 117 | - [NIP-01: Basic Protocol](https://github.com/nostr-protocol/nips/blob/master/01.md) | ||
| 118 | - [NIP-77: Negentropy Sync](https://github.com/nostr-protocol/nips/blob/master/77.md) | ||
| 119 | - [nostr-relay-builder docs](https://docs.rs/nostr-relay-builder) | ||
| 120 | - [tungstenite WebSocket limits](https://docs.rs/tungstenite/latest/tungstenite/protocol/struct.WebSocketConfig.html) | ||