feat: add multi-device provisioning and local remote-config UI
- add multi-device portal device management, key rotation, and sync request APIs - add Pi settings UI for remote server URL, device name, and API key - add local test-connection and sync-now actions - make upload and config sync reuse dynamic shared credentials - add Phase 25.5c migration and docs updates Co-authored-by: Copilot <[email protected]>
This commit is contained in:
@@ -1570,10 +1570,166 @@ updates its display-facing values without a manual restart once sync lands.
|
||||
|
||||
---
|
||||
|
||||
### Phase 26: Native Android companion app — 🚧 PLANNED (low priority, always last)
|
||||
Revives the phone-relay goal (Phase 11/12) properly this time, using a real native app instead of
|
||||
a web page - the only way to actually hold a WiFi connection to the Pi's hotspot and a cellular
|
||||
connection to the internet open simultaneously (`ConnectivityManager.bindProcessToNetwork()`,
|
||||
### Phase 25.5c: Multi-device provisioning + force-sync UX — 🚧 PLANNED
|
||||
Extend the current single-device portal page into a real multi-device management
|
||||
surface: register devices from the server, mint/rotate API keys, help the Pi
|
||||
consume those credentials without SSH-heavy manual steps, and expose an honest
|
||||
**force sync** action that works with the system's pull-based/offline-first
|
||||
architecture.
|
||||
|
||||
#### Goals
|
||||
- **Multi-device server view**: stop assuming one `DEVICE_NAME` in the portal;
|
||||
show all registered devices and let the user select which one is being edited
|
||||
- **Device registration from the portal**: create a new device row and return a
|
||||
one-time raw API key directly in the browser UI (same behavior as
|
||||
`server/scripts/create_device.py`, but web-managed)
|
||||
- **Credential handoff to the Pi**: make it possible to put `YARIS_SERVER_URL`,
|
||||
`YARIS_DEVICE_NAME`, and `YARIS_API_KEY` onto the Pi from the app UI rather
|
||||
than editing `db/upload_creds.env` manually over SSH
|
||||
- **Force sync button**: let the user request an immediate config pull without
|
||||
pretending the cloud server can magically push into an offline car
|
||||
|
||||
#### Proposed implementation
|
||||
1. **Server: device management model + portal**
|
||||
- Add a device list page/card in the remote portal, with:
|
||||
- registered devices table
|
||||
- "Add device" action
|
||||
- "Rotate API key" action
|
||||
- per-device "Edit settings" action
|
||||
- Replace the single-device lookup helper with:
|
||||
- list devices
|
||||
- fetch one device by id
|
||||
- update one device's identity/config
|
||||
- Keep raw API keys **write-only/one-time reveal**:
|
||||
- show only immediately after create/rotate
|
||||
- never store raw keys server-side
|
||||
- only store hash as today
|
||||
|
||||
2. **Server: browser APIs**
|
||||
- Add login-protected endpoints for:
|
||||
- list devices
|
||||
- create device + return one-time API key
|
||||
- rotate device API key + return one-time API key
|
||||
- get/update selected device config
|
||||
- request force sync for a selected device
|
||||
- Device settings UI should populate from the selected device row, not from a
|
||||
single hardcoded portal `DEVICE_NAME`
|
||||
|
||||
3. **Pi: credential management UI**
|
||||
- Add a local Settings section for remote server credentials:
|
||||
- server URL
|
||||
- device name
|
||||
- API key
|
||||
- connection test button
|
||||
- Persist those values in the existing gitignored
|
||||
`db/upload_creds.env` (or an equivalent sqlite-backed secret store if we
|
||||
later choose to migrate away from env files)
|
||||
- After saving:
|
||||
- reload upload client / sync client config
|
||||
- allow enabling **Sync with server** only when creds test cleanly
|
||||
|
||||
4. **Force sync semantics**
|
||||
- **Constraint**: the server cannot directly reach a Pi behind hotspot/NAT,
|
||||
and the car is often offline. So a server-side force sync button cannot be
|
||||
a true push.
|
||||
- Proposed behavior:
|
||||
- server sets a per-device `force_sync_requested_at` flag/version bump
|
||||
- Pi checks that flag during normal online polling
|
||||
- local Pi UI also gets a **Sync now** button that triggers an immediate
|
||||
fetch against the server using the saved credentials
|
||||
- Result:
|
||||
- from server: "sync on next online contact"
|
||||
- from Pi: "sync immediately if internet is currently available"
|
||||
|
||||
#### Data model changes
|
||||
- Add server columns such as:
|
||||
- `last_config_sync_at`
|
||||
- `last_config_sync_version`
|
||||
- `force_sync_requested_at`
|
||||
- `api_key_rotated_at`
|
||||
- Optionally add a lightweight pairing/claim model later if copying raw API keys
|
||||
manually into the Pi UI feels too clunky
|
||||
|
||||
#### UI/UX shape
|
||||
- **Remote portal**
|
||||
- left: device list / selector
|
||||
- right: settings form for selected device
|
||||
- actions: Add device, Rotate key, Copy creds, Request sync
|
||||
- status badges: last seen, last config version, last sync requested, maybe
|
||||
last successful contact
|
||||
- **Pi local app**
|
||||
- server credentials card in Settings
|
||||
- Test connection button
|
||||
- Sync now button
|
||||
- clear warning when local edits are locked because server sync is enabled
|
||||
|
||||
#### Security / operational notes
|
||||
- Never display a stored API key after the creation/rotation response is gone
|
||||
- Keep key rotation explicit; rotating a key should warn that the old Pi config
|
||||
will stop authenticating until updated
|
||||
- Avoid logging raw keys in Flask logs, JS errors, or shell output captured in
|
||||
committed docs
|
||||
- Because the Pi is usually offline, all flows must degrade gracefully when the
|
||||
remote portal requests work the Pi cannot act on yet
|
||||
|
||||
#### Suggested execution order
|
||||
1. Server multi-device DB/API refactor
|
||||
2. Remote portal device list + register/rotate key UI
|
||||
3. Pi local server-credentials UI + connection test
|
||||
4. Pi/local immediate **Sync now** button
|
||||
5. Server "request sync" flag + per-device status indicators
|
||||
6. End-to-end validation:
|
||||
- create device on server
|
||||
- copy/paste creds into Pi UI
|
||||
- test connection
|
||||
- enable sync
|
||||
- change device identity on server
|
||||
- verify Pi receives it
|
||||
- rotate key and verify old key stops working until Pi updated
|
||||
|
||||
#### Open constraint to keep in mind
|
||||
- If you want **zero manual copy/paste** for API key provisioning, that becomes
|
||||
a pairing problem (claim code / QR / short-lived token). That is doable, but
|
||||
it is a bigger scope than simply exposing key create/copy fields in the two
|
||||
existing UIs. My recommendation is to ship copy/paste provisioning first, then
|
||||
add a pairing flow only if it still feels too annoying.
|
||||
|
||||
#### Implementation progress (2026-08-10)
|
||||
- **Server-side multi-device API/UI**
|
||||
- Added device listing, create-device, rotate-key, per-device config, and
|
||||
request-sync endpoints in the remote portal
|
||||
- Reworked `/settings/device` into a multi-device management page with a
|
||||
registered-device list, selected-device editor, one-time API-key reveal, and
|
||||
copy-credentials helper
|
||||
- **Server schema**
|
||||
- Added Phase 25.5c migration for `last_config_sync_at`,
|
||||
`last_config_sync_version`, `force_sync_requested_at`, and
|
||||
`api_key_rotated_at`
|
||||
- Added device sync acknowledgement endpoint so the Pi can report when a
|
||||
config version was actually applied
|
||||
- **Pi-side provisioning**
|
||||
- Added local Settings UI/API for `YARIS_SERVER_URL`, `YARIS_DEVICE_NAME`, and
|
||||
`YARIS_API_KEY`
|
||||
- Added **Test connection** and **Sync now** actions in the Pi app
|
||||
- Reused the existing gitignored `db/upload_creds.env` store instead of
|
||||
inventing a second credential location
|
||||
- **Runtime behavior fixes**
|
||||
- Upload client now reads credentials dynamically instead of only at import
|
||||
- Device sync thread can restart after credentials are added later
|
||||
- Enabling **Sync with server** now validates that remote credentials exist
|
||||
|
||||
#### Pending validation
|
||||
- Deploy Phase 25.5c to unraid + Pi
|
||||
- Live smoke-test device creation / key rotation / local credential paste flow
|
||||
- Confirm server-side **Request sync** and Pi-side **Sync now** behavior against
|
||||
the running hardware
|
||||
|
||||
---
|
||||
|
||||
### Phase 26: Native Android companion app — 🚧 PLANNED (low priority, always last)
|
||||
Revives the phone-relay goal (Phase 11/12) properly this time, using a real native app instead of
|
||||
a web page - the only way to actually hold a WiFi connection to the Pi's hotspot and a cellular
|
||||
connection to the internet open simultaneously (`ConnectivityManager.bindProcessToNetwork()`,
|
||||
unavailable to browser JS - see the Phase 11 update). Explicitly **not urgent** and intentionally
|
||||
kept at the end of the roadmap, after all web/server work and the Phase 25 core in-car acceptance
|
||||
test are done. Permanent vehicle installation remains owner-handled and is not an implementation
|
||||
|
||||
Reference in New Issue
Block a user