a73x

298bebbf

docs: the release is one tarball per OS

a73x   2026-09-04 12:50

Commit message
docs: the release is one tarball per OS

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SakwJEwD9dXBoRP5kWbemW

CLAUDE.md
Old New
@@ -29,6 +29,8 @@ make provision-mac # clone, boot and key a macOS guest on a tart host
29 make install # the one user binary -> ~/.local/bin, ReleaseSafe 29 make install # the one user binary -> ~/.local/bin, ReleaseSafe
30 # MUX_TARGET follows the HOST: a Mac installs a Mac 30 # MUX_TARGET follows the HOST: a Mac installs a Mac
31 # binary, because that is the only one it can link 31 # binary, because that is the only one it can link
32 make release release-mac # one tarball per OS into dist/, cut on that OS;
33 # release-mac runs `make release` on $MAC_BUILDER
32 make xversion-build xversion # cross-version gate; XVER_OLD_WORKTREE points at the old checkout (default ..) 34 make xversion-build xversion # cross-version gate; XVER_OLD_WORKTREE points at the old checkout (default ..)
33 ``` 35 ```
34 36
README.md
Old New
@@ -25,9 +25,9 @@ that runs on any x86_64 Linux:
25 ~/Downloads/zig-x86_64-linux-0.15.2/zig build -Dtarget=x86_64-linux-musl 25 ~/Downloads/zig-x86_64-linux-0.15.2/zig build -Dtarget=x86_64-linux-musl
26 ``` 26 ```
27 27
28 `make release` does the whole release build instead — static musl, 28 `make release` does the whole release build instead — static musl on
29 `ReleaseSafe`, stripped, checked against `build.zig`'s version, and tarred 29 Linux, `ReleaseSafe`, stripped, checked against `build.zig`'s version, and
30 into `dist/`. The tarball is what to hand another machine, because it 30 tarred into `dist/`. The tarball is what to hand another machine, because it
31 carries the exec bit that a bare downloaded binary does not: 31 carries the exec bit that a bare downloaded binary does not:
32 32
33 ```sh 33 ```sh
@@ -35,6 +35,19 @@ make release
35 ssh HOST 'mkdir -p ~/.local/bin && tar xzf - -C ~/.local/bin' < dist/mux-vVERSION-x86_64-linux-musl.tar.gz 35 ssh HOST 'mkdir -p ~/.local/bin && tar xzf - -C ~/.local/bin' < dist/mux-vVERSION-x86_64-linux-musl.tar.gz
36 ``` 36 ```
37 37
38 A release is one tarball per OS, named for the target it holds:
39 `mux-vVERSION-x86_64-linux-musl.tar.gz` and
40 `mux-vVERSION-aarch64-macos.tar.gz`. Each is cut on that OS — `make release`
41 builds for the host it runs on — and both are published under
42 `https://git.a73x.sh/mux/releases/vVERSION/`, so installing a published
43 build is one line:
44
45 ```sh
46 mkdir -p ~/.local/bin
47 curl -fsSL https://git.a73x.sh/mux/releases/vVERSION/mux-vVERSION-x86_64-linux-musl.tar.gz | tar xzf - -C ~/.local/bin
48 curl -fsSL https://git.a73x.sh/mux/releases/vVERSION/mux-vVERSION-aarch64-macos.tar.gz | tar xzf - -C ~/.local/bin
49 ```
50
38 On an Apple-silicon Mac the same build and install commands work, with two 51 On an Apple-silicon Mac the same build and install commands work, with two
39 things to install first and one binary you cannot cross-compile — see 52 things to install first and one binary you cannot cross-compile — see
40 [macOS](#macos). 53 [macOS](#macos).
@@ -728,6 +741,10 @@ make build # first build fetches + compiles the QUIC deps, as on Linu
728 make install # an aarch64 binary in ~/.local/bin 741 make install # an aarch64 binary in ~/.local/bin
729 ``` 742 ```
730 743
744 The Mac tarball of a release is cut by `make release-mac`, which runs
745 `make release` on the Mac named by `MAC_BUILDER` — the same box, and the
746 same variable, the macOS gates build on — and publishes it from there.
747
731 `make build` runs `deps/mac-sdk.sh` first, which may build a shadow SDK 748 `make build` runs `deps/mac-sdk.sh` first, which may build a shadow SDK
732 under `deps/mac-sdk/`. Xcode 26.4 and later ship a `libSystem` stub that 749 under `deps/mac-sdk/`. Xcode 26.4 and later ship a `libSystem` stub that
733 lists only the `arm64e` slice, and the pinned Zig 0.15.2 matches the bare 750 lists only the `arm64e` slice, and the pinned Zig 0.15.2 matches the bare
docs/decisions.md
Old New
@@ -8833,3 +8833,67 @@ clone-and-boot leg left with the VM lifecycle, and what replaced it is the
8833 preflight line, which is not an `ok`. The user's real Mac is no longer a 8833 preflight line, which is not an `ok`. The user's real Mac is no longer a
8834 gate target at all; it can still be `MAC_BUILDER`, which is the one role 8834 gate target at all; it can still be `MAC_BUILDER`, which is the one role
8835 that touches nothing. 8835 that touches nothing.
8836
8837 ## 2026-09-04 — a release is one tarball per OS, and a Mac cuts the Mac one
8838
8839 `RELEASE_TARGET` now follows the host, the way `MUX_TARGET` already did.
8840 That reverses the rule the Makefile carried until today, which was that what
8841 a release IS must not follow the host that cuts it, and the reversal is
8842 worth stating because the old rule was right when it was written.
8843
8844 It was written when there was one release, a Linux one, and it guarded
8845 against one accident: `MUX_TARGET` had just been hoisted to the top of the
8846 Makefile behind a `uname -s` switch, which put `make release` on a Mac one
8847 `?=` away from building a Mach-O binary and shipping it as the Linux
8848 tarball. What removes that danger is not the rule but the NAME. The target
8849 is in the filename, so a Mac cuts `mux-vN-aarch64-macos.tar.gz` and a Linux
8850 box cuts `mux-vN-x86_64-linux-musl.tar.gz`, and no step downstream — the
8851 publish, the release listing, the README's install line, `mux d upgrade`'s
8852 stream — can take one for the other. The old rule's second argument, that
8853 macOS ships no `sha256sum`, was never an argument about what a release is;
8854 it is a spelling, and `SHA256` is `shasum -a 256` on Darwin now.
8855
8856 The reason to reverse it is that mux runs on two operating systems, and
8857 there is no cross-compiling the Mac binary: zig 0.15.2 asks `xcrun` for the
8858 SDK only for a NATIVE target, so `-Dtarget=aarch64-macos` gets no system
8859 include path and `@cImport` of `<util.h>` fails. So the Mac release exists
8860 only if a Mac cuts it. `RELEASE_BUILD_TARGET` is the second variable that
8861 falls out of this: `native` is the only `-Dtarget=` word that builds on a
8862 Mac, and `aarch64-macos` is the only name the artifact may carry, so the
8863 name and the build word are separate variables that happen to be the same
8864 string on Linux. `make release` on Linux is byte-for-byte the recipe it was.
8865
8866 `make release-mac` (tools/release-mac.sh) is how the Mac tarball gets cut
8867 from a Linux box. It refuses unless the tag exists locally AND points at
8868 HEAD — a release is cut from a tag, never a branch tip — pushes exactly that
8869 one tag (`--no-follow-tags` and an explicit refspec: a developer with
8870 `push.followTags = true`, which is a common global setting, otherwise sends
8871 every reachable annotated tag, measured as five extra tags in the hand
8872 check), and then hands ONE remote shell script to `MAC_BUILDER`. That
8873 script fetches the tag as a tag, detaches onto it, runs `make release` and
8874 publishes with the builder's own `git-collab`. The publish is the builder's
8875 because the builder is the box holding the bytes; nothing here runs
8876 git-collab locally, and nothing here touches main.
8877
8878 `MAC_BUILDER` has no default, and the refusal says so. A release published
8879 to whatever machine happened to be in someone's ssh config is worse than a
8880 refusal, and the variable is the same one the macOS gates take, so the
8881 builder is a box that is already known to build this repo.
8882
8883 The builder needs three one-time arrangements beyond what the gates need,
8884 all of them on the builder itself: its checkout's `origin` must be the real
8885 remote (a gate-only checkout can have been cloned from anywhere), and it
8886 needs `git-collab init` and `git-collab init-key` so it has an identity to
8887 publish under. That key is then trusted from the developer's own box with
8888 `git-collab key add --global --label BUILDER`; without the trust step the
8889 builder's publish is refused for the identity, which reads nothing like a
8890 build failure.
8891
8892 One outcome to expect rather than debug: a publish of a version that
8893 already exists is REFUSED, and that is correct. The answer is to download
8894 the published tarball from `https://HOST/mux/releases/vN/FILE`, extract it
8895 and `cmp` the BINARY against the local one — the gzip wrapper never matches,
8896 because it carries an mtime — and never `--force` on the strength of a
8897 matching sha alone. That rule was established at v0.0.1-16 (2026-08-29),
8898 where a publish was refused against an identical binary built 76 seconds
8899 earlier and the download-and-cmp is what established it was identical.