From f3eb5bf4c2877e084499be6dcce27101d6b47eae Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Tue, 9 Jun 2026 11:32:58 +0000 Subject: [PATCH] bloom: raise max_inbound_fpr antipoison cap from 0.05 to 0.10 The inbound FilterAnnounce FPR cap rejects filters whose false-positive rate (fill^k) exceeds the configured maximum. On the fixed 1 KB / k=5 filter, 0.05 corresponds to fill 0.549 (~1,300 reachable entries), and the busiest nodes' aggregates were beginning to hit that ceiling as the mesh grew. Raise the default to 0.10 (fill 0.631, ~1,630 entries) to restore headroom toward the fixed-filter capacity limit. A saturated or poisoned filter is ~100% FPR and remains rejected, so the antipoison gate is not materially weakened. Updates the config default, the config-reference and bloom-filter design docs, and the changelog. --- CHANGELOG.md | 8 ++++++++ docs/design/fips-bloom-filters.md | 4 ++-- docs/reference/configuration.md | 4 ++-- src/config/node.rs | 13 ++++++++----- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd59372..497ab8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- `node.bloom.max_inbound_fpr` default raised from `0.05` to `0.10`. The + cap rejects inbound `FilterAnnounce` whose FPR (`fill^k`) exceeds it. On + the fixed 1 KB / k=5 filter, `0.05` corresponds to fill 0.549 (~1,300 + reachable entries) and had begun rejecting the busiest nodes' aggregates + as the mesh approached that size. `0.10` (fill 0.631, ~1,630 entries) + restores headroom toward the fixed-filter capacity limit without + materially weakening the antipoison gate: a saturated or poisoned filter + is ~100% FPR and still rejected. - Sidecar example (`examples/sidecar-nostr-relay`): `udp.mtu` is now overridable via the `FIPS_UDP_MTU` environment variable, defaulting to 1472 (preserving prior behavior). Plumbed through `docker-compose.yml` diff --git a/docs/design/fips-bloom-filters.md b/docs/design/fips-bloom-filters.md index 1e71356..bc2e6f1 100644 --- a/docs/design/fips-bloom-filters.md +++ b/docs/design/fips-bloom-filters.md @@ -350,14 +350,14 @@ exposed through the control socket and `fipstop` dashboard. The estimator refuses to produce a value when any contributing filter is above the antipoison FPR cap (`node.bloom.max_inbound_fpr`, -default `0.05`); a partial aggregate would silently underestimate. +default `0.10`); a partial aggregate would silently underestimate. Consumers handle the resulting `None` by displaying an "unknown" state rather than a misleading number. ## Antipoison: Inbound FPR Cap Inbound `FilterAnnounce` payloads are checked against -`node.bloom.max_inbound_fpr` (default `0.05`). Filters whose +`node.bloom.max_inbound_fpr` (default `0.10`). Filters whose estimated false positive rate exceeds the cap are dropped silently (no NACK on the wire) — they would otherwise inflate downstream candidate evaluation cost without contributing useful discrimination. diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index a8fd3ed..bf02a26 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -254,7 +254,7 @@ Controls tree construction and parent selection. | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `node.bloom.update_debounce_ms` | u64 | `500` | Debounce interval for filter update propagation | -| `node.bloom.max_inbound_fpr` | f64 | `0.05` | Antipoison cap: reject inbound `FilterAnnounce` frames whose advertised false-positive rate exceeds this value. Valid range `(0.0, 1.0)`. The default `0.05` corresponds to fill 0.549 at k=5 (≈3,200 entries on the 1 KB filter) | +| `node.bloom.max_inbound_fpr` | f64 | `0.10` | Antipoison cap: reject inbound `FilterAnnounce` frames whose advertised false-positive rate exceeds this value. Valid range `(0.0, 1.0)`. The default `0.10` corresponds to fill 0.631 at k=5 (≈1,630 entries on the 1 KB filter); a saturated/poisoned filter is still ~100% FPR and rejected | Bloom filter size (1 KB), hash count (5), and size classes are protocol constants and not configurable. @@ -899,7 +899,7 @@ node: flap_dampening_secs: 120 # extended hold-down on flap bloom: update_debounce_ms: 500 - max_inbound_fpr: 0.05 # antipoison cap on inbound FilterAnnounce FPR + max_inbound_fpr: 0.10 # antipoison cap on inbound FilterAnnounce FPR session: default_ttl: 64 pending_packets_per_dest: 16 diff --git a/src/config/node.rs b/src/config/node.rs index 0f6421f..6a5cde7 100644 --- a/src/config/node.rs +++ b/src/config/node.rs @@ -625,9 +625,12 @@ pub struct BloomConfig { pub update_debounce_ms: u64, /// Antipoison cap: reject inbound FilterAnnounce whose FPR exceeds /// this value (`node.bloom.max_inbound_fpr`). Valid range `(0.0, 1.0)`. - /// Default `0.05` ≈ fill 0.549 at k=5 ≈ ~3,200 entries on the 1KB - /// filter. Conceptually distinct from future autoscaling hysteresis - /// setpoints — same unit, different knobs. + /// Default `0.10` ≈ fill 0.631 at k=5 ≈ ~1,630 entries on the 1 KB + /// filter (Swamidass–Baldi). Raised from 0.05 so aggregates that are + /// legitimately near their operating ceiling are not rejected before + /// the network reaches the fixed-filter capacity limit; conceptually + /// distinct from future autoscaling hysteresis setpoints — same unit, + /// different knobs. #[serde(default = "BloomConfig::default_max_inbound_fpr")] pub max_inbound_fpr: f64, } @@ -636,7 +639,7 @@ impl Default for BloomConfig { fn default() -> Self { Self { update_debounce_ms: 500, - max_inbound_fpr: 0.05, + max_inbound_fpr: 0.10, } } } @@ -646,7 +649,7 @@ impl BloomConfig { 500 } fn default_max_inbound_fpr() -> f64 { - 0.05 + 0.10 } }