Adds the server side of auto-update (issue #16, was epic #6's [H]). - pb_migrations/1786500000_releases.js: a superuser-only 'releases' collection (platform, variant, version, buildNumber, file, sha256, size, notes). Read is gated in the rules — any signed-in account sees clean builds; only an nsfwEnabled account sees adult ones — so PocketBase's native protected-file serving hands adult bytes only to flagged accounts (no custom streaming). - pb_hooks/update.pb.js: GET /api/update/manifest?platform=… (auth). Picks the variant server-side from the caller's nsfwEnabled (adult) vs clean — the client can't request adult — and returns the latest build's version/buildNumber/notes/ sha256/size + the protected downloadPath. - scripts/publish-release.sh: uploads a built artifact as a superuser (computes sha256 + size, multipart POST). Token or email+password via env. - docs/auto-update-contract.md: the collection, endpoint, download flow, gating. Migration + hook + script syntax-checked. Live verification pends deploying this to the PB (collection auto-applies on boot, hook loads from pb_hooks/).
3.5 KiB
Auto-update — contract (issue #16)
The app checks for a newer build and installs it, receiving the adult or
clean variant according to its account's admin-set nsfwEnabled flag.
Server pieces (this repo):
pb_migrations/1786500000_releases.js— thereleasescollection.pb_hooks/update.pb.js— theGET /api/update/manifestroute.scripts/publish-release.sh— uploads a built artifact as a superuser.
Client pieces (amber-app): lib/data/update/update_service.dart (version check +
manifest fetch), the desktop swap-installer, and the Android APK install channel.
releases collection
| Field | Type | Notes |
|---|---|---|
platform |
select | windows | linux | android. |
variant |
select | clean | adult. |
version |
text | Human semver shown to the user, e.g. 1.0.1. |
buildNumber |
number | Monotonic int; the comparator the updater actually uses. |
file |
file | The artifact: desktop = .zip of the release bundle, android = .apk. Protected (see rules). |
sha256 |
text | Lowercase hex SHA-256 of the artifact; verified before install. |
size |
number | Bytes. |
notes |
text | Optional release notes (shown in the update prompt). |
created/updated |
autodate |
Unique index on (platform, variant, buildNumber).
Access rules. Read is gated:
@request.auth.id != '' && (variant = 'clean' || @request.auth.nsfwEnabled = true)
— any signed-in account reads clean rows; only an nsfwEnabled account reads
adult rows. Because the view rule needs auth, the attached file is served
as a protected file: downloading needs a short-lived file token
(POST /api/files/token), and minting/using it re-checks the same rule — so
adult bytes never reach a non-flagged account. create/update/delete are
superuser-only (null rules); publishing goes through the admin API.
Endpoint
GET /api/update/manifest?platform=windows|linux|android (auth: users)
Returns the latest build for the caller's platform. The variant is chosen
server-side from nsfwEnabled — the client cannot request adult.
- No release for that platform/variant →
{ "available": false }. - Otherwise:
{ "available": true, "platform": "linux", "variant": "clean", "version": "1.0.1", "buildNumber": 2, "notes": "…", "sha256": "<64 hex>", "size": 12345678, "filename": "amber-linux-clean.zip", "downloadPath": "/api/files/releases/<recordId>/<filename>" }
Download. The app mints a file token (POST /api/files/token, its user
auth) and GETs {base}{downloadPath}?token=<fileToken>, then verifies sha256
before installing.
Publishing a build
Build the artifact, then (on the build machine):
PB_ADMIN_TOKEN=<superuser token> ./scripts/publish-release.sh \
--platform linux --variant clean --version 1.0.1 --build 2 \
--file build/amber-linux-clean.zip --notes "What changed"
PB_ADMIN_EMAIL + PB_ADMIN_PASSWORD work instead of a token. The script
computes the SHA-256 + size and uploads via the superuser REST API. Re-publishing
the same (platform, variant, buildNumber) is rejected by the unique index —
bump buildNumber for each release.
Gating summary
nsfwEnabled (admin-set on the users record) is the single source of truth:
the manifest hook reads it to pick the variant, and the collection rules enforce
it independently at read/download time. A no-flag account only ever sees and
downloads clean builds.