mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 23:18:24 +00:00
Production gitnostr.com repeatedly received CLOSED responses from relay.ngit.dev after reconnect: its 34-filter live set for 924 full repositories, 89 state-only repositories, and 3,748 roots crossed rust-nostr 0.45's newly selected 1 MiB cumulative subscription-state limit. Fourteen representative REQs were accepted and the remainder were refused, silently leaving persistent live coverage incomplete; cooldown recovery only recreated the same impossible set. Raise the explicitly selected per-connection retained subscription-state allowance to 5 MiB. This remains a finite boundary, matches the largest individual WebSocket message already admitted, and leaves roughly four times the observed working-set headroom without promising the theoretical 500 x 96 KiB maximum. Document the exact serving policy and its lack of NIP-11 negotiation. A scenario opens 17 persistent sub-96 KiB REQs carrying 34 filters. It failed unchanged 2.1.1 at live-14 with the production CLOSED reason and passes with the new bound. Correctness assumes this allowance is enforced per connection by rust-nostr; client-side adaptation to unknown third-party cumulative byte limits and multi-connection sharding remain excluded because NIP-11 exposes no such capability. Validation: focused scenario passed; 658 library tests passed; git diff --check passed; nix build .#ngit-grasp passed.
66 lines
3.6 KiB
Markdown
66 lines
3.6 KiB
Markdown
# Embedded relay limits
|
||
|
||
ngit-grasp embeds rust-nostr `LocalRelay` 0.45.0. The application selects every
|
||
effective limit explicitly so future dependency defaults cannot silently alter
|
||
production admission policy.
|
||
|
||
## Effective limits
|
||
|
||
| Limit | ngit-grasp default | Operator configuration | NIP-11 |
|
||
| --- | ---: | --- | --- |
|
||
| Total inbound connections | Unbounded | `NGIT_MAX_CONNECTIONS` | No standard field |
|
||
| Active REQs per connection | 500 | `NGIT_RELAY_MAX_SUBSCRIPTIONS` | `max_subscriptions` |
|
||
| 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 | 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` |
|
||
| Handshake deadline | 10 seconds | Fixed | No standard field |
|
||
| Subscription-ID length | 250 bytes | Fixed | `max_subid_length` |
|
||
| Filters per REQ | 20 | Fixed | No standard field |
|
||
| Subscription state per connection | 5 MiB | Fixed | No standard field |
|
||
| Active negentropy sessions per connection | 10 | Fixed | No standard field |
|
||
| Negentropy items per connection | 50,000 | Fixed | No standard field |
|
||
| Negentropy frame | 60,000 bytes | Fixed upstream | No standard field |
|
||
|
||
The filter setting is applied consistently to rust-nostr's explicit filter
|
||
cap, per-query result cap, and omitted-limit default. Limits are per filter;
|
||
results from multiple filters in one REQ are merged without an aggregate
|
||
truncation.
|
||
|
||
The 192 KiB event default is three times rust-nostr's new 64 KiB default.
|
||
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 5 MiB subscription-state allowance raises rust-nostr's 1 MiB default.
|
||
Repository sync keeps multiple byte-budgeted filters live: production's
|
||
34-filter coverage reached roughly 1.2 MiB and the smaller bound silently left
|
||
part of that coverage closed. The new allowance remains finite per connection,
|
||
matches the maximum admitted WebSocket message, and provides about four times
|
||
the observed working-set headroom. NIP-11 has no field for this cumulative
|
||
byte limit, so clients cannot negotiate it.
|
||
|
||
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
|
||
uses `max_subscriptions`, falling back conservatively when absent. Adaptive
|
||
historic pagination uses `default_limit` with a verification page and learns
|
||
from raw delivered page sizes. `max_limit` describes explicit filter limits;
|
||
historic sync currently omits `limit`, so it does not treat `max_limit` as an
|
||
omitted-filter page-size promise.
|
||
|
||
NIP-11 has no standard `max_filters` field in the current schema. Filter-count
|
||
and serialized-message budgets therefore remain conservative client-side
|
||
constants rather than falsely negotiated capabilities.
|