0019: Distributing the CLI: release binaries, Windows zips and WinGet¶
Date: 2026-10-12 · Status: accepted
Context¶
Tenax runs in Kubernetes, but people use the tenax CLI from their own machines, many of them on Windows. Releases
published binaries for Linux and macOS only, and the CLI did not build for Windows: internal/proc ran children in
Unix process groups.
Decision¶
- Windows builds. On Windows a child the CLI starts (
cloudflaredfortenax sandbox connect) gets its own console process group, and stopping it ends its whole process tree withtaskkill /T /F; Windows has no group signals. The controller and data movers stay Linux-only. CI builds and vets the CLI for Windows on every change. - Release assets. Every release carries
tenax-<os>-<arch>for Linux and macOS,tenax-windows-<arch>.exeandtenax-windows-<arch>.zip(tenax.exeandLICENSE.txt) for amd64 and arm64, the chart, andSHA256SUMS. The zips are reproducible (fixed timestamps,hack/release/mkzip), so their checksums depend only on the binaries. - Public downloads, private source. Tenax's repository is private, so its release downloads are too, and package
managers download anonymously. Each release copies the CLI downloads (binaries, Windows zips,
LICENSE,SHA256SUMS; no source, no chart) to a public repository that holds nothing else (vars.TENAX_PUBLIC_REPO, defaultrepublic-global/tenax-cli, written withsecrets.TENAX_PUBLIC_RELEASES_TOKEN). Without the token the release stays private and no package is built. - WinGet downloads from the public release. The WinGet manifests (
TheRepublic.Tenax, portable zip) point at its zips and pin their SHA-256 (hack/release/packages.sh); their metadata links only to the public repository. WinGet is built into Windows 10 and 11, so users need no extra tool. - The release job fills the manifests on Linux and attaches them to the private release as
tenax-winget-manifests.zip. A maintainer submits them from a Windows machine (GitHub-hosted Windows runners are not available to us), as below. Pre-releases (v1.2.3-rc1) are never submitted. - Not now. Chocolatey (it moderates every new version by hand, and packing it needs a Windows runner), a Scoop bucket and a Homebrew tap; code signing (Authenticode) needs a certificate. They can follow without changing the release assets.
Submitting a release to WinGet¶
From PowerShell on Windows, with the manifests zip from the release unpacked (<dir> is
manifests\t\TheRepublic\Tenax\<version>):
winget validate --manifest <dir>
winget settings --enable LocalManifestFiles # once, as administrator
winget install --manifest <dir>; tenax version # install it exactly as users will
winget install Microsoft.WingetCreate # once
wingetcreate submit --token <GitHub token, classic, public_repo scope> <dir>
wingetcreate opens a pull request on microsoft/winget-pkgs; Microsoft's bots validate it and the first version
also gets a moderator review. Once it merges, winget install TheRepublic.Tenax installs that version.
Consequences¶
winget install TheRepublic.Tenaxworks once a version's pull request onwinget-pkgsmerges; each release needs one manual submission until a Windows runner can do it.- Unsigned executables can trigger SmartScreen warnings on first run until a signing certificate is added.
- Publishing needs the public repository and its token; without them a release still succeeds and stays private.
The token used for
wingetcreateonly opens the pull request on Microsoft's publicwinget-pkgs; WinGet never needs access to the source.