wip(k1): checkpoint connection recovery rewrite
Capture the current unreleased K1 connection, recovery, lifecycle, viewer, and test work as a single known-bad baseline for subsequent fixes.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# ADR 0013: explicit K1 local connection matrix
|
||||
|
||||
- Status: amended 2026-07-20; Bridge is the product path, Quick Connect retained as a prepared-host laboratory path
|
||||
- Status: amended 2026-08-08; Bridge is the product path, Quick Connect retained as a prepared-host laboratory path
|
||||
- Date: 2026-07-19
|
||||
- Extends: ADR 0004, ADR 0005 and ADR 0012
|
||||
|
||||
@@ -47,6 +47,23 @@ for the previous session. An active acquisition is never retargeted in place.
|
||||
The later correlated MQTT `DeviceInfo` response supplies model, firmware,
|
||||
serial and vendor identity; IP equality alone cannot identify a K1.
|
||||
|
||||
The 2026-08-08 physical Bridge trace corrected the earlier field model: the
|
||||
first `7f02` text slot is `WIFI_AP` in AP state but contains the joined network
|
||||
name in FW 3.0.2 station state. Mission Core therefore normalizes that station
|
||||
response to `WIFI_CLIENT` internally and admits Bridge/Direct only when the raw
|
||||
post-write name exactly matches the current explicit request plus a valid
|
||||
non-AP private address. The raw name is not persisted in the secret-free
|
||||
network audit or published through API state.
|
||||
|
||||
The limitation still applies when recording an interrupted attempt that has no
|
||||
exact post-write network-name observation. A changed private DHCP address alone
|
||||
cannot identify the selected network. An already AP-ready baseline likewise
|
||||
cannot prove the outcome of an interrupted Quick-to-Quick attempt. Mission Core
|
||||
therefore records that attempt as terminal `outcome-unknown` and never replays it automatically. The
|
||||
historical uncertainty is not a permanent barrier: after the old active
|
||||
operation and cleanup have ended, a later explicit operator scan, selection,
|
||||
and connect is a distinct session with its own one reviewed write.
|
||||
|
||||
Product decision on 2026-07-20: Bridge/direct-LAN is the continuing route.
|
||||
Quick Connect remains visible and executable on an already prepared host, but
|
||||
is not a deployment dependency or portability claim.
|
||||
@@ -86,12 +103,65 @@ and the following cold Swift/CoreWLAN process missed the beacon. The corrected
|
||||
implementation holds the selected `BleakClient` open through bounded native
|
||||
SSID discovery and the single association call.
|
||||
|
||||
BLE discovery and the selected device action form one host session. A physical
|
||||
run proved that immediately rediscovering the same K1 by its CoreBluetooth UUID
|
||||
can fail even though the preceding scan exposed it. Mission Core retains the
|
||||
non-serializable `BLEDevice` handle process-locally and uses that exact handle
|
||||
for the next selected network action; it never exposes the handle through API
|
||||
state or treats the macOS UUID as durable device identity.
|
||||
One explicit six-second BLE discovery and the later Apply action form one
|
||||
operator intent without a second discovery. A physical run proved that
|
||||
immediately rediscovering the same K1 by its CoreBluetooth UUID can fail even
|
||||
though the preceding scan exposed it. Mission Core retains the non-serializable
|
||||
`BLEDevice` handle process-locally and uses that exact handle for Apply; it never
|
||||
exposes the handle through API state or treats the macOS UUID as durable device
|
||||
identity. UI row selection itself performs no GATT or backend I/O.
|
||||
|
||||
The operator-visible candidate list, local selected draft and admitted active
|
||||
session are separate contracts. Every explicit scan replaces the candidate
|
||||
set. Selection only binds a local form to one result from the latest admitted
|
||||
generation. Wall-clock age does not remove that generation while the operator
|
||||
completes the form. Apply admits only its exact retained handle and live GATT
|
||||
validation may create the active session; a remembered UUID is never mutation
|
||||
authority. Proven disconnect, explicit stop, app/backend restart, another
|
||||
explicit Scan, or a committed mode transition revokes the applicable candidate
|
||||
or live session. Rediscovery never auto-connects.
|
||||
|
||||
Quick Connect to Bridge is an explicit topology transition, not another scan
|
||||
heuristic. Selecting Bridge — or choosing another K1 while Bridge is already
|
||||
selected — sends one idempotent local `reset_scenario` CAS. It seals retained
|
||||
receiver/camera/control ownership, invalidates candidates and credentials and
|
||||
retires old physical lineage truthfully, while sending no device command, BLE,
|
||||
host-network write or automatic Scan. The next explicit Scan starts the clean
|
||||
discovery flow, while Apply remains the topology and device-mutation boundary.
|
||||
The operator selects one discovered K1, sees the Bridge credentials immediately
|
||||
and submits once. The new GATT
|
||||
session reads internal baseline `7f02` and emits exactly one reviewed 99-byte
|
||||
station write. A connect failure ends that attempt. An ambiguous post-write
|
||||
failure is terminal `outcome-unknown` audit, not a permanent cross-session
|
||||
fence. There is no automatic BLE or network-write retry.
|
||||
|
||||
Likewise, an exact REST `network_applied` result with unready/unknown control
|
||||
spends that Apply and its credentials without making the read-model attempt a
|
||||
permanent topology lock. Recommended Verify is pinned to the backend
|
||||
current/configured target. A separately explicit new intent still requires
|
||||
current server policy: Bridge prepares `select-device` before a later fresh
|
||||
scan; Quick and Direct run an admitted fresh scan, select only its latest row
|
||||
and create a new idempotency Apply; a mode or same-mode new-device transition
|
||||
uses one idempotent local-only `reset_scenario` before that fresh scan. None of
|
||||
these new-intent UI paths reuses the old
|
||||
intent or runs as a hidden frontend/mutating continuation. The service-owned
|
||||
same-intent read-only bootstrap declared below is the sole post-ACK exception.
|
||||
|
||||
The Apply REST call returns as soon as the exact durable
|
||||
`network_applied` proof is available. The service may continue the same
|
||||
intent's supervised control bootstrap read-only after that ACK. This performs
|
||||
no BLE/host mutation or retry and creates no frontend Verify, Scan, Apply or
|
||||
blocking Apply loader. While the exact child is accepted/running, the UI may
|
||||
show only a passive **Сеть настроена · подтверждаем управление** indicator and
|
||||
must keep every recovery action disabled. Later control state arrives only as
|
||||
backend presentation convergence; terminal unready/unknown state then exposes
|
||||
the explicit server-policy recovery choices.
|
||||
|
||||
Bridge and Quick Connect were physically accepted as separate paths before
|
||||
this amendment. The combined Quick Connect to Bridge transition has automated
|
||||
contract coverage but remains a distinct physical acceptance gate; it must not
|
||||
be reported as field-accepted until one redacted live run records both sides of
|
||||
the transition.
|
||||
|
||||
The corrected host boundary derives a non-secret, device-scoped profile ID from
|
||||
the selected SSID. The reviewed client contains per-device `WiFiAP_SSID` and
|
||||
@@ -110,13 +180,40 @@ that opaque source before any BLE write. A missing provider fails closed. The
|
||||
browser, API, argv, logs, manifests and evidence never receive the secret; the
|
||||
importer's short-lived mutable buffer is zeroized after the Keychain handoff.
|
||||
|
||||
The 2026-08-06 field regression established that process identity is part of
|
||||
this prepared-host contract. Runtime `swiftc` compilation produced an ad-hoc
|
||||
helper with an unstable designated identity. macOS then requested Keychain
|
||||
authorization repeatedly and the same process context failed to expose the
|
||||
exact K1 SSID through CoreWLAN even after K1 had acknowledged AP-ready. That
|
||||
runtime-compiled route is rejected. The laboratory adapter uses the previously
|
||||
physically accepted Apple-signed interpreter path,
|
||||
`/usr/bin/xcrun swift <reviewed-source>`, and validates the source path before
|
||||
launch. A portable product implementation still requires a packaged,
|
||||
precompiled and properly signed helper with a stable bundle identifier,
|
||||
designated requirement, Location/CoreWLAN authorization and Keychain ACL; the
|
||||
current prepared-host path does not claim that packaging work is complete.
|
||||
|
||||
Before any BLE write, the helper's preflight is non-interactive. It first checks
|
||||
Keychain item existence through metadata, then validates the selected profile's
|
||||
SSID and `exact-firmware-profile` provenance inside the helper without returning
|
||||
secret data. Provider material is also read with interaction disabled if a
|
||||
missing device profile must be materialized. The association phase accepts only
|
||||
that already materialized exact profile. It never
|
||||
falls back to the system Wi-Fi Keychain, rewrites a profile opportunistically,
|
||||
or opens a password/authorization dialog after K1 has changed network state.
|
||||
An unavailable or unauthorized profile therefore fails closed with a precise
|
||||
reason code and no automatic device retry.
|
||||
|
||||
The host-network boundary, rather than the XGRIDS frontend, owns platform
|
||||
association. Browsers expose no Wi-Fi join API, and Apple's iOS
|
||||
`NEHotspotConfiguration` consent flow is unavailable on macOS. The current
|
||||
implementation therefore uses a short-lived Swift/CoreWLAN + macOS Keychain
|
||||
helper; Windows Credential Manager and Linux Secret Service adapters remain
|
||||
separate platform work. The helper performs repeated read-only exact-SSID scans
|
||||
inside one 15-second discovery window and at most one association. It never
|
||||
inside one 30-second discovery window and at most one association. The larger
|
||||
window covers the physically observed 18.142-second beacon-discovery case;
|
||||
AP-ready confirms K1 state but does not prove that macOS has already observed
|
||||
the RF beacon. It never
|
||||
repeats the BLE command, guesses a password or treats `7f01` as a credential-read
|
||||
command. The credential-bearing 99-byte station-provisioning frame and fixed
|
||||
100-byte AP-enable frame are separate reviewed payloads.
|
||||
@@ -128,12 +225,13 @@ The owner also observed no explicit device/account pairing in the normal
|
||||
LixelGo onboarding flow; this is consistent with a firmware-defined AP secret,
|
||||
but does not establish account-wide authorization for arbitrary scanners.
|
||||
|
||||
Connection verification refreshes the session-scoped lease with the same
|
||||
read-only BLE status operation. It does not write a characteristic, re-provision
|
||||
Apply may read BLE baseline internally while establishing a new selected
|
||||
session. Selection never does so, and the normal flow has no mandatory or hidden
|
||||
"verify without write" recovery step. The baseline read does not re-provision
|
||||
Wi-Fi, scan the subnet, change a host route, or touch VPN configuration. The
|
||||
later canonical MQTT session supplies the real data-plane connection and live
|
||||
`DeviceInfo` identity check. A BLE lease observation is therefore not by itself
|
||||
a claim that MQTT/RTSP is reachable.
|
||||
later canonical MQTT
|
||||
session supplies the real data-plane connection and live `DeviceInfo` identity
|
||||
check; BLE status alone is not a claim that MQTT/RTSP is reachable.
|
||||
|
||||
## Consequences
|
||||
|
||||
@@ -148,6 +246,10 @@ a claim that MQTT/RTSP is reachable.
|
||||
therefore not scheduled for this Quick Connect path.
|
||||
- Direct Connect requires an already-running hotspot and a controller route;
|
||||
Mission Core does not create or manage that hotspot.
|
||||
- Discovery never auto-connects devices. Mode, selection and input are local
|
||||
only. App restart, disconnect, explicit stop and mode transition require a
|
||||
fresh explicit scan-select-Apply session. Apply performs no hidden rescan or
|
||||
Verify and may cross at most one device-mutation boundary.
|
||||
- The application-control, START/STOP and raw-first acquisition protocol is
|
||||
unchanged after a target address is admitted.
|
||||
- Direct Connect remains explicitly pending one owner-operated physical
|
||||
|
||||
Reference in New Issue
Block a user