mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
chore(relay): relax temporary query allowance
rust-nostr 0.45 newly enables a 120-query-per-minute LocalRelay default. Production sync exhausted it because each SDK-managed NIP-77 NEG-MSG continuation consumes the same per-connection bucket, while the small per-connection quota alone is weak DoS protection because clients can multiply connections. Override the embedded relay allowance by 10x to 1,200 queries per minute while retaining a finite safety backstop. Keep the value internal rather than expanding the operator configuration surface, and document the effective limit and its rationale. Correctness assumes the existing subscription, message, write, event-size, and connection-admission controls remain the primary resource bounds. Per-IP or global rate limiting and proactive outbound historic pacing are deliberately excluded. This override is temporary: review its value after rust-nostr separates or otherwise revises NIP-77 continuation accounting. Validation: nix develop -c cargo check --lib.
This commit is contained in:
@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Temporarily override rust-nostr 0.45's newly introduced LocalRelay query
|
||||
allowance from 120 to 1,200 messages per minute per connection. This retains
|
||||
a finite DoS backstop while avoiding an unusually restrictive default that
|
||||
also charges every SDK-managed NIP-77 `NEG-MSG` continuation. Re-evaluate
|
||||
the 10× allowance after upstream revises that NIP-77 accounting.
|
||||
- Pace query starts after a relay reports its per-connection query-rate budget
|
||||
is exhausted, preventing fixed cooldown recovery from replaying the same
|
||||
fast historic-sync burst indefinitely. If an SDK-managed NIP-77 exchange
|
||||
|
||||
@@ -31,11 +31,15 @@ operator configurable; other dependency hardening is pinned in the builder.
|
||||
These limits prevent individual connections from overwhelming the relay.
|
||||
The relay advertises the standard `max_subscriptions`, `max_limit`,
|
||||
`default_limit`, `max_message_length`, and `max_subid_length` NIP-11 fields.
|
||||
rust-nostr 0.45 also enforces fixed ngit-grasp-selected defaults of 120 queries,
|
||||
rust-nostr 0.45 also enforces fixed ngit-grasp-selected defaults of 1,200 queries,
|
||||
30 authentication events, and 6,000 WebSocket messages per minute; 20 filters
|
||||
per REQ; 1 MiB subscription state; 10 active negentropy sessions and 50,000
|
||||
negentropy items per connection; a 5 MiB WebSocket message; and a 10-second
|
||||
handshake deadline. NIP-11 has no standard fields for most of those controls.
|
||||
The query allowance is a temporary 10× override of rust-nostr's newly added
|
||||
120/minute default because NIP-77 currently charges each SDK-managed `NEG-MSG`
|
||||
continuation separately; it must be reviewed when upstream revises that
|
||||
accounting.
|
||||
|
||||
### Per-IP Connection Monitoring
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ though they advertise the larger accepted `max_limit`.
|
||||
#### Admission and rate limits (condensed)
|
||||
|
||||
Native rate limiting varies wildly and is invisible to clients. Our own
|
||||
embedded relay enforces per-connection per-minute quotas (120 queries, 6,000
|
||||
embedded relay enforces per-connection per-minute quotas (1,200 queries, 6,000
|
||||
WebSocket messages, 60 event writes); nostream ships per-IP connection-attempt
|
||||
and kind-specific event quotas with EWMA decay; khatru and haven offer
|
||||
discrete leaky counters that drain over minutes; nostr-rs-relay, relayer,
|
||||
@@ -190,6 +190,9 @@ itself because a hostile client can multiply connections; per-IP admission and
|
||||
global resource bounds address that threat more directly. rust-nostr also
|
||||
charges SDK-managed NIP-77 `NEG-MSG` continuation frames to this same bucket,
|
||||
so one application-started reconciliation can consume multiple query tokens.
|
||||
ngit-grasp temporarily overrides that default to 1,200 queries/minute while
|
||||
retaining a finite per-connection backstop. Re-evaluate the 10× value after
|
||||
upstream separates or otherwise revises NIP-77 continuation accounting.
|
||||
|
||||
NIP-11 describes hard relay limitations, not rate-limit algorithms. The
|
||||
standard fields relevant here are `max_limit` and
|
||||
|
||||
@@ -13,7 +13,7 @@ production admission policy.
|
||||
| Results per filter | 500 | `NGIT_RELAY_FILTER_LIMIT` | `max_limit`, `default_limit` |
|
||||
| Serialized event size | 192 KiB | `NGIT_RELAY_MAX_EVENT_SIZE_BYTES` | No equivalent field |
|
||||
| Event writes per minute | 60 | Fixed | No standard field |
|
||||
| Queries per minute | 120 | Fixed | No standard field |
|
||||
| Queries per minute | 1,200 | Fixed temporary override | No standard field |
|
||||
| Authentication events per minute | 30 | Fixed | No standard field |
|
||||
| WebSocket messages per minute | 6,000 | Fixed | No standard field |
|
||||
| WebSocket message size | 5 MiB | Fixed | `max_message_length` |
|
||||
@@ -35,6 +35,14 @@ Production history contains a valid NIP-34 patch event of about 149 KiB, so
|
||||
64 KiB is incompatible with ngit-grasp's purpose. The raised limit remains
|
||||
bounded and below the 5 MiB WebSocket message ceiling.
|
||||
|
||||
The 1,200-query allowance temporarily overrides rust-nostr 0.45's newly
|
||||
introduced 120/minute default. A finite per-connection bound remains one useful
|
||||
DoS layer, but the upstream default is unusually restrictive for sync clients
|
||||
and currently counts every SDK-managed NIP-77 `NEG-MSG` continuation as another
|
||||
query. Review this 10× override after upstream changes that accounting; a small
|
||||
per-connection quota is not a substitute for per-IP admission or global
|
||||
resource bounds because clients can multiply connections.
|
||||
|
||||
## Client adaptation
|
||||
|
||||
ngit-grasp refetches NIP-11 per connection session. Its outbound sync ledger
|
||||
|
||||
+10
-1
@@ -43,6 +43,15 @@ use crate::sync::rejected_index::RejectedEventsIndex;
|
||||
/// tighter operation quotas in charge of valid protocol work.
|
||||
const CLIENT_MESSAGES_PER_MINUTE: u32 = 6_000;
|
||||
|
||||
/// Temporary compatibility override for rust-nostr 0.45's newly introduced
|
||||
/// 120-query-per-minute default.
|
||||
///
|
||||
/// A finite per-connection bound remains useful as one DoS layer, but 120 is
|
||||
/// too restrictive for legitimate sync and NIP-77 currently charges every
|
||||
/// SDK-managed `NEG-MSG` continuation against the same allowance. Re-evaluate
|
||||
/// this 10× value after upstream separates or otherwise revises that accounting.
|
||||
const CLIENT_QUERIES_PER_MINUTE: u32 = 1_200;
|
||||
|
||||
/// NIP-34 Write Policy — admission and routing for GRASP-01 events
|
||||
///
|
||||
/// Acts as the top-level admission gate and router. Each incoming event is:
|
||||
@@ -1021,7 +1030,7 @@ pub async fn create_relay(
|
||||
max_reqs: config.relay_max_subscriptions,
|
||||
notes_per_minute: 60,
|
||||
})
|
||||
.queries_per_minute(120)
|
||||
.queries_per_minute(CLIENT_QUERIES_PER_MINUTE)
|
||||
.auth_events_per_minute(30)
|
||||
.messages_per_minute(CLIENT_MESSAGES_PER_MINUTE)
|
||||
.max_websocket_message_size(5 * 1024 * 1024)
|
||||
|
||||
Reference in New Issue
Block a user