# OTA protocol The updater uses a JSON manifest rather than Git operations on-device. ## Manifest schema 1 ```json { "schema": 1, "release": "1.2.3", "files": [ { "source": "firmware/launcher.py", "target": "/launcher.py", "sha256": "..." }, { "source": "firmware/app/main.py", "target": "/app/main.py", "sha256": "..." } ] } ``` A file is downloaded only when its local SHA-256 differs. ## Transaction 1. Wait for the long update interval to become due. 2. Wait for configured inactivity and no busy lock. 3. Connect Wi-Fi if needed. 4. Fetch the manifest. 5. Hash local target files. 6. Download only changed files into `/.update`. 7. Verify every SHA-256. 8. Create `/update_pending.json` with `commit_complete=false`. 9. Rename current files to `.bak`. 10. Rename staged files into their final locations. 11. Mark `commit_complete=true`. 12. Reboot. 13. New launcher/app performs setup. 14. App calls `ctx["system"].ready()`. 15. Backups and pending marker are removed. A power loss during commit leaves `commit_complete=false`; stable `boot.py` restores the backups before normal startup. If a completed update cannot import/start, `boot.py` restores the backups. ## Why boot.py is intentionally stable The recovery component should not replace itself in the same transaction it is responsible for recovering. Update `/boot.py` manually and rarely. ## Authentication Credentials exist only in `/.env`. Supported modes: - `none` / `public` - `auto` - `basic` - `bearer` - `token` - `header` (arbitrary header + optional prefix) HTTPS is required unless `UPDATE_ALLOW_HTTP=true` is explicitly set.