Skip to content

Forwarder architecture fixes: trust model, data integrity, and decomposition#217

Merged
iwismer merged 38 commits into
mainfrom
iwismer/forwarder-architecture-fixes
Jul 4, 2026
Merged

Forwarder architecture fixes: trust model, data integrity, and decomposition#217
iwismer merged 38 commits into
mainfrom
iwismer/forwarder-architecture-fixes

Conversation

@iwismer

@iwismer iwismer commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the issues found in the forwarder architecture review, in five phases (36 commits, each task TDD'd and reviewed):

Trust model (critical)

  • Static receivers are pinned: server allow-list snapshots can no longer revoke operator-configured receivers
  • Reader-control verbs are gated on the negotiated CAP_READER_CONTROL capability and a new control.allow_reader_control flag (default true); capability-gate denials are written inline to prevent a control-loop deadlock under request floods
  • P2P remote-config writes may no longer modify the protected [auth], [p2p], or [control] sections (privilege-escalation fix); the receiver UI renders those sections read-only
  • Control-plane verbs are attributed to the peer EndpointId in tracing and UI logs

Data integrity

  • Per-section config writes now run canonical-loader validation before persisting (no more boot-failing configs)
  • Journal append failures retry with capped backoff instead of dropping the consumed read
  • Stream identity (epoch/next_seq) is restored from the server registry after journal loss, with slack for catalog-push lag; new self-scoped GET /forwarder/catalog server endpoint

Contention & decomposition

  • P2P replay reads moved to per-subscriber read-only SQLite connections (WAL), off the shared write mutex
  • status_http.rs decomposed: config_service.rs and status_store.rs extracted; reader/UPS tasks no longer depend on the HTTP module

P2P mediums/lows

  • Catalog serves live reader connectivity + bumped generation at connect time
  • Frame decode owned by rt-p2p-protocol (single source of framing invariants); server HTTP clients split out of allowlist.rs
  • /api/v1/status serves cached server reachability (no inline WAN call); change-gated server_status_changed UI event
  • Device-token bootstrap retries in-process with backoff; allow-list re-polls promptly after a failed pushed apply
  • Heartbeat pongs validated against the outstanding nonce window; fanout drops counted and surfaced; unbounded legacy pending_events replay path removed (tests ported to the cursor API)

One behavioral note for the E2E suite: the remote-config gating scenario now asserts that remote [control] writes are rejected and flips the gate via a local TOML edit instead.

Test Plan

  • cargo fmt --all -- --check and cargo clippy --workspace --all-targets clean
  • cargo test --workspace --lib and cargo test --workspace -- --test-threads=4 green
  • E2E loopback stack uv run scripts/e2e/run_stack.py: 65/65 checks GREEN (three lanes)
  • bash scripts/validate-packaging.sh: 79/79
  • Receiver UI: npm test 164 passed, npm run check/lint clean
  • Manual smoke on hardware: static receiver survives an allow-list poll cycle; remote [p2p] write rejected end-to-end; journal-loss reboot restores next_seq from the server catalog

Written and posted by AI on behalf of Isaac.

@iwismer iwismer force-pushed the iwismer/forwarder-architecture-fixes branch from 0c63ac8 to cf052a5 Compare July 4, 2026 01:33
iwismer added 29 commits July 3, 2026 21:54
Thread the authenticated peer NodeId from connection admission into the
control loop and emit a tracing event (peer, verb, request_id, target,
outcome) at every reader-control and remote-config dispatch arm,
including capability-gate denials.

Extend RemoteConfigHandler::set_config to take the peer so config
writes are attributed in the UI log: ForwarderRemoteConfigHandler now
holds the shared UiLogger and logs both accepted and rejected remote
config writes with the requesting receiver's node id.
@iwismer iwismer force-pushed the iwismer/forwarder-architecture-fixes branch from cf052a5 to d5b8aa3 Compare July 4, 2026 01:59
@iwismer iwismer marked this pull request as ready for review July 4, 2026 01:59
@iwismer iwismer merged commit 3ba3eee into main Jul 4, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant