NetworkManager D-Bus layer + audit fixes + feature batch #2

Merged
Breadway merged 4 commits from feature/nm-rework into main 2026-08-31 18:14:18 +08:00
Owner

Cut from what was on local main (2 unpushed commits) plus a ~3250-line uncommitted working tree, rebased onto origin/main.

Commits

  1. Fix bugs from a full audit — Tailscale recovery, SSID verification, captive portals, config races (was unpushed on main).
  2. Add feature batch — captive-portal detection, schedules, exit-node failover, 802.1x, per-network DNS (was unpushed on main).
  3. Talk to NetworkManager over D-Bus instead of shelling out to nmcli — new zbus dep. Connect / scan / status / watch all speak org.freedesktop.NetworkManager on the system bus. Wi-Fi PSKs and 802.1x passwords travel inside AddAndActivateConnection2 / Update2 payloads — never on a command line. Watch loop reacts to Device.StateChanged / connectivity signals. settings.nmcli_waitconnect_wait (old key still accepted via #[serde(alias)]).
  4. Test the NM layer against an in-process D-Bus faketests/common/fake_nm.rs (1007 lines) stands up a fake NM on a private bus; cli.rs / flow_watch.rs exercise the real nm paths with no live NetworkManager. 139 tests (was 141 pre-rework).

Reconciliation with origin/main

origin/main had one commit local main didn't: 02e9612 "Feed Wi-Fi PSK to nmcli --ask on stdin, never argv". That is fully superseded by commit 3 — there is no nmcli invocation left, and secrets never reach a command line at all. Rebase conflicts in nm.rs / watch.rs / README.md / tests/* were resolved in favour of the D-Bus rework; 02e9612's remaining hunks were rustfmt-only noise.

Not done in this PR

  • Mechanical vs behavioural changes in the D-Bus rework are not split into separate commits — it was one uncommitted blob and the split couldn't be reconstructed reliably. Commit 3's message calls out the behavioural deltas (wait-on-ACTIVATED, D-Bus signal watch, config key rename).
  • No live NetworkManager smoke test yet.

Checks

cargo build clean · cargo test 139 pass · cargo clippy --all-targets -- -D warnings clean.

Manual smoke (needs doing on hermes)

  • breadcrumbs status / connect / switch <profile> against live NM
  • first connect to a password network → PSK stored in NM, local copy cleared, nothing in ps aux | grep -i <psk>
  • 802.1x / enterprise network connect
  • breadcrumbs watch — pull the Wi-Fi, confirm it reacts within a second (D-Bus signal, not poll)
  • captive portal detection
  • a breadcrumbs.toml still using nmcli_wait = loads without error
Cut from what was on local `main` (2 unpushed commits) plus a ~3250-line uncommitted working tree, rebased onto `origin/main`. ## Commits 1. **Fix bugs from a full audit** — Tailscale recovery, SSID verification, captive portals, config races (was unpushed on `main`). 2. **Add feature batch** — captive-portal detection, schedules, exit-node failover, 802.1x, per-network DNS (was unpushed on `main`). 3. **Talk to NetworkManager over D-Bus instead of shelling out to nmcli** — new `zbus` dep. Connect / scan / status / watch all speak `org.freedesktop.NetworkManager` on the system bus. Wi-Fi PSKs and 802.1x passwords travel inside `AddAndActivateConnection2` / `Update2` payloads — never on a command line. Watch loop reacts to `Device.StateChanged` / connectivity signals. `settings.nmcli_wait` → `connect_wait` (old key still accepted via `#[serde(alias)]`). 4. **Test the NM layer against an in-process D-Bus fake** — `tests/common/fake_nm.rs` (1007 lines) stands up a fake NM on a private bus; `cli.rs` / `flow_watch.rs` exercise the real `nm` paths with no live NetworkManager. 139 tests (was 141 pre-rework). ## Reconciliation with `origin/main` `origin/main` had one commit local `main` didn't: `02e9612` "Feed Wi-Fi PSK to nmcli --ask on stdin, never argv". That is **fully superseded** by commit 3 — there is no `nmcli` invocation left, and secrets never reach a command line at all. Rebase conflicts in `nm.rs` / `watch.rs` / `README.md` / `tests/*` were resolved in favour of the D-Bus rework; `02e9612`'s remaining hunks were rustfmt-only noise. ## Not done in this PR - Mechanical vs behavioural changes in the D-Bus rework are **not** split into separate commits — it was one uncommitted blob and the split couldn't be reconstructed reliably. Commit 3's message calls out the behavioural deltas (wait-on-ACTIVATED, D-Bus signal watch, config key rename). - No live NetworkManager smoke test yet. ## Checks `cargo build` clean · `cargo test` 139 pass · `cargo clippy --all-targets -- -D warnings` clean. ## Manual smoke (needs doing on hermes) - `breadcrumbs status` / `connect` / `switch <profile>` against live NM - first connect to a password network → PSK stored in NM, local copy cleared, nothing in `ps aux | grep -i <psk>` - 802.1x / enterprise network connect - `breadcrumbs watch` — pull the Wi-Fi, confirm it reacts within a second (D-Bus signal, not poll) - captive portal detection - a `breadcrumbs.toml` still using `nmcli_wait =` loads without error
Breadway added 4 commits 2026-08-31 15:13:21 +08:00
The watch loop could spin forever on a stopped Tailscale daemon (the
auto-start path was unreachable) while re-notifying every retry, report
"connected" when NM autoconnect won a race onto a different SSID, and
classify captive portals as healthy (200/302 accepted as internet). The
bread-bus subscription thread also read/wrote config and state files
concurrently with the watch loop. Fix all of those plus: EDITOR values
with arguments, scan --to silently ignoring unknown profiles, deleted
core profiles being resurrected, inline-network migration data loss,
login never retried, XDG-unaware install-service, detect persisting a
stale default profile, password length leaking through the mask, a
stdin/stdout pipe deadlock, and several minor UI/robustness issues.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
Implements the planned feature sweep: tri-state connectivity with portal
detection, time-based profile schedules, priority exit-node list with
failover, enterprise (802.1x) network support, per-network DNS, signal-
aware selection, auto-learn markers, suspend/resume recovery, prune
command, scored detection, and richer bread events (network.changed,
tailscale.changed). CLI gains --json output, init --wait, and add --dns/
--eap/--identity/--ca-cert. Adds regression coverage for each feature;
141 tests pass and clippy is clean with -D warnings.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
breadcrumbs now speaks `org.freedesktop.NetworkManager` on the system
bus directly (new `zbus` dependency) — no `nmcli` subprocesses for
connect, scan, status, or the watch loop.

Why:
- Wi-Fi PSKs and 802.1x passwords no longer touch a command line. They
  travel inside `AddAndActivateConnection2` / `Update2` settings
  payloads, so they are never visible to other local users via
  `/proc/<pid>/cmdline`. This fully supersedes the earlier
  "feed the PSK to `nmcli --ask` on stdin" approach.
- The watch loop reacts to real `Device.StateChanged` / connectivity
  signals instead of parsing `nmcli monitor` text.
- Connect waits on the device actually reaching the ACTIVATED state
  rather than trusting `nmcli --wait`.

Config: `settings.nmcli_wait` is renamed to `connect_wait`; the old key
is still accepted via `#[serde(alias)]`. `status.rs` loses its private
`ipv4()` nmcli helper in favour of `nm::ipv4_address`. `util::run_with_stdin`
stays (tailscale still uses it) but no longer carries secrets.
Test the NM layer against an in-process D-Bus fake
All checks were successful
check / check (push) Successful in 1m39s
cc709a4af9
`tests/common/fake_nm.rs` stands up a fake `org.freedesktop.NetworkManager`
on a private bus so `cli.rs` and `flow_watch.rs` exercise the real
`nm` code paths without a live NetworkManager and without shelling out.
Replaces the previous command-capture scaffolding in those two files;
scenario coverage (captive portal, exit-node failover, 802.1x,
per-network DNS, schedule triggers, Tailscale recovery, SSID
verification) is preserved — 139 tests.
Breadway merged commit dcf7eb7162 into main 2026-08-31 18:14:18 +08:00
Breadway deleted branch feature/nm-rework 2026-08-31 18:14:18 +08:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Breadway/breadcrumbs#2
No description provided.