Release matrix: Linux, Windows, macOS and FreeBSD

Drop android (same binary as linux/arm64), big-endian ppc64 (cannot be
verified: even hello-world crashes under qemu-ppc64 with the cross
toolchain), NetBSD (cross 0.2.5's sysroot lacks libexecinfo), illumos,
Solaris and WASI, as agreed. docs/PLATFORMS.md records each decision.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Andy Dixon
2026-10-08 12:40:30 +01:00
co-authored by Claude
parent 7506c1dbef
commit 25496ddb97
6 changed files with 56 additions and 93 deletions
+6 -7
View File
@@ -68,21 +68,20 @@ jobs:
run: |
rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl i686-unknown-linux-musl \
arm-unknown-linux-musleabihf armv7-unknown-linux-musleabihf riscv64gc-unknown-linux-musl \
loongarch64-unknown-linux-musl powerpc64le-unknown-linux-musl powerpc64-unknown-linux-gnu \
s390x-unknown-linux-gnu x86_64-pc-windows-gnu i686-pc-windows-gnu aarch64-pc-windows-gnullvm wasm32-wasip1 \
x86_64-unknown-freebsd i686-unknown-freebsd x86_64-unknown-netbsd x86_64-unknown-illumos x86_64-pc-solaris
loongarch64-unknown-linux-musl powerpc64le-unknown-linux-musl \
s390x-unknown-linux-gnu x86_64-pc-windows-gnu i686-pc-windows-gnu aarch64-pc-windows-gnullvm \
x86_64-unknown-freebsd i686-unknown-freebsd
- name: build
run: |
# Everything but macOS, which the test job builds on a Mac.
TARGETS="linux/amd64 linux/arm64 linux/386 linux/armv6 linux/armv7 linux/riscv64 linux/loong64 \
linux/ppc64le linux/ppc64 linux/s390x android/arm64 windows/amd64 windows/386 windows/arm64 \
freebsd/amd64 freebsd/386 netbsd/amd64 illumos/amd64 solaris/amd64 wasip1/wasm" ./build.sh
linux/ppc64le linux/s390x windows/amd64 windows/386 windows/arm64 freebsd/amd64 freebsd/386" ./build.sh
- name: run builds under qemu, cross-checked with the Go oracle
run: |
sudo apt-get update -q && sudo apt-get install -yq qemu-user libc6-s390x-cross libc6-ppc64-cross
sudo apt-get update -q && sudo apt-get install -yq qemu-user libc6-s390x-cross
(cd oracle/go && go build -o ../../target/vault-go .)
for t in "arm64 qemu-aarch64" "armv7 qemu-arm" "riscv64 qemu-riscv64" "ppc64le qemu-ppc64le" \
"s390x qemu-s390x -L /usr/s390x-linux-gnu" "ppc64 qemu-ppc64 -L /usr/powerpc64-linux-gnu"; do
"s390x qemu-s390x -L /usr/s390x-linux-gnu"; do
EMU="${t#* }" scripts/smoke.sh "dist/vault-linux-${t%% *}" target/vault-go
done
- uses: actions/upload-artifact@v4
+2 -4
View File
@@ -37,7 +37,7 @@ TARGETS="linux/amd64 windows/amd64" ./build.sh
`build.sh` cross-compiles with `cargo-zigbuild` and `zig` (Linux, statically
linked with musl, and Windows), on a Mac for macOS, and with `cross` and
Docker for the BSDs, illumos and Solaris. It names binaries as before
Docker for FreeBSD. The release covers Linux, Windows, macOS and FreeBSD. It names binaries as before
(`vault-linux-arm64`, `vault-windows-amd64.exe`, …), builds 32-bit ARM Linux
for both armv6 and armv7, and writes `dist/SHA256SUMS`. `docs/PLATFORMS.md`
lists every target, how it is built and tested, and the Go targets Rust
@@ -103,9 +103,7 @@ Commands that change the vault take an exclusive lock on `DIR/.lock`, and
commands that read it take a shared one. A second process waits, and says
so, until the first finishes. The operating system releases the lock if a
process dies; the locks are the same ones the Go version used, so old and
new versions exclude each other. WebAssembly (WASI) has no file locks, so
there the lock file itself is the lock. After a crash there you remove it
by hand; the error message names it.
new versions exclude each other.
Adding a name that already exists replaces it. `get` never replaces an
existing file (or symlink) and writes nothing until the whole file has
+9 -21
View File
@@ -9,16 +9,13 @@
# release builds from the pinned toolchain (rust-toolchain.toml) and the
# locked dependencies (Cargo.lock), with build paths remapped so the output
# does not depend on where the tree lives. Linux binaries are static (musl)
# except s390x and ppc64, which link glibc dynamically; static linking is
# except s390x, which links glibc dynamically; static linking is
# checked. SHA256SUMS is written next to the binaries.
#
# How each target is built, and what the host needs:
# zig cargo-zigbuild and zig on PATH (Linux, Windows GNU ABI)
# mac a Mac with Xcode (macOS)
# cross `cross` and Docker (FreeBSD, NetBSD, illumos, Solaris,
# and big-endian ppc64, which zig
# links for the wrong ABI)
# cargo nothing extra (WASI)
# cross `cross` and Docker (FreeBSD)
# A requested target the host cannot build is reported, the others are still
# built, and the script exits non-zero. See docs/PLATFORMS.md for the Go
# targets with no Rust equivalent; asking for one is an error.
@@ -40,9 +37,7 @@ table() {
linux/riscv64 riscv64gc-unknown-linux-musl zig
linux/loong64 loongarch64-unknown-linux-musl zig
linux/ppc64le powerpc64le-unknown-linux-musl zig
linux/ppc64 powerpc64-unknown-linux-gnu cross
linux/s390x s390x-unknown-linux-gnu zig
android/arm64 aarch64-unknown-linux-musl zig
windows/amd64 x86_64-pc-windows-gnu zig
windows/386 i686-pc-windows-gnu zig
windows/arm64 aarch64-pc-windows-gnullvm zig
@@ -50,19 +45,15 @@ table() {
darwin/arm64 aarch64-apple-darwin mac
freebsd/amd64 x86_64-unknown-freebsd cross
freebsd/386 i686-unknown-freebsd cross
netbsd/amd64 x86_64-unknown-netbsd cross
illumos/amd64 x86_64-unknown-illumos cross
solaris/amd64 x86_64-pc-solaris cross
wasip1/wasm wasm32-wasip1 cargo
EOF
}
# Go targets that Rust cannot build with a stable toolchain (no prebuilt
# standard library, or no Rust target at all). See docs/PLATFORMS.md.
unsupported="aix/ppc64 dragonfly/amd64 freebsd/arm freebsd/arm64 freebsd/riscv64 js/wasm
linux/mips linux/mipsle linux/mips64 linux/mips64le netbsd/386 netbsd/arm netbsd/arm64
openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 openbsd/ppc64 openbsd/riscv64
plan9/386 plan9/amd64 plan9/arm"
# Go-era targets no longer released (the release covers Linux, Windows,
# macOS and FreeBSD). See docs/PLATFORMS.md for why each was dropped.
unsupported="aix/ppc64 android/arm64 dragonfly/amd64 freebsd/arm freebsd/arm64 freebsd/riscv64
illumos/amd64 js/wasm linux/mips linux/mipsle linux/mips64 linux/mips64le linux/ppc64
netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm
openbsd/arm64 openbsd/ppc64 openbsd/riscv64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 wasip1/wasm"
out=${OUT:-dist}
jobs=${JOBS:-1}
@@ -89,7 +80,7 @@ build_one() {
row=$(table | awk -v t="$want" '$1 == t')
if [[ -z $row ]]; then
if grep -qw -- "$want" <<<"$unsupported"; then
echo "UNSUPPORTED $want: no stable Rust target; see docs/PLATFORMS.md" >&2
echo "UNSUPPORTED $want: not released; see docs/PLATFORMS.md" >&2
else
echo "UNKNOWN $want" >&2
fi
@@ -98,7 +89,6 @@ build_one() {
read -r _ triple method <<<"$row"
os=${want%/*}
[[ $os == windows ]] && ext=.exe
[[ $os == wasip1 ]] && ext=.wasm
name=vault-${want/\//-}$ext
tdir=$root/target
[[ $JOBS_N != 1 ]] && tdir=$CARGO_TARGET_DIR_BASE/$triple
@@ -114,7 +104,6 @@ build_one() {
command -v cross >/dev/null && command -v docker >/dev/null ||
{ echo "SKIPPED $want: needs cross and docker" >&2; return 1; }
cmd=(cross build) ;;
cargo) cmd=(cargo build) ;;
esac
local flags=$RUSTFLAGS
# zig's musl getrandom() never reaches the kernel on riscv64 (it spins);
@@ -126,7 +115,6 @@ build_one() {
return 1
fi
bin=$tdir/$triple/release/vault$ext
[[ $os == wasip1 ]] && bin=$tdir/$triple/release/vault.wasm
cp -- "$bin" "$OUT_DIR/$name"
if [[ $triple == *-musl* ]] && command -v file >/dev/null; then
file -L "$OUT_DIR/$name" | grep -q 'static' || { echo "FAILED $want: not statically linked" >&2; return 1; }
+2 -2
View File
@@ -92,8 +92,8 @@ Command line:
- `ls`/`list` piped into a reader that stops early end quietly with
status 141, as Go's did by `SIGPIPE`.
Platforms: see `PLATFORMS.md`. Some targets Go built have no stable Rust
equivalent; they are listed there and need a decision before release.
Platforms: releases now cover Linux, Windows, macOS and FreeBSD (16
binaries, down from 43); `PLATFORMS.md` lists what was dropped and why.
## Unchanged on purpose
+27 -45
View File
@@ -1,8 +1,8 @@
# Platforms
The Go version was built for every target of `go tool dist list` that needs
no cgo: 43 binaries. Rust supports fewer operating systems and CPUs with a
prebuilt, stable standard library. This is the target-by-target record:
no cgo: 43 binaries. The Rust release is narrower by decision (Linux,
Windows, macOS, FreeBSD): 16 binaries. This is the target-by-target record:
how each is built, and how far it has actually been verified. "Runtime" means
a binary was executed; "build" means it only compiled and linked.
@@ -14,7 +14,10 @@ x86-64. `scripts/smoke.sh` is the runtime check: create a key and vault, store
files across object boundaries, read them back, then have the Go oracle read
what that binary wrote and the binary read what the oracle wrote.
## Supported
## Released
The release covers Linux, Windows, macOS and FreeBSD (a decision made during
the port: the Go release's more exotic targets are no longer built).
| Go name | Rust target | Linking | Built by | Verified |
| --- | --- | --- | --- | --- |
@@ -23,63 +26,42 @@ what that binary wrote and the binary read what the oracle wrote.
| linux/386 | i686-unknown-linux-musl | static | zig | runtime: smoke test under qemu-i386, cross-checked |
| linux/armv6 | arm-unknown-linux-musleabihf | static | zig | runtime: smoke test under qemu-arm, cross-checked |
| linux/armv7 | armv7-unknown-linux-musleabihf | static | zig | runtime: smoke test under qemu-arm, cross-checked |
| linux/riscv64 | riscv64gc-unknown-linux-musl | static | zig | runtime: smoke test under qemu-riscv64, cross-checked; unit tests under qemu (see note 1) |
| linux/riscv64 | riscv64gc-unknown-linux-musl | static | zig | runtime: smoke test and crypto unit tests under qemu-riscv64, cross-checked (note 1) |
| linux/loong64 | loongarch64-unknown-linux-musl | static | zig | runtime: smoke test under qemu-loongarch64, cross-checked |
| linux/ppc64le | powerpc64le-unknown-linux-musl | static | zig | runtime: smoke test under qemu-ppc64le, cross-checked |
| linux/s390x | s390x-unknown-linux-gnu | glibc, dynamic | zig | runtime: smoke test under qemu-s390x (big-endian) with an Ubuntu glibc sysroot, cross-checked |
| linux/ppc64 | powerpc64-unknown-linux-gnu | glibc, dynamic | cross (Docker) | see note 2 |
| android/arm64 | aarch64-unknown-linux-musl | static | zig | build only on Android; it is the linux/arm64 binary (see note 3) |
| windows/amd64 | x86_64-pc-windows-gnu | static CRT | zig | build; tests cross-compile cleanly; runtime in CI only (windows-2025 job, not yet run) |
| windows/386 | i686-pc-windows-gnu | static CRT | zig | build only |
| windows/arm64 | aarch64-pc-windows-gnullvm | static CRT | zig | build only |
| darwin/amd64 | x86_64-apple-darwin | system | a Mac (Xcode) | CI only (macos-15-intel job, not yet run) |
| darwin/arm64 | aarch64-apple-darwin | system | a Mac (Xcode) | CI only (macos-15 job, not yet run) |
| freebsd/amd64 | x86_64-unknown-freebsd | system | cross (Docker) | see note 4 |
| freebsd/386 | i686-unknown-freebsd | system | cross (Docker) | see note 4 |
| netbsd/amd64 | x86_64-unknown-netbsd | system | cross (Docker) | see note 4 |
| illumos/amd64 | x86_64-unknown-illumos | system | cross (Docker) | see note 4 |
| solaris/amd64 | x86_64-pc-solaris | system | cross (Docker) | see note 4 |
| wasip1/wasm | wasm32-wasip1 | n/a | cargo | build only; no agent, no terminal prompts (passphrase from the environment), lock file instead of locks |
| freebsd/amd64 | x86_64-unknown-freebsd | system | cross 0.2.5 (Docker) | build only (no emulator) |
| freebsd/386 | i686-unknown-freebsd | system | cross 0.2.5 (Docker) | build only |
Feature notes: the `unlock` agent works on Unix only (Linux, macOS, the BSDs,
illumos, Solaris); Windows asks for the passphrase every time. Full agent
hardening (`PR_SET_DUMPABLE`) is Linux-only; elsewhere the agent locks its
memory and disables core dumps. See `SECURITY.md`.
Feature notes: the `unlock` agent works on Linux, macOS and FreeBSD;
Windows asks for the passphrase every time. Full agent hardening
(`PR_SET_DUMPABLE`) is Linux-only; elsewhere the agent locks its memory and
disables core dumps. See `SECURITY.md`.
1. zig's bundled musl `getrandom()` never reaches the kernel on riscv64 (the
process spins). `build.sh` and `packaging/package.sh` build that target
with `--cfg getrandom_backend="linux_raw"`, which makes the `getrandom`
crate issue the system call itself; with that flag the binary passes.
2. zig links big-endian ppc64 for the ELFv2 ABI (`/lib64/ld64.so.2`), but
big-endian glibc distributions use ELFv1 (`ld64.so.1`), so the zig build
would not start there. `build.sh` builds it with `cross` (GCC toolchain)
instead.
3. Go's android/arm64 binary was a static Linux binary used in Termux; this
is the same. Name resolution for `add URL`/`mirror` reads
`/etc/resolv.conf`, which Android does not have, so those two commands
may fail to resolve hosts there; everything else is unaffected.
4. Built with `cross` 0.2.5 images; not executed (no emulator). The
dependency graph supports these systems (rustix, ring, getrandom).
## Not supported
## No longer released
These Go targets have no Rust target with a prebuilt standard library on the
stable toolchain (Rust tier 3, built only with nightly `-Zbuild-std`, or no
Rust target at all). Building them would need a nightly toolchain and
untested standard libraries, which this project does not ship.
| Go name | Rust status |
| Go name | Why |
| --- | --- |
| aix/ppc64 | tier 3 (powerpc64-ibm-aix) |
| dragonfly/amd64 | tier 3 |
| freebsd/arm, freebsd/arm64, freebsd/riscv64 | tier 3 |
| netbsd/386, netbsd/arm, netbsd/arm64 | tier 3 |
| openbsd/386, amd64, arm, arm64, ppc64, riscv64 | tier 3 (OpenBSD packages Rust itself; building in an OpenBSD VM would work) |
| linux/mips, mipsle, mips64, mips64le | tier 3 since Rust 1.72 |
| plan9/386, amd64, arm | no Rust target |
| js/wasm | wasm32-unknown-unknown has no filesystem or OS access; not meaningful for vault |
| android/arm64 | was a copy of the static Linux arm64 binary; that binary still runs in Termux, but `add URL`/`mirror` need `/etc/resolv.conf` |
| linux/ppc64 (big-endian) | zig links it for the ELFv2 loader that BE distributions lack; built with `cross` instead, even a hello-world crashes at start-up under qemu-ppc64, so it cannot be verified |
| linux/mips, mipsle, mips64, mips64le | Rust tier 3 (no stable std) since 1.72 |
| netbsd/amd64 | builds only with a newer sysroot than cross 0.2.5 provides (std needs `libexecinfo`); netbsd/386, arm, arm64 are tier 3 |
| openbsd/* | Rust tier 3; OpenBSD packages Rust itself, so a native build there would work |
| freebsd/arm, arm64, riscv64 | Rust tier 3 |
| dragonfly/amd64, aix/ppc64 | Rust tier 3 |
| illumos/amd64, solaris/amd64 | outside the chosen matrix (illumos builds with cross; Solaris has no cross 0.2.5 image) |
| wasip1/wasm | outside the chosen matrix (it builds; no agent, no prompts) |
| js/wasm | no filesystem in the browser target |
| plan9/* | no Rust target |
Dropping these is a scope change from the Go release (43 binaries) and needs
an explicit decision before a release: either accept the narrower matrix,
or add nightly/native builds for chosen targets (OpenBSD in a VM is the most
practical).
Asking `build.sh` for any of these reports it and fails.
+10 -14
View File
@@ -221,16 +221,15 @@ Linux PC or server & almost everything & vault-linux-amd64 \\
Linux on ARM64 & Pi 4/5 (64-bit OS), Graviton, Ampere & vault-linux-arm64 \\
Raspberry Pi, 32-bit & Pi 2, 3, 4 on a 32-bit OS & vault-linux-armv7 \\
Raspberry Pi Zero / 1 & the tiny older ones & vault-linux-armv6 \\
Android & in Termux & vault-android-arm64 \\
FreeBSD, NetBSD & amd64 (FreeBSD also 386) & vault-freebsd-amd64 etc. \\
Something exotic & RISC-V, POWER, s390x, LoongArch, illumos, Solaris & see \cmd{dist/} \\
Android & in Termux: the Linux ARM64 file & vault-linux-arm64 \\
FreeBSD & amd64 or 386 & vault-freebsd-amd64 etc. \\
Something exotic & RISC-V, POWER (little-endian), s390x, LoongArch & see \cmd{dist/} \\
\end{vtable}
\begin{squirrel}
The release covers some twenty platforms, from Windows on ARM to IBM
mainframes, and even WebAssembly. A few that the older Go builds reached,
such as Plan~9, OpenBSD and MIPS, have no stable Rust toolchain yet;
\file{docs/PLATFORMS.md} keeps the list. Will you ever need
The release covers sixteen builds for Linux, Windows, macOS and FreeBSD,
from Windows on ARM to IBM mainframes. The older Go builds reached further,
to Plan~9 and WebAssembly; \file{docs/PLATFORMS.md} says why those went. Will you ever need
\file{vault-linux-s390x}? Almost certainly not. Is it pleasing that it
exists? Extremely.
\end{squirrel}
@@ -290,8 +289,8 @@ so Gatekeeper quarantines it. Once you've checked the SHA-256, clear the flag:
\subsection{Android (Termux)}
\begin{shell}
$ curl -LO https://.../vault-android-arm64
$ chmod +x vault-android-arm64 && mv vault-android-arm64 $PREFIX/bin/vault
$ curl -LO https://.../vault-linux-arm64
$ chmod +x vault-linux-arm64 && mv vault-linux-arm64 $PREFIX/bin/vault
\end{shell}
\section{Did it work?}
@@ -311,8 +310,7 @@ $ ./build.sh # every platform it can, into ./dist, with SH
\end{shell}
\cmd{build.sh} cross-compiles Linux and Windows with \cmd{cargo-zigbuild}
and \cmd{zig}, macOS on a Mac, and the BSDs, illumos and Solaris with
\cmd{cross} and Docker; it says which targets your machine can't build.
and \cmd{zig}, macOS on a Mac, and FreeBSD with \cmd{cross} and Docker; it says which targets your machine can't build.
32-bit ARM Linux gets two builds, armv6 and armv7. Want just a few? Set
\env{TARGETS}. Want them somewhere else? Set \env{OUT}.
@@ -1080,8 +1078,7 @@ who's slow at typing never blocks you.
\textbf{What if vault crashes while holding the lock?} On Linux, macOS, the
BSDs and Windows, the operating system drops the lock the moment the process
dies. Nothing to clean up. Older Go builds of vault use the very same locks,
so mixing versions is safe. On WebAssembly, which has no file locks, vault
uses a \file{.lock} file instead. If one's ever left behind,
so mixing versions is safe. If one's ever left behind,
vault tells you after a minute exactly which file to delete.
\end{whyso}
@@ -1335,7 +1332,6 @@ could not read passphrase (set VAULT\_PASSPHRASE\ldots) & No terminal to ask on,
agent did not start: \ldots & \cmd{unlock} couldn't start its helper. The reason follows; commands still work, asking for the passphrase. \\
nothing was unlocked & \cmd{vault lock} found nothing to lock. Not an error. \\
waiting for another vault process to finish & Not an error. Another command is writing; this one continues when it's done. \\
vault still locked after a minute\ldots & WebAssembly only. If nothing else is running, delete the file it names. \\
cannot lock the vault: \ldots & The lock file couldn't be opened or locked; the reason follows. Check the vault folder's permissions. \\
\ldots\ changed while being added & The file was being written while vault read it. Nothing was stored; try again when it's finished. \\
\ldots\ changed after it was stored; not deleting it & \cmd{migrate} stored the file, but it changed before it could be deleted. The original is kept; the vault has the version it read. \\