feat: OTA control plane (signed firmware releases, update scheduling, TLS, admin UI) #8

Merged
troed merged 13 commits from devel into main 2026-10-10 17:35:28 +02:00
Owner

OTA control plane (crew)

Implements the crew side of the Starfleet OTA design
(docs/specs/2026-09-10-ota-design.md, spec + plan docs in this PR):

  • crew/firmware.py (new): strict MAJOR.MINOR.PATCH version parsing,
    manifest validation, Ed25519 signature verification (new cryptography dep).
  • Store: firmware_releases + firmware_updates tables (FK'd),
    devices.last_firmware_version with legacy-DB migration.
  • API: PUT /firmware/{version} (multipart image+manifest+signature;
    strict validation pipeline; DB row rolled back on file-write failure),
    GET /firmware, the open-on-LAN download surface (latest.json,
    manifest.json, manifest.sig, image.bin), PUT/DELETE /devices/{id}/firmware-update, GET /devices/{id}/firmware-pending, and
    firmware fields on GET /devices rows (derived pending/applied status).
  • CLI: TLS serving when api.tls_cert/api.tls_key are configured.
  • Admin UI: Firmware page + per-device firmware rows with Schedule/Cancel.
  • Docs: README config/API/section updates, AGENTS.md, regenerated
    screenshots.

Tests: 190 passing; ruff + mypy clean. Admin UI verified in a live browser
session (all badge states, schedule/cancel round-trips).

Follow-up plans (same spec, separate PRs): ② computer (server.ota.schedule
relay, wss migration, conformance) ③ communicator-esp32 (A/B partitions,
ota module) ④ communicator-sailfish (wss migration).

# OTA control plane (crew) Implements the crew side of the Starfleet OTA design (`docs/specs/2026-09-10-ota-design.md`, spec + plan docs in this PR): - **`crew/firmware.py`** (new): strict `MAJOR.MINOR.PATCH` version parsing, manifest validation, Ed25519 signature verification (new `cryptography` dep). - **Store**: `firmware_releases` + `firmware_updates` tables (FK'd), `devices.last_firmware_version` with legacy-DB migration. - **API**: `PUT /firmware/{version}` (multipart image+manifest+signature; strict validation pipeline; DB row rolled back on file-write failure), `GET /firmware`, the open-on-LAN download surface (`latest.json`, `manifest.json`, `manifest.sig`, `image.bin`), `PUT/DELETE /devices/{id}/firmware-update`, `GET /devices/{id}/firmware-pending`, and firmware fields on `GET /devices` rows (derived pending/applied status). - **CLI**: TLS serving when `api.tls_cert`/`api.tls_key` are configured. - **Admin UI**: Firmware page + per-device firmware rows with Schedule/Cancel. - **Docs**: README config/API/section updates, AGENTS.md, regenerated screenshots. Tests: 190 passing; ruff + mypy clean. Admin UI verified in a live browser session (all badge states, schedule/cancel round-trips). Follow-up plans (same spec, separate PRs): ② computer (`server.ota.schedule` relay, wss migration, conformance) ③ communicator-esp32 (A/B partitions, ota module) ④ communicator-sailfish (wss migration).
- store: add delete_firmware_release for release rollback
- put_firmware_release: return 500 and delete the row when release files fail to write, so the version is not permanently re-upload-blocked and downloads/latest.json do not point at a missing release
- list_devices: derive firmware version/status from the live value (strictly newer than the stored row) so the pending->applied flip is not one poll late
- tests: write-failure rollback, same-poll applied status, firmware-pending public base URL pin, and newest-first release list pin
docs: regenerate README screenshots with firmware UI
All checks were successful
CI / Sanity check (ubuntu-latest) (push) Successful in 36s
CI / Sanity check (ubuntu-latest) (pull_request) Successful in 28s
2d58193b16
fix(firmware): persist device-reported version on the pending relay
All checks were successful
CI / Sanity check (ubuntu-latest) (push) Successful in 38s
CI / Sanity check (ubuntu-latest) (pull_request) Successful in 29s
d58fb61899
The device reports its running firmware version on every connect. crew now
records it when computer asks for firmware-pending, so the derived
applied/pending status survives the device dropping out of computer's live
registry. An already-current device gets 404 (nothing pending), so computer
stops re-sending server.ota.schedule while crew still shows the update as
applied.
troed merged commit 072259b1a6 into main 2026-10-10 17:35:28 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
starfleet/crew!8
No description provided.