cat / goq
clone

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

fix/release-build-and-order
goq / docs / release-credentials.md
release-credentials.md 8.6K rendered
1# Release credential bring-up23Everything needed to publish goq is already built and already fails closed. The4only thing standing between this repository and a public release is that two5committed trust pins still read `unconfigured`:67| Pin | File | Becomes |8| --- | --- | --- |9| Sigil publisher key | `release/sigil-minisign.pub` | The offline Minisign public key |10| Sigil bootstrap channel | `website/install-sigil` | `publisher_key` and `release_tag` |1112Both are free. There is no third credential, because Portal is deliberately not13signed with an Apple Developer ID.1415This document is the operator path from those sentinels to a published16prerelease. It closes the credential half of17[issue #4](https://github.com/FelineStateMachine/goq/issues/4) and18[issue #5](https://github.com/FelineStateMachine/goq/issues/5).1920Read [Public release delivery](public-release-delivery.md) for the trust model21and [the Portal runbook](portal-release.md) for the publication transaction.2223## Read this ordering constraint first2425`scripts/verify-sigil-bootstrap.py` accepts exactly two states: fully closed, or26fully open. A commit that configures `release/sigil-minisign.pub` while27`website/install-sigil` still says `unconfigured` is rejected as "partially28configured", and the website gate fails.2930The bootstrap also pins one immutable `release_tag`. Combined with the31promotion workflow reading `release/sigil-minisign.pub` from the checked-out32tag, this means the release commit must already contain the real key and a33bootstrap pinned to the tag it is itself becoming.3435Pushing either path to `main` triggers a goq.sh deployment. So the release36commit is tagged and released **before** it is merged, or the public install37command goes live pointing at a release that does not exist yet. The sequence38in "First release" below does this in the correct order. Do not shortcut it.3940## Part A: the Sigil offline publisher key4142This key is the root of trust for every Bazzite install. It must be generated43and must stay on a machine that is not the release runner and never a CI44environment.4546```bash47minisign -G -p sigil-minisign.pub -s sigil-release.key48```4950Choose a strong passphrase and store it separately from the key file. Back up51`sigil-release.key` offline, in two physically separate locations. There is no52recovery path: losing it means every future release needs a new key and a53reviewed re-pin, and disclosing it means an attacker can sign a package that54the bootstrap will install without complaint.5556Prove the passphrase works before you depend on it:5758```bash59echo test > /tmp/sigcheck60minisign -Sm /tmp/sigcheck -s sigil-release.key61minisign -Vm /tmp/sigcheck -P "$(tail -1 sigil-minisign.pub)"62```6364`sigil-minisign.pub` is two lines, an `untrusted comment:` line and the base6465key. Both matter, in different places:6667- `release/sigil-minisign.pub` takes the file verbatim. The verifier tolerates68 the comment line and reads the key from the last non-empty line.69- `website/install-sigil` takes **only the base64 key** in70 `readonly publisher_key="..."`, with no comment line.7172The two must match exactly or the gate refuses to open the channel. The key73decodes to exactly 42 bytes beginning `Ed`; anything else is rejected as74malformed.7576Record the key fingerprint and publish it through a channel independent of the77website, so a compromised site cannot also redefine what "the real key" is.7879## Part B: no Apple credentials8081Portal ships as an ad-hoc signed, un-notarized DMG. There is no Apple Developer82Program membership, no Developer ID certificate, and no notarization83credential, and none of them are required to cut a release.8485The packaging gate **refuses** to build in ad-hoc mode if any of86`APPLE_SIGNING_IDENTITY`, `APPLE_CERTIFICATE`, `APPLE_API_KEY`,87`APPLE_API_ISSUER`, `APPLE_API_KEY_PATH`, `APPLE_ID`, or `APPLE_PASSWORD` is88set. Do not add them to the `main` environment "just in case".8990What replaces Apple as the trust anchor:9192- a GitHub/Sigstore build-provenance attestation bound to the exact workflow,93 tag ref, and source commit;94- the published SHA-256 beside the DMG;95- a release manifest that records `signing: "adhoc"` and `notarized: false`.9697The cost is real and is stated on the website and in the release notes: macOS98blocks Portal's first launch, and the user must allow it under System Settings,99Privacy and Security.100101If a membership is ever purchased, `--signing developer-id` still implements the102full Developer ID path, and `docs/public-release-delivery.md` records what has103to be restored. That branch is not exercised by CI; re-validate it end to end104before trusting it.105106## Part C: the protected GitHub environment107108Create an environment named `main` with required reviewers and restrict its109deployment branches and tags to the release policy.110111It needs **no Apple secrets**. The website deployment needs112`CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`; scope the token to Workers113deployment on the goq.sh account only.114115No Minisign secret belongs here, or in any other GitHub environment. The116promotion workflow has no input capable of receiving it, and that is deliberate.117118## First release, in order119120The tag is `v0.1.0`, matching the version already in every workspace manifest.1211221. **Prepare one reviewed release commit** on a branch. It replaces123 `release/sigil-minisign.pub` with the real public key file and opens124 `website/install-sigil` with the base64 `publisher_key` and125 `readonly release_tag="v0.1.0"`. No version bump is needed.126127 Verify locally before pushing:128129 ```bash130 python3 scripts/verify-sigil-bootstrap.py \131 --bootstrap website/install-sigil \132 --public-key-file release/sigil-minisign.pub133 ./scripts/verify-website.sh134 ./scripts/tests/portal-release.sh135 ```136137 Expect `sigil_bootstrap_channel=open`.1381392. **Tag that exact commit and push the tag. Do not merge to `main` yet.**140 Merging now would deploy an install command pointing at a release that does141 not exist.1421433. **Run Sigil Release with `build-candidate`** against the tag. It creates the144 draft and attaches the unsigned candidate archive and its checksum.1451464. **Run Portal release** against the same tag. It builds, verifies, and147 attests the ad-hoc macOS arm64 assets, and separately builds and attests the148 unpublished preview matrix. The draft stays a draft.149150 Signing comes after this, not before. The Portal job refuses to start unless151 the draft holds *exactly* the two Sigil candidate assets, and it names its152 own result the five-asset **pre-signature** contract. Attaching the153 `.minisig` first makes that precondition fail.1541555. **Sign offline.** Move the candidate bytes to the publisher machine,156 independently confirm the tag commit, then:157158 ```bash159 scripts/sign-bazzite-release.sh \160 --tag v0.1.0 \161 --archive /absolute/offline/path/sigil-v0.1.0-linux-glibc2.17-x86_64.tar.gz \162 --source-commit "$tag_commit" \163 --minisign-key /absolute/offline/path/sigil-release.key \164 --public-key-file /absolute/offline/path/sigil-minisign.pub165 ```166167 Carry only the resulting `.minisig` back and attach it to the draft, taking168 the draft from five assets to six. Never use `--clobber`.1691706. **Run Sigil Release with `promote-signed-draft`.** It requires the exact171 six-asset set, re-verifies both products against the same tag and commit,172 and publishes the prerelease.1731747. **Verify as a stranger would**, on a different machine: download the DMG,175 check the digest, run `gh attestation verify`, confirm the Gatekeeper prompt176 behaves as documented, and run the public install command on clean Bazzite.1771788. **Merge the release commit to `main`.** This deploys goq.sh with the install179 command open against the now-published tag.1801819. **Enable the Portal download** with the follow-up reviewed change to182 `website/portal-release.json` described in183 [the Portal runbook](portal-release.md). It needs the published DMG's184 verified SHA-256, so it can only happen after step 6.185186Every subsequent release repeats steps 1 to 9, except that the key is already187correct and only `release_tag` in `website/install-sigil` moves.188189## Deliberately not automated190191- The Minisign secret key never enters CI. Signing is a manual offline step.192- Opening or moving the public install channel is a reviewed website commit.193- Enabling the Portal download link is a separate reviewed commit made only194 after an operator independently verifies the published artifact.195- Preview matrix artifacts are never published. See196 [Portal platform support](portal-platform-support.md).197198If a step fails, the release stays a draft. Fix the source, discard the199unpublished draft after review, and use a new version and tag; do not replace200assets on a tag that anyone may already have fetched.