Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Living planning document. Aim is to get DAPPS into the hands of node operators w

## Where we are now

The protocol is fully specified (`README.md`'s "On-air protocol" section, including F4 versioning policy). The implementation matches the spec; the on-air format is byte-validated against real BPQ in CI via `m0lte/linbpq` (Testcontainers-managed). Local apps talk to DAPPS via MQTT (durable, idempotent on `dapps-id`) or REST. TTL forwarding works across multi-hop topologies. F1 end-to-end source tracking, F2 multi-part messages, F3 `rev` polling (opportunistic + scheduled) all done. B5 passive-learning routing + B5.1 MeshCore-flavoured DSR alternative both shipped. B6.1 connected-mode probe-and-map with transitive `peers` discovery (probe results now feed B5's learned-route graph too), B6.2 HF-NVIS solicit-and-listen (on-demand + scheduled cadence), B7 single-counter discovery airtime budget with probe strategies, all live. **H3 RHPv2 bearer** for XRouter is done (DAPPS_NODE_BEARER=rhpv2; mainline BPQ RHP still awaiting upstream support). C1 single-file native binaries publish via CI on five platforms; C2 env-var seeding plus `dapps --show-config`; C3 `/Health`, `/Operational`, decision-events ring + structured journal mirror, MQTT heartbeat; C4 install/upgrade docs in the README; C5.1 update-availability banner + C5.2 triggered self-update via privileged `dapps-updater.service`. D1-D4 dashboard MVP. E1-E4 developer guide complete. Phase M (#81-#85) - MCP server at `/mcp` with 26 operator-facing tools.
The protocol is fully specified (`README.md`'s "On-air protocol" section, including F4 versioning policy). The implementation matches the spec; the on-air format is byte-validated against real BPQ in CI via `m0lte/linbpq` (Testcontainers-managed). Local apps talk to DAPPS via MQTT (durable, idempotent on `dapps-id`) or REST. TTL forwarding works across multi-hop topologies. F1 end-to-end source tracking, F2 multi-part messages, F3 `rev` polling (opportunistic + scheduled) all done. B5 passive-learning routing + B5.1 MeshCore-flavoured DSR alternative both shipped. B6.1 connected-mode probe-and-map with transitive `peers` discovery (probe results now feed B5's learned-route graph too), B6.2 HF-NVIS solicit-and-listen (on-demand + scheduled cadence), B7 single-counter discovery airtime budget with probe strategies, all live. **H3 RHPv2 bearer** for XRouter is done (DAPPS_NODE_BEARER=rhpv2; mainline BPQ RHP still awaiting upstream support). C1 single-file native binaries publish via CI on five platforms; C2 env-var seeding plus `dapps --show-config`; C3 `/Health`, `/Operational`, decision-events ring + structured journal mirror, MQTT heartbeat; C4 install/upgrade docs in the README; C5.1 update-availability banner + C5.2 triggered self-update via privileged `dapps-updater.service`. D1-D4 dashboard MVP. E1-E4 developer guide complete. Phase M (#81-#85) - MCP server at `/mcp` with 26 operator-facing tools. **Master TX kill-switch** - operator stop button + dev-time centralised URL signal, both enforced at the bearer chokepoints (AGW frame / RHP open / UDP send), removed before 1.0 (see "Open tasks" entry).

What's left is in the "Suggested ordering" near the bottom plus the open H bearer-integration phase. The biggest remaining items are external-blocked (H1/H2 MeshCore hardware; H3 mainline-BPQ RHP awaiting upstream support - the XRouter half of H3 has shipped), parked (C5.3 scheduled auto-update - banner + one-click + `trigger_update` MCP cover today's operator population; F5 signing - design parked, see the F5 section for the recommended shape when it gets pulled forward), or not-yet-prioritised (Phase G second-language reference impl, scratchpad phone messenger app).

Expand Down Expand Up @@ -39,6 +39,8 @@ What's left is in the "Suggested ordering" near the bottom plus the open H beare

- **Inject `TimeProvider` so time-dependent code is testable** *(done)*. `TimeProvider.System` is registered as a singleton; `Database`, `OperationalMetrics`, `OutboundForwarderService`, `TtlSweeperService`, `UpdateChecker`, `ProbeSchedulerService`, `DiscoveryService`, `NodeProber`, `UdpDatagramListener`, and `UpdaterOrchestrator` all take it via DI. Every `DateTime.UtcNow` in those classes routes through it; every `Task.Delay(span, ct)` uses the `Task.Delay(span, TimeProvider, ct)` overload; `TtlSweeperService`'s `PeriodicTimer` uses the new `(TimeSpan, TimeProvider)` constructor. The `OutboundForwarderService` test-only ctor is gone - replaced with init-only `TickInterval` / `StartupDelay` setters. Tests in `TimeProviderInjectionTests` exercise the wiring end-to-end with `FakeTimeProvider.Advance(...)` (e.g. saving a row, advancing the clock by 90 s, asserting `DeleteExpired` soft-deletes it). 430/430.

- **Master TX kill-switch - operator toggle + dev-time centralised** *(done)*. Two safety mechanisms composed through one seam. (1) An operator master TX-stop button rendered on every dashboard page: `SystemOptions.TxEnabled` (default true) persisted in the systemoptions table, flipped via POST `/TxControl/{stop,resume}`. When closed, a sticky red banner shows on every Razor page with the block reason and a Resume button. (2) A centralised URL kill-switch the project author controls during the development phase: `TxKillSwitchPoller : BackgroundService, ITxKillSwitchSignal` polls a hardcoded HTTPS URL (`https://compute.oarc.uk/storage/public/folders/4803/dapps-devtime-killswitch.json`) every 5 minutes and stops TX if the JSON says so. Wire shape `{"txAllowed":bool,"reason":string?,"appliesTo":["*"|"M0LTE-*"|"M0LTE-2"]}` with glob-ish callsign targeting so one URL can pause one site without affecting the fleet. **Deliberately not configurable**: URL, cadence, 30-minute staleness window, and fail-open behaviour are all `const`s on `TxKillSwitchPoller`; operators cannot disable the polling, repoint it, or relax the timings. The whole subsystem is removed (not made configurable per-fleet) before 1.0; `docs/dev-time-tx-kill-switch.md` documents this transparently and is linked from `getting-started.md`. **Bearer-level enforcement** is the architectural commitment - `IDappsTxGate` is consulted at the four chokepoints where DAPPS bytes meet on-air emissions: `AgwFrameTransport.WriteFrameAsync` (gates RF Kinds C/v/c/D/M/V/K, lets admin / disconnect through so the BPQ session stays usable), `Rhpv2OutboundTransport.ConnectAsync` (before active OpenAsync = AX.25 SABM) and its `writeOutgoing` lambda (I-frames), `Rhpv2InboundService` writeOutgoing (replies on accepted-inbound sessions), and `UdpDatagramBackhaul.SendAsync`. Discovery, probes, polls, forwards and floods all flow through these chokepoints, so no application-layer service had to be touched. **TLS hardening**: the named `tx-kill-switch` `HttpClient` pins a fresh `SocketsHttpHandler` with default `SslOptions` (system trust store + chain / hostname / expiry validation) so any future tweak to the default handler can't silently weaken kill-switch validation; `PollOnce` adds a runtime guard that refuses to fetch over plaintext if the URL ever drifts off `https://`. Belt-and-braces over .NET's already-validating default. **Audit trail**: both signals write `tx-control` rows symmetrically - `bearer="ui"` for the local toggle (every press, including no-op presses, so operator intent is logged), `bearer="remote"` for remote-driven flips (first-poll-says-BLOCK and ALLOW↔BLOCK transitions; steady-state polls and failed polls don't audit). Files: `dapps.client/Tx/IDappsTxGate.cs`, `dapps.core/Services/SystemOptionsBackedTxGate.cs`, `dapps.core/Services/TxKillSwitchPoller.cs`, `dapps.core/Controllers/TxControlController.cs`. Tests: 27 across `TxGateTests`, `SystemOptionsBackedTxGateTests`, `TxControlControllerTests`, `TxKillSwitchPollerTests`, `TxKillSwitchPollerAuditTests`. PRs #126 (bearer seam), #127 (UI toggle + banner), #128 (poller, originally configurable), #129 (URL hardcoded + transparent docs), #131 (5-min cadence + TLS hardening + softer wording), #132 (symmetric audit). 644/644.

**Goal:** DAPPS core talks in terms of forwarding durable DAPPS units to neighbours, not in terms of opening a stream and speaking one specific session protocol.

The current factoring around `IDappsOutboundTransport` + `Stream` was the right move to get AGW under an interface, but it is still too transport-shaped if DAPPS is going to support a datagram bearer such as MeshCore without contorting the rest of the system. This is high priority while the code is still in progress.
Expand Down
Loading