ReleasesDraft
Release checklist
The order matters: nothing is served before the release is signed, and everything served is built from the signed assets.
-
Green.
scripts/release-check.shpasses: the suite, the idle budgets, the packed tarball, a temp install, the Harness, and the checksums insite/box. -
Build. (Run the dry-run dispatch on a commit whose Windows build,
lumen-win.yml, is already green: a tag build has a Windows installer, but the release job can race it.) The release workflow buildsvyre.tgz,release.json, the box files,shell.json(scripts/shell-hashes.mjs, the hash of every file the phone's service worker caches) andmanifest.json. -
Sign. The sign job writes the final
SHA256SUMS(every file above is listed) andSHA256SUMS.sig: Ed25519, with the release key, over the linevyre-release-sumsand then the exactSHA256SUMSbytes. The private key is a protected secret of the workflow and is never on a machine. -
Publish. The GitHub release carries the assets as signed, unchanged:
vyre.tgz,SHA256SUMS,SHA256SUMS.sig,shell.json,manifest.jsonand the rest. Publishing never rebuildsSHA256SUMS. -
Prove the update path. On a throwaway server,
vyre updateinstalls the release and says "signature checked against Vyre's release key"; the same release with its signature removed is refused. A Mac does the same. 5b. Served matches signed. After the deploy in step 6,node scripts/check-served.mjs --origin https://vyre.run --release <the release's assets folder>passes: the setup page and the install scripts vyre.run serves are the files the release's signedsetup.jsonlists. It is a tamper check for what that one request gets, since an origin can answer another client or network differently: run it from more than one place. -
vyre.run. Deploy the site and the install files (
scripts/build-site.sh, thenscripts/deploy-site.sh site --branch main, which refuses a folder that carries a stagingsetup/config.json). -
phone.vyre.run. Built and deployed with the first signed release, with the owner's approval, and never before it:
node scripts/build-phone.mjs --tag vX.Y.Z --out phone-site npx wrangler pages deploy phone-site --project-name <the Pages project> --branch mainThe build refuses unless the signature verifies and every file of the signed
shell.jsonis in the site, so the origin serves exactly what was signed. Check thathttps://phone.vyre.run/release/SHA256SUMS.siganswers and that a phone that opened the page before shows the new shell after one reload. -
Announce only after steps 5 to 7 pass.
Naming (Lumen, Vyre Memory) waits for its own clearance before it goes on vyre.run.
What the released compose.yml must look like (pulled boxes)
A box that pulls its image checks the release before it pulls (vyre update in box/vyre, and install-box.sh). The release job must therefore write the released compose.yml with every image: line as the literal image: <name>@sha256:<64 hex>: no ${VYRE_IMAGE:-...} variable, no tag, no trailing comment. release.json names the box image (and the computer image) by the same digest under images, and cosign signs each digest with the release workflow's identity. The wrapper installed on a server is the release build of box/vyre (scripts/strip-wrapper.mjs, run by build-site.sh): release-check.sh fails if it still names a test override, and release.sh refuses VYRE_TEST_UNSTRIPPED_WRAPPER.
After 0.2.0 lands on stage: redeploy the site for vyre.run/w
https://vyre.run/w is scripts/install-windows.ps1, put at site/w by scripts/build-site.sh and served as plain text by site/_headers. It only exists once the site is redeployed from the commit that carries 0.2.0 (the site is built from main only; scripts/deploy-site.sh). Check afterwards: curl -sI https://vyre.run/w is 200 with content-type: text/plain, and curl -s https://vyre.run/w | cmp - scripts/install-windows.ps1. release-check.sh --live does both.
The patch fast lane
Code that runs on a server still ships only as a signed release. A patch needs no more ceremony than that.
node scripts/patch-release.mjs <fix commit>...makeshotfix/vX.Y.Z+1off the newest stable tag, cherry-picks the fixes, moves every version place, writesrelease/notes/X.Y.Z+1.mdfrom the commit subjects and commits it. Edit the first line of the notes (the summary) and push the branch (--pushdoes it).- The branch's hosted runs are the gates. List any red with its reason.
- With the user's go: set
VYRE_RELEASES=goand tag the commitvX.Y.Z+1.X, builds, and waits for the user'srelease` approval, which is the one approval the release needs. - After the release run completes,
release-verify.ymlruns the release checks by itself (the folder against the previous release's pinned key, both signatures, both image digests with and without a login, and a real box on the previous release updating itself), andsite-deploy.ymlputs vyre.run on the new tag, waiting for thedeployapproval. Both workflows run from main, so they are only live once they are on main. - The app deploy (
relay-deploy.ymlwithapp=trueand the release run id) waits for the samedeployapproval.
Before the relay fix for GHSA-25xh-w9j7-7v28 counts as closed
The app no longer has a shared limit on /v1/pair or the setup mailbox, so cost is the edge's. Both of these are required, not recommended:
- Dispatch relay-deploy with
relay=trueandrelay_edge_rule=true(a separate environment secret,CLOUDFLARE_WAF_TOKEN, scoped to Zone WAF Edit on vyre.run, used by this step only). It writes one per-address Cloudflare rate-limit rule on/v1/pairand/v1/setup/mbx, and prints the rules it wrote. Check the rule in the Cloudflare dashboard. - The relay's Workers and Durable Objects run on a paid plan (Workers Paid bought for vyre.run, 2 Oct 2026; confirm in the dashboard or by the account's plan in the API after the relay deploy). Every
/v1/pairrequest reaches a ticket object and every random locator on the mailbox creates one, so on the Free plan's daily request cap an outsider could still stop pairing and installs until it resets. The edge rule limits one address; only the paid plan covers a distributed flood. - The edge rule matches the host relay.vyre.run only.
workers_devis off in relay/worker/wrangler.toml, so the deployed Worker has no other public address; a staging copy of the Worker, or any other hostname, is not covered by it. -
CLOUDFLARE_WAF_TOKEN(Zone WAF Edit covers the whole zone) is an environment secret on the protecteddeployenvironment with approval, used only by relay-deploy.yml, never a repository-wide secret, and the deploy token does not carry that scope. - Release note, residuals: the edge rule blocks per address whatever the answer would be (a neighbour on the same address can still get it blocked, hits included); a flood from many addresses can still use up the account's Workers and Durable Object quota on the Free plan (a hit counts too), so the paid plan is required (bought 2 Oct 2026). The advisory stays private until released.