Add phone hotspot uplink

Prefer a configured phone hotspot for outbound synchronization, preserve secure write-only credentials, and recover through home WiFi or the local Pi hotspot.

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-09 22:48:11 -04:00
co-authored by Copilot
parent 5c707fe295
commit cdb032b888
17 changed files with 1211 additions and 88 deletions
+39 -2
View File
@@ -1092,7 +1092,7 @@ defaults and shared alert/navigation shell. Browser testing at 390x844 found and
top-bar overlap by adding a dedicated bottom mobile save action; the real save then completed with
no horizontal overflow or console errors.
### Phase 22: Phone-hotspot internet uplink — 🚧 PLANNED
### Phase 22: Phone-hotspot internet uplink — ✅ DONE
Add a no-SIM internet path where the Pi joins a user's phone hotspot as a WiFi client and uses the
phone's cellular data for outbound synchronization. This must work with an ordinary iPhone or
Android hotspot and support tracking rows, alert events/state, software/server checks, and large
@@ -1124,10 +1124,47 @@ client simultaneously:
while still associated, automatic Pi-hotspot recovery, local `yaris.local` access, tracking
catch-up, and a disposable chunked video upload. Record phone/OS limitations before committing.
**Implemented and hardware-tested 2026-08-09 with a real Pixel hotspot.**
- Added a root-owned `yaris-uplink` NetworkManager helper, systemd timer/wait service, dispatcher
transition recorder, restricted sudoers entries, and Settings APIs/UI. Priority is
`phone-hotspot` (20), `home-wifi` (10), then the Pi `Hotspot` (-10). A non-disruptive scan checks
that the phone SSID is actually visible before replacing a working home connection.
- The intended single-phone workflow is **save credentials first → select Wait for hotspot → enable
the phone hotspot**. The wait service gives the user ten seconds before dropping the Pi AP, scans
for up to two minutes, and always restarts the periodic watcher. If the phone never appears or
later disappears, NetworkManager/helper recovery selects home WiFi when available and otherwise
restores the Pi hotspot. Multiple dead-man tests independently forced home WiFi recovery.
- Phone credentials go from browser to the privileged helper over stdin and into an atomic
root-only (`0600`) NetworkManager keyfile. Passwords never enter SQLite, command arguments,
status files, HTML/JavaScript responses, or read APIs. The UI exposes only configured state and
SSID, supports replace/remove, and preserves in-progress edits during status polling.
- Status reports actual connection/mode/IP, NetworkManager-proven internet connectivity, whether
large uploads are currently possible, last transition/error, and the last phone-assigned direct
address. The non-secret transition record persists across reboot under `/var/lib/yaris-obd`.
- Pi-hosted hotspot testing initially found that the real phone could open `10.42.0.1` but not
`yaris.local` even though Avahi correctly joined/published on `wlan0`. Added a NetworkManager
shared-dnsmasq unicast fallback for `yaris.local`; the repeated dead-man test then passed.
- Pixel tethering has a different limitation: the hotspot-host phone can reach the Pi by its DHCP
address, but relays neither mDNS (`yaris.local`) nor the DHCP hostname (`yaris`). Settings
therefore retains and links the last direct phone-network address with a DHCP-change warning.
The tested Pixel repeatedly assigned the same address, but the implementation does not assume it
is permanent.
- Proved cold boot both with Pixel absent (selected home WiFi) and present (selected Pixel directly,
Web UI/service started, and the server received a fresh heartbeat). Turning Pixel off repeatedly
restored home WiFi in about 16 seconds. The same fallback code is covered for restoring the Pi
hotspot when home WiFi is unavailable.
- Proved real tracking uploads through Pixel data against remote Postgres. The first disposable
1 MiB video exposed repeated TLS EOF failures over tethering despite small requests working;
setting only the phone profile MTU to 1400 fixed the path, after which a second 1 MiB disposable
video reached the real server over Pixel data. Both named local/server files and exact database
upload/hash rows were removed afterward, and dashcam uploads were restored enabled.
- The future Phase 24 alert-event synchronization queue will automatically use this normal IP
uplink; Phase 22 does not prematurely implement that server schema/API.
### Phase 23: Finish web UI redesign — 🚧 PLANNED
**Roadmap decision 2026-08-09: keep the redesign after functional uplink work.** Phase 15
already pulled the architecture-level work forward: every page now shares one Jinja shell and
the later list-based features have a reusable component contract. Phases 16-21 still add most of
the later list-based features have a reusable component contract. Phases 16-22 still add most of
the final screens and dashboard data. Redesigning their visual details before those features
exist would mean guessing at layouts and polishing the same surfaces twice; keeping the final
pass here lets it cover the complete product once, while new phases still build on consistent