Files
NODEDC_MISSION_CORE/docs/audits/2026-09-07-k1-connection-core-r15.md
T

10 KiB

K1 connection-path audit and R15

Observed failure sequence

The owner reported four failed enrollment results between 19:08 and 19:11 MSK on 2026-09-07. The board is running the matched R14 pair: Node 0.8.11 and K1 0.1.11. The K1 service entered active state at 19:06:33 MSK with zero restarts. This is not an old-package or service-restart explanation.

Journal time (MSK) Recorded failure boundary Device Wi-Fi write
19:08:36 BleakDBusError, ensure_device_for_gatt → find_device_by_address → active_scan:477 → assert_reply Not reached
19:08:51 NetworkProvisioningConflict, exact capture admission at facade.connect:11127 Not reached
19:09:51 Same nested discovery boundary as 19:08:36 Not reached
19:10:41 Same revoked-capture admission as 19:08:51 Not reached

The first and third failures occur before BleakClient.__aenter__, service validation, baseline read and the journalled Wi-Fi write. The second and fourth occur even earlier. A wrong Wi-Fi password cannot cause these four failures; this does not prove the supplied credentials correct, because K1 never received them in these calls. No credential or raw exception payload is copied here.

R14 held a BleakScanner open while its nested resolver started a second scanner. Bleak 3.0.2 uses one BlueZ manager/D-Bus client per loop. BlueZ permits one discovery session per client and adapter, not one per Python scanner. See the BlueZ Adapter API. The retained path was absent, so these calls took that nested branch.

The Wi-Fi function then revoked the exact selected capture in finally simply because no GATT baseline had completed. Discovery failure was thus incorrectly treated as a failed GATT exchange. The UI classified only connect-failed as a Bluetooth failure, leaving the form usable after resolution-failed. The next explicit Apply met the already-revoked capture and produced the misleading expired-search message. This is explicit invalidation, not a form timeout or a Wi-Fi refusal.

The old journal formatter omitted structured extra fields and native D-Bus codes. The exact native code of the historical calls was not retained. A test using the installed Bleak manager and a synthetic D-Bus wire enforcing BlueZ's documented single-session rule reproduces R14's exact call chain with org.bluez.Error.InProgress. This is reproduction evidence, not a claim that the missing historical code was recovered. The bounded Bluetooth service journal contains no additional entries for this interval. Protected stores were not bypassed to obtain more diagnostics.

Path ownership review

Path Owner and final authority Result of review
Search / select in enrollment UI selects one row from runtime + discovery + mode revision; selecting a row performs no physical connection Retained; no automatic Apply or scan added
UI → Core Fleet → Node Go broker One operation ID, durable delivery journal, deadline, exact node/runtime binding; lost response observes the same ID Retained; delivery completion does not mean device connected
Node bridge → plugin facade One bridge lock; plugin journal, lifecycle/dispatch gates and OS BLE arbiter Retained; no second writer or recovery loop introduced
Fresh / retained / durable BLE identity Exact address/native path, owner epoch and scan/session lineage; these identities have distinct admission purposes Retained; age alone does not revoke a form selection
Native path resolution → Connect Previously separate holder plus nested resolver Replaced by one selected_device_discovery context; exact observations come from its own scanner; held through Connect and released before GATT I/O
Station provisioning / status read / AP activation Different reviewed payloads and policies, same native connection lifecycle All three use that single discovery context; one Connect, no automatic write repeat
Failure / cancellation cleanup Transport lease, native callbacks and command journal have separate ownership Removed duplicate revocation in exception and finally paths; only an attempted Connect/GATT exchange without baseline revokes a capture
Verify in enrollment / Verify in device detail Facade policy, ledger lineage, exact persisted target, route, DeviceInfo and physical reconciliation Removed the adapter-level divergence: both use the same verification_parameters; another selected device cannot inherit the saved target
Host Wi-Fi network list Linux host network manager, read-only listing Separate from BLE discovery; not a second K1 scanner
Wi-Fi response → control readiness Same parent intent and owned DeviceInfo bootstrap; network-applied and control-ready are distinct Retained; missing control confirmation never authorizes resending Wi-Fi
START / STOP / preview Named acquisition, physical command journal, active session and persistent preview recording Existing R13/R14 behavior retained; this change issues no physical test commands

The simplification removes overlapping native discovery, duplicate failure cleanup, divergent Node verification selection and duplicated password markup. It does not combine different physical modes into a guessed fallback or erase their side-effect/identity fences.

