cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

release/portal-packaging
goq / docs / portal-release.md
portal-release.md 8.7K rendered
1# Portal public release runbook (issue #5)23This runbook completes the repository-controlled portion of4[issue #5](https://github.com/FelineStateMachine/goq/issues/5). The published5Portal download is **macOS arm64 only**.67Issue #5 asks for an explicit, recorded decision between arm64, x86_64, and8universal. The decision is **arm64-only, as separate-architecture builds rather9than a universal binary**. A universal DMG would double the download for every10user to serve a shrinking Intel population, and each additional published11architecture adds a notarization, Gatekeeper, and promotion path that must be12independently proven rather than assumed. macOS x86_64, Linux x86_64, and13Windows x86_64 are instead built and attested every release as unpublished14preview artifacts. Revisit this decision only with demand evidence, not by15default. See [Portal platform support](portal-platform-support.md) for the tier16definitions.1718Portal is always a compiled DMG download. There is no shell installer and no19fallback to an ad-hoc, demo, or development build.2021Portal is distributed **unsigned by Apple standards**: ad-hoc signed, not22notarized, not stapled, and not accepted by Gatekeeper without user action.23See the signing boundary in24[public release delivery](public-release-delivery.md) for why, and what25replaces Apple as the trust anchor.2627The release build uses no optional Cargo features. In particular,28`demo-direct-node` and `experimental-non-macos-pointer-capture` are forbidden29from the default feature set and from the packaging command. The latter exists30only to compile the unverified Tao + browser Pointer Lock path during31non-macOS platform UAT; see32[Portal platform support](portal-platform-support.md).3334## One-time GitHub setup3536Protect the GitHub environment named `main` with required reviewers and limit37deployment branches/tags according to the repository's release policy.3839The Portal publication job needs **no Apple secrets**, because Portal is not40Developer ID signed or notarized. The packaging gate actively refuses to run in41ad-hoc mode when any `APPLE_*` credential is present in the environment, so a42leaked or half-configured credential cannot silently produce a differently43signed artifact.4445The job does require `attestations: write` and `id-token: write`, since the46GitHub/Sigstore build-provenance attestation is what replaces Apple as the47trust anchor.4849## Prepare an exact tag5051Choose a SemVer tag such as `v0.1.0`. Before tagging, update every52workspace package version and `src-tauri/tauri.conf.json` to the tag's version.53The shared verifier rejects a dirty worktree, a tag not resolving exactly to54`HEAD`, any version mismatch, a non-arm64 target, or a Portal manifest that55enables `demo-direct-node` by default.5657Run the non-secret policy checks before pushing the tag:5859```bash60python3 scripts/verify-portal-release.py website \61 --manifest website/portal-release.json62./scripts/tests/portal-release.sh63./scripts/verify-website.sh64```6566Create and push the tag only from the reviewed clean release commit. Dispatch67the workflows from that tag ref, not from `main`; the Portal build refuses to68attest an input tag checked out by a different workflow ref. First run69the **Sigil Release** workflow with `build-candidate`; it creates the shared70draft release and attaches the two verified Sigil candidate assets. Then71manually run **Portal release** for the same tag. Both workflows share a72concurrency group, so their release mutations cannot overlap.7374## Publication transaction7576The workflow performs this fail-closed sequence:77781. Re-verifies clean tag, exact `HEAD`, workspace versions, Tauri version, and79 demo-feature policy on Linux.802. Uses a native `macos-26` arm64 runner and the protected `main` environment.813. Builds without feature flags in `--signing adhoc` mode, verifies the DMG,82 and requires the mandatory arm64 ad-hoc signature. It rejects the build if a83 certificate authority chain, an Apple TeamIdentifier, or a notarization84 ticket is present, so an ad-hoc release cannot quietly become something else.854. Confirms the executable contains only arm64, the bundle identifier is86 `sh.goq.portal`, no host or credential artifact is inside the bundle, and the87 DMG, digest, and JSON manifest agree with the source tag and commit. The88 manifest records `signing: "adhoc"` and `notarized: false`.895. Creates GitHub/Sigstore build-provenance attestations for the exact three90 Portal assets from the protected tag-ref workflow.916. Requires the existing draft to contain exactly the two Sigil candidate92 assets, then uploads exactly:9394 - `Portal-VERSION-arm64.dmg`95 - `Portal-VERSION-arm64.dmg.sha256`96 - `Portal-VERSION-arm64.json`97987. Reads GitHub's remote asset list and requires the exact five-file combined99 pre-signature set. It deliberately leaves the release as a draft.100101## Unpublished preview matrix102103The same workflow runs a `preview` job for macOS x86_64, Linux x86_64, and104Windows x86_64. It builds the exact release tag with no optional features,105records a `preview-manifest.json` with each artifact's SHA-256, attests the106build provenance, and uploads the result as the 14-day Actions artifact107`portal-preview-$tag-$target`.108109The job holds `contents: read` only. It cannot attach an asset to the release,110so the published contract stays exactly the three macOS arm64 files and the111Sigil promotion gate still sees its expected six-asset set. Preview builds run112with `fail-fast: false` and are not a dependency of `publish`; a Windows113bundler failure must never block a signed macOS release.114115Preview artifacts are unsigned. Do not publish them, link them, or hand them to116users as a download. Use them for the platform acceptance checklist in117[Portal platform support](portal-platform-support.md), and only from the tag's118own workflow run.119120The operator next attaches the offline Sigil `.minisig`. The **Sigil Release**121`promote-signed-draft` operation requires exactly all six Portal and Sigil122assets, re-verifies the Sigil signature/archive/provenance and Portal123digest/manifest against the same tag, verifies every Portal asset's attestation124against the exact Portal workflow, source tag, and source commit, mounts the125downloaded DMG on a native arm64 macOS runner, repeats the ad-hoc signature,126architecture, identifier, and payload checks, confirms no notarization ticket127appeared, and only then publishes the prerelease with final notes.128129If signing, upload, or remote verification fails, the shared release remains a130draft for diagnosis. Do not replace assets on an existing tag; fix the source,131delete an unpublished failed draft after review, and create a new version/tag132when provenance changed.133134For a local rehearsal on an arm64 Mac, use an empty directory outside the135repository. No Apple credentials are needed, and any that are set will be136rejected:137138```bash139scripts/package-macos-client.sh \140 --release-tag v0.1.0 \141 --signing adhoc \142 --output-dir /absolute/empty/release-directory143```144145## Enable the reviewed website download146147The checked-in `website/portal-release.json` starts with `available: false` and148contains no URL. After the prerelease is promoted:1491501. Download all three GitHub assets on a separate machine.1512. Verify the digest file against the DMG and inspect the JSON manifest.1523. Verify Gatekeeper again on the downloaded DMG.1534. Submit a normal reviewed change that replaces only the `macos-arm64` entry154 with `available: true` and these exact fields:155156```json157{158 "architecture": "arm64",159 "asset": "Portal-0.1.0-arm64.dmg",160 "available": true,161 "checksum_asset": "Portal-0.1.0-arm64.dmg.sha256",162 "checksum_url": "https://github.com/FelineStateMachine/goq/releases/download/v0.1.0/Portal-0.1.0-arm64.dmg.sha256",163 "download_url": "https://github.com/FelineStateMachine/goq/releases/download/v0.1.0/Portal-0.1.0-arm64.dmg",164 "manifest_asset": "Portal-0.1.0-arm64.json",165 "manifest_url": "https://github.com/FelineStateMachine/goq/releases/download/v0.1.0/Portal-0.1.0-arm64.json",166 "platform": "macos",167 "release_tag": "v0.1.0",168 "release_url": "https://github.com/FelineStateMachine/goq/releases/tag/v0.1.0",169 "sha256": "REPLACE_WITH_VERIFIED_LOWERCASE_SHA256",170 "verification": "adhoc-signed+github-attested+sha256",171 "version": "0.1.0"172}173```174175`scripts/verify-website.sh` validates the manifest and JavaScript gate before176the existing protected `main` website deployment. A missing or invalid177manifest, fetch failure, or unavailable entry leaves the anchor without an178`href` and explains that no signed download is offered.179180To withdraw a download, submit a reviewed manifest change back to:181182```json183{184 "available": false,185 "reason": "Signed Portal download temporarily unavailable."186}187```188189This disables the website link without substituting another artifact. GitHub190release deletion or withdrawal and website rollback are separate reviewed191operator actions.