Безопасно восстанавливает управляющее подключение и локальные checkpoint без повторных команд сканеру. Добавляет PCAP/Bridge guardrails и регрессионные проверки одношагового переподключения. Известный дефект: после второго подключения интерфейс не присоединяется к новой генерации preview правой камеры. В живой Quick Connect-сессии STOP был принят, но READY не подтвердился до таймаута; автоматический повтор STOP запрещён.
266 lines
16 KiB
Markdown
266 lines
16 KiB
Markdown
# ADR 0013: explicit K1 local connection matrix
|
|
|
|
- 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
|
|
|
|
## Context
|
|
|
|
Mission Core v0.5.0 exposed only the already accepted shared-LAN path even
|
|
though the product UI named the other XGRIDS connection directions. Retained
|
|
owner-operated evidence establishes two different network ownership models:
|
|
|
|
- the K1 joins a network whose credentials are sent in the reviewed 99-byte BLE
|
|
provisioning frame; and
|
|
- the controller joins the K1 access point and then reaches the same local
|
|
MQTT/RTSP data plane at the observed AP address.
|
|
|
|
Official workflow notes separately name Bridge, Direct Connect and Quick
|
|
Connect. The names alone do not authorize another BLE command or an inferred
|
|
credential decoder.
|
|
|
|
## Decision
|
|
|
|
Plugin v0.6.0 exposes three explicit, fail-closed choices under one exact
|
|
firmware `3.0.2` compatibility profile:
|
|
|
|
| UI mode | Topology | Device action | Host action | Acceptance |
|
|
| --- | --- | --- | --- | --- |
|
|
| Bridge | `direct-lan` | one reviewed BLE provisioning write | none | physically accepted in Mission Core |
|
|
| Direct Connect | `controller-hotspot` | the same reviewed BLE provisioning write | operator prepares the hotspot and route | implementation complete; physical run pending |
|
|
| Quick Connect | `device-ap` | one fixed reviewed 100-byte AP-enable write | preinstalled exact-firmware credential provider, bounded exact-SSID discovery and one association | physically accepted on one prepared Mac; not portable bootstrap |
|
|
|
|
Bridge remains the default. Every API request carries both `connection_mode`
|
|
and its exact topology attestation; mismatched pairs are rejected before any
|
|
network action. Acquisition and camera admission must match the active mode.
|
|
The fixed K1 AP address is admitted only after a successful Quick Connect host
|
|
association.
|
|
|
|
Bridge and Direct Connect do not have a fixed product IP. The address reported
|
|
by BLE characteristic `7f02` is a DHCP lease observation scoped to the current
|
|
connection, never device identity and never a durable configuration value.
|
|
Before a new application-control session, an implicit-host acquisition, or a
|
|
factory-calibration read, Mission Core performs one read-only `7f02` read for
|
|
the selected CoreBluetooth device and replaces the previous target. An address
|
|
change creates a new `device_session_id` and invalidates calibration captured
|
|
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.
|
|
|
|
Direct Connect does not introduce a new vendor payload. The operator first
|
|
starts a hotspot on the controlling device and enters that hotspot's SSID and
|
|
password. Mission Core performs the same single, physically reviewed BLE write
|
|
used by Bridge and accepts only the non-AP private IPv4 returned by K1 status.
|
|
|
|
Quick Connect does not accept a credential from the browser/API. The first
|
|
implementation incorrectly jumped from BLE discovery directly to host Wi-Fi
|
|
association. A physical negative run showed that the selected K1 was still on
|
|
the existing LAN and the expected AP was not visible. Re-review of the exact
|
|
LixelGO branch recovered the missing state transition: after BLE connection it
|
|
writes one 100-byte `7f01` frame, zero except for `0x01` at offset 99, and treats
|
|
the callback as the AP-launch result before invoking the OS Wi-Fi connector.
|
|
|
|
Mission Core now performs that bounded AP-enable write at most once. The first
|
|
physical run reached `WIFI_AP` at the reviewed AP address, and a targeted
|
|
CoreWLAN scan found the SSID matching the selected BLE advertised name. It also
|
|
disproved the earlier assumption that LixelGO carried one global Quick Connect
|
|
profile: retained client analysis exposes `WiFiAP_SSID` and `WiFiAP_Password` as
|
|
fields of each device record.
|
|
|
|
A subsequent run found that `7f02` may retain `WIFI_AP / 192.168.56.1` after the
|
|
SSID stops beaconing. Static review then recovered the missing discriminator:
|
|
LixelGO maps response byte 51 to its AP-ready flag and waits up to 15 seconds for
|
|
that flag. Mission Core therefore mirrors LixelGO: every new operator Quick
|
|
Connect action emits exactly one reviewed enable frame, including from a
|
|
`WIFI_AP` baseline, and admits host discovery only after byte 51 becomes
|
|
non-zero. This is not an automatic retry.
|
|
|
|
The same review proved that LixelGO does not close its BLE manager between the
|
|
AP-ready callback and native Wi-Fi connect. A failed Mission Core run had done
|
|
exactly that: byte 51 changed from zero to one, the Python BLE context exited,
|
|
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.
|
|
|
|
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.
|
|
|
|
Quick Connect Apply is an exact idempotent ensure-target operation when the
|
|
selected K1 already reports the canonical AP-ready state. Mission Core accepts
|
|
that unchanged state only after the one reviewed write-with-response is
|
|
durably confirmed, then performs the normal exact device-SSID host association.
|
|
An unconfirmed or merely read-only unchanged observation remains fail-closed;
|
|
this rule neither retries the BLE write nor weakens Bridge admission.
|
|
|
|
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
|
|
`WiFiAP_Password` fields, but the 2026-07-20 review of the exact official K1
|
|
`3.0.2` firmware recovered their upstream source: the scanner's bundled
|
|
NetworkManager AP script assigns one firmware-constant WPA2 material, while
|
|
`lixel_nman` constructs `XGR-` plus six device-identity characters with a MAC
|
|
fallback. It is neither an iPhone credential nor a per-device secret.
|
|
|
|
The laboratory implementation can install a firmware-scoped credential source from the
|
|
authenticated official archive. The offline importer validates the exact
|
|
SHA-256, streams the bounded application-partition range, requires one valid AP
|
|
declaration and writes the value to the OS secure store through helper stdin.
|
|
On macOS, the Keychain helper materializes the selected device profile from
|
|
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 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.
|
|
|
|
No reviewed BLE characteristic or response supplies the AP credential. That
|
|
does not prove a universal negative for every vendor build, but it means the
|
|
current plugin has no evidence-backed device-side credential acquisition path.
|
|
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.
|
|
|
|
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; BLE status alone is not a claim that MQTT/RTSP is reachable.
|
|
|
|
## Consequences
|
|
|
|
- The UI now represents all three intended directions instead of disabled
|
|
placeholders.
|
|
- Quick Connect can temporarily remove the controlling host from its previous
|
|
Wi-Fi network. The operating system may require Wi-Fi/location permission.
|
|
- A new host needs a separate exact-firmware provider installation. The current
|
|
application does not obtain it from BLE and does not bootstrap it by itself.
|
|
- Automatic firmware download, iPhone extraction and hard-coded credential
|
|
delivery are explicitly rejected product routes. Windows/Linux adapters are
|
|
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
|
|
acceptance cycle. Quick Connect AP activation and host association are
|
|
physically accepted only on the prepared K1/FW 3.0.2 Mac stand. Bridge is the
|
|
only accepted portable product path in this matrix.
|