Operator presentation and diagnostics

  • Both direct and onboard enrollment consume K1WifiPasswordField, extracted from the existing direct composition using Design Guideline TextField, IconButton and eye icons. Visibility resets when the value is cleared. Passwords remain absent from command journals and are cleared after submission.
  • Search busy, unavailable Bluetooth, missing selected K1, and other discovery failures have distinct shared messages. They state that Wi-Fi was not sent only with side_effect_status=none. Unknown/after-write outcomes keep the existing read-only reconciliation path.
  • Legacy resolution errors are recognized as Bluetooth failures so an old result cannot leave the same invalid form open for another Apply.
  • Node failure logs now print a finite allowlist of stage, native BlueZ error and write-attempt/confirmation booleans alongside the existing causal stack. Exception messages, SSID, password, frame data and locals are excluded.

Verification and delivery

The regression first failed on unmodified R14 with the same nested stack, then passed after replacing the resolver. The test uses the real pinned Bleak BlueZ manager/scanner and dbus-fast with a synthetic wire; no adapter is accessed. Tests cover cached/missing paths, timeout, busy reply, cancellation, connection failure and callback/session release for all three BLE operation kinds. Other tests cover exact address/adapter, owner and capture changes, write boundary, single submission, shared Verify admission, and password visibility.

The dev dependency explicitly includes the same pinned dbus-fast version so this contract is also tested on macOS. Linux runtime dependencies are unchanged. Detailed final check counts and artifact provenance are recorded after the release checks. R15 is Node 0.8.12 plus K1 0.1.12 and uses the existing encrypted onboard application credential. Installation and physical UI acceptance are separate from synthetic checks; neither is claimed from a successful build.

Source acceptance

  • Backend connection/arbiter/journal suite: 189 passed.
  • Installer and package lifecycle tests: 14 passed.
  • Complete Control Station unit suite: 808 passed, including direct K1 recovery, frontend boundaries and shared password visibility.
  • Node UI boundary test: 1 passed.
  • Node Go enrollment tests passed.
  • Control Station production build and TypeScript check passed (8.00 s).
  • Ruff and Git whitespace checks passed.

The board was online and published idle acquisition before staging. The local Docker daemon was not running; no Docker startup or parallel heavy builds were used. The original canonical Core process remained on port 8000.

Built and staged R15

Source commit: 762d77ef95ccd763b7e3a0d62e04464a4f9289c1. Node production build/typecheck passed. Both packages were inspected: no archive links, unsafe paths or private application-key member; matching source provenance. K1 public provenance has application_material_included=false, and every payload hash listed in its provenance matched. Node retains the admitted Design Guideline commit 5b882bc3d9b13a86e6c26111ef5ded687ea3a2fc. Payload regular-file counts are 71 for Node and 150 for K1.

  • mission-core-node_0.8.12_amd64.deb: 109838058 bytes; SHA-256 b5ee0471e3dc61f5533e5e4e80b3b98a6afc73aa964f7a74fbb20d1df58ef44f.
  • mission-core-xgrids-k1_0.1.12_amd64.deb: 318115004 bytes; SHA-256 9b382258d358f8cabf5b75e4f46d3475b83856ec604a389b10d0a575ea85cd27.

The unique R15 directory is staged on the authorized board. Both remote SHA-256 checks passed. Apt simulation upgrades exactly Node 0.8.11 → 0.8.12 and K1 0.1.11 → 0.1.12, with zero additions/removals. Existing pre-install safe-state guards are unchanged. No credential extraction or replacement is included.

The native “Mission Core · K1 R15” installer is open for owner-entered sudo. Actual installation status and physical acceptance remain pending. The owner should test through the regular UI with Chrome cache cleared: fresh scan/select, password visibility, one Apply, named start, stop, and Verify after returning to the device. No physical CLI probe or scanner command was issued by the agent. CUA exposes no documented cache/click controls in this session, so clean-cache visual/hardware acceptance is not inferred from unit or source checks.

Final Core health returned HTTP 200 and ok=true. Port 8000 is still served by the original PID 88424; its HTML references the newly built app-ClyCkvea.js and app-DD9yVBC_.css. No service on port 8765 was started.

Installation verified after owner restart

The owner completed the Ubuntu installation and restarted. Read-only checks confirmed Node 0.8.12 and K1 0.1.12 installed. Both services are active/running with NRestarts=0; start times are 20:13:40 and 20:13:49 MSK respectively on 2026-09-07. The bounded last-five-minutes warning/error service journal returned no entries. Fleet reports the board online. Physical enrollment and acquisition acceptance remain pending the owner's next UI test.