Device updates
The update-check contract: one GET request, staged rollouts, update policies.
The contract
GET /u/<appRef>/<channel>/<platform>/<arch>/<currentVersion>| Header | Purpose |
|---|---|
X-Relayer-Device | Anonymous UUID, generated and persisted by your app. Enables sticky rollout bucketing and the fleet dashboard. Optional but strongly recommended. |
Responses:
200- update available; JSON manifest below.204- up to date, no matching artifact, or this device is not in the rollout cohort yet. Either way: do nothing, check again later.
{
"version": "1.5.0",
"notes": "markdown release notes",
"pub_date": "2026-07-12T10:00:00Z",
"url": "https://your-storage/…/app-darwin-arm64.dmg",
"sha512": "…",
"signature": "…",
"updateMode": "recommended",
"minimumSupportedVersion": "1.2.0"
}Version comparison is semver-aware (prereleases handled). Artifact matching:
exact platform+arch first, then platform+universal, then platform-only.
Try it
# one device, currently on 1.0.0
curl -H "X-Relayer-Device: $(uuidgen)" \
https://relayer-three.vercel.app/u/YOUR_APP_REF/stable/darwin/aarch64/1.0.0
# simulate a small fleet (each device = a distinct UUID)
for i in $(seq 1 10); do
curl -s -o /dev/null -H "X-Relayer-Device: $(uuidgen)" \
https://relayer-three.vercel.app/u/YOUR_APP_REF/stable/darwin/aarch64/1.0.0
doneStaged rollouts
Every release has a rollout percentage, controlled from the Releases tab
(1 → 10 → 25 → 50 → 100 quick steps, or any value). Devices are bucketed
deterministically - sha256(deviceId:releaseId) % 100 - so cohorts are
sticky: a device offered 2.1.0 keeps being offered 2.1.0; a device outside
the cohort keeps getting the previous release until you ramp.
Devices that don't send X-Relayer-Device can't be bucketed and sit out
partial rollouts - they only receive fully-rolled-out (100%) releases.
Pause stops serving a release temporarily. Roll back withdraws it permanently - the fleet falls back to the newest remaining release on their next check. Rollbacks cannot be un-done by design; publish a fixed version.
Update policies
Per channel, set a minimum supported version. Devices reporting a
version below it receive the update with "updateMode": "required" and the
minimumSupportedVersion field - your app decides how to enforce it (block
UI, force install, nag). Everything else gets your channel's default mode
(optional or recommended).
Client loop (any language)
on schedule (e.g. every 6h + on launch):
res = GET /u/APP/stable/{platform}/{arch}/{installed_version}
with X-Relayer-Device: {stored_uuid}
if res is 204: done
if res is 200:
verify sha512/signature after download
if updateMode == "required": install now
else: prompt or install per your UXTen lines in any runtime. If you ship Tauri or Electron, you don't even need that - see Adapters.