Release distribution
The tag workflow first reuses the complete CI workflow as a gate, then builds
the supported desktop and firmware artifacts, publishes signed updater metadata,
release notes, attestations, and SHA256SUMS.txt, and finally generates
package-manager manifests. The published checksum is the trust boundary:
installers fail closed when SHA256SUMS.txt or the exact asset entry is missing,
and manifests must quote the digest of the release asset rather than a digest
copied or recomputed by hand.
Desktop bundles carry no platform code signature — see Platform code signing below. That is a deliberate, permanent choice, and it is independent of the Tauri updater signature, which is mandatory.
Generated package-manager manifests
Current desktop artifacts are Linux .deb/.rpm/AppImage, Windows MSI/NSIS,
and macOS Apple Silicon/Intel .app.tar.gz archives. The release also includes
firmware output, updater signatures, latest.json, checksums, provenance, and
package metadata.
After a release, the workflow uploads these additional assets:
| Asset | Consumer | What to do |
|---|---|---|
ioruba.rb |
Homebrew | Automatically committed to bernardopg/homebrew-ioruba; users run brew tap bernardopg/ioruba && brew install --cask ioruba. |
ioruba.json |
Scoop | Automatically committed to bernardopg/scoop-ioruba; users run scoop bucket add ioruba https://github.com/bernardopg/scoop-ioruba && scoop install ioruba. |
BernardoGomes.Ioruba*.yaml (three files) |
winget | Attached to the release and submitted as a PR to microsoft/winget-pkgs, where Microsoft validates and reviews it. |
They are generated from the release API plus its SHA256SUMS.txt by
scripts/packaging/generate.mjs. The
pure renderer is covered by
scripts/packaging/manifests.test.mjs.
Do not edit digest, URL, or version by hand: regenerate from the release if a
mistake is found.
The Homebrew tap and Scoop bucket are public repositories owned by the project.
Each has an isolated write-only deploy key: the release workflow cannot use
one key to modify the other or the source repository. The known GitHub ED25519
host key is pinned; the workflow never trusts ssh-keyscan output.
winget uses Microsoft’s public community repository and requires its review. The initial submission is PR #415149. Future manifests remain attached to every release; fully automated upstream PRs would require a dedicated, least-privilege GitHub App or fine-grained token for the project fork, neither of which should be substituted by a broad personal credential.
Platform code signing: not used
This project does not hold an Apple Developer ID or a Windows Authenticode
certificate, and does not intend to acquire either. The release workflow has a
single unconditional build step with no signing branch, no certificate import,
and no APPLE_* / WINDOWS_CERTIFICATE* secrets.
Do not reintroduce conditional signing steps. The previous arrangement branched
on secrets.APPLE_CERTIFICATE != '', and when the certificate import steps were
removed in def4782 the branch survived while the variables it depended on
(WINDOWS_CERTIFICATE_THUMBPRINT, APPLE_SIGNING_IDENTITY) silently became
undefined — a workflow that appeared to sign and did not.
Consequences to keep documented for users:
| Platform | Effect | Mitigation shipped |
|---|---|---|
| Windows | SmartScreen shows “unrecognized app” on first run | README instructs More info → Run anyway; verify via SHA256SUMS.txt |
| macOS | Gatekeeper refuses an unsigned, unnotarized bundle | One-line installer and Homebrew cask run xattr -dr com.apple.quarantine |
| Linux | No effect — distro packaging carries its own trust | — |
Integrity is still verifiable, through two independent channels that do not require a paid certificate:
SHA256SUMS.txt, published per release and required by both installers.- GitHub artifact attestations:
gh attestation verify <asset> --repo bernardopg/ioruba.
.dmg stays disabled. Tauri’s DMG script asks Finder through AppleScript to lay
out the mounted image, and that operation is intermittently unauthorized on
GitHub-hosted macOS runners. Since a desktop bundle failure aborts the whole
release, .app.tar.gz is the reproducible artifact.
In-app updater
The updater is enabled and fail-closed. tauri-plugin-updater verifies every
candidate against the public key embedded in tauri.conf.json; the private key
and its password exist only as TAURI_SIGNING_PRIVATE_KEY and
TAURI_SIGNING_PRIVATE_KEY_PASSWORD repository secrets. The original encrypted
key pair is retained outside the repository at ~/.config/ioruba/updater/ with
mode 600 and must be backed up before the machine is retired — losing it would
strand installed clients on the current update chain.
Every platform build uploads a detached .sig. A separate job waits for the
whole build matrix, then creates exactly one latest.json from the complete set
of signed Linux and Windows artifacts (and macOS artifacts when present). This
avoids the known parallel-matrix race where several jobs delete and upload the
same latest.json. Missing signatures fail the release instead of publishing a
partial manifest.
The app checks this endpoint every six hours, offers an explicit Update and restart action, verifies the artifact before installing it, and relaunches only after a successful verification. Browser/dev mode keeps the informational GitHub release check; it never treats an arbitrary URL as an installable update.
For every tag, verify that the release contains latest.json and each required
.sig, then test an update from the previous Ioruba build on Linux and Windows.
Tauri updater signing is unrelated to platform code signing: the updater key is a project-owned minisign key, always present, and the absence of an Apple or Authenticode certificate does not weaken it.
The updater only offers itself where it can actually succeed. On Linux,
tauri-plugin-updater can replace an AppImage, .deb or .rpm, but a build
installed by a distro package manager (AUR, apt, dnf) lives under /usr and is
root-owned; attempting the swap downloads the whole artifact and then fails with
Permission denied (os error 13). is_managed_install detects that case and the
UI links to the release page instead of offering an install button.
AUR: the PKGBUILD must opt out of LTO
arch-pkgbuild in .github/workflows/release.yml generates the ioruba-desktop
PKGBUILD, which builds from source on the user’s machine. That PKGBUILD must
always carry options=('!lto' '!debug').
Arch’s stock /etc/makepkg.conf ships OPTIONS=(... lto) with
LTOFLAGS="-flto=auto", and makepkg injects that flag into CFLAGS. The cc
crate — used by the ring crate’s build.rs — then compiles ring’s C and
assembly sources into GIMPLE bitcode (.gnu.lto_* sections) instead of native
ELF objects. Cargo drives the final link through rust-lld, which cannot run
GCC’s LTO front-end, so every ring symbol comes back undefined:
rust-lld: error: undefined symbol: ring_core_0_17_14__x25519_sc_mask
rust-lld: error: undefined symbol: ring_core_0_17_14__OPENSSL_cpuid_setup
...
The linker is a red herring — forcing bfd via RUSTFLAGS reproduces the same
wall of errors. Only removing -flto from CFLAGS fixes it. Regressed in
1.8.0-1; fixed in 1.8.0-2.
When editing the published package directly, work in the AUR clone
(git push origin HEAD:master) and regenerate the metadata with
makepkg --printsrcinfo > .SRCINFO before committing. The -bin package
installs a prebuilt AppImage and is unaffected.