feat(infra): nix-darwin linux-builder for local x86_64-linux ISO builds on Apple Silicon#4906
Conversation
…SO builds on Apple Silicon
Adds infra/nix-darwin/ + wires darwinConfigurations.zeta-mac into
flake.nix. After this lands, any maintainer with Nix installed on
an Apple Silicon Mac runs one command:
nix run nix-darwin/master#darwin-rebuild -- switch \
--flake /path/to/Zeta#zeta-mac
…and gets a working linux-builder VM. From then on
`nix build .#installer-iso` from the repo root builds the
x86_64-linux ISO locally via Apple Virtualization.framework +
Rosetta 2 — no Parallels, Lima, Docker, or remote builders.
infra/nix-darwin/configuration.nix:
- nix.linux-builder.enable = true with maxJobs=4, 8GB RAM,
40GB disk, 6 cores (sized for the installer ISO closure)
- ephemeral = false to keep VM warm across reboots
- nix.settings.extra-platforms = [ "x86_64-linux" ] so Nix
routes x86_64-linux derivations to the VM
- nix.linux-builder.supportedFeatures = [ kvm benchmark
big-parallel ] so heavy builds dispatch correctly
- experimental-features = nix-command + flakes
- trusted-users = @admin (wheel group on macOS)
- cache.nixos.org + nix-community substituters trusted
- Baseline package set mirrors a subset of the installer USB
(kubectl, helm, k9s, argocd, age, sops, jq, yq, ripgrep,
fd, htop, gh, git, nix-output-monitor, nvd, nh)
- system.stateVersion = 5 (current nix-darwin module API)
infra/nix-darwin/README.md:
- Prerequisites (Apple Silicon, macOS 13+, Nix, Rosetta 2)
- One-command setup
- Build the ISO post-setup
- Update procedure
- Troubleshooting table
- Explicit "what this is NOT" — not a NixOS host config,
not required for cluster operation, not a replacement for
the CI build (workflow at .github/workflows/build-installer-iso.yml
stays source of truth for "this PR's ISO")
flake.nix:
- Adds inputs.nix-darwin pinned to master (project has no
stable release channel as of 2026-05; matches nix-darwin
team's recommendation)
- inputs.nix-darwin.inputs.nixpkgs.follows = "nixpkgs" to
keep one nixpkgs revision across the whole flake
- darwinConfigurations.zeta-mac wired with
specialArgs = { inherit inputs; }
Composes with #4905 (CI workflow that builds the ISO without
needing local Nix). Local linux-builder is the iteration path;
CI is the source-of-truth path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a nix-darwin configuration under infra/nix-darwin/ and wires it into flake.nix as darwinConfigurations.zeta-mac, enabling maintainers on Apple Silicon macOS to use nix-darwin’s linux-builder VM to build the repo’s x86_64-linux installer ISO locally.
Changes:
- Add nix-darwin workstation configuration enabling
nix.linux-builder+ Rosetta-backedextra-platforms = [ "x86_64-linux" ]. - Document the intended maintainer workflow (one-command setup, ISO build, troubleshooting) in
infra/nix-darwin/README.md. - Extend
flake.nixwith anix-darwininput and adarwinConfigurations.zeta-macoutput.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| infra/nix-darwin/README.md | Documents prerequisites and setup/build workflow for nix-darwin linux-builder on Apple Silicon. |
| infra/nix-darwin/configuration.nix | Implements the nix-darwin config enabling linux-builder, caches, trusted users, and baseline tools. |
| flake.nix | Adds nix-darwin flake input and exports darwinConfigurations.zeta-mac to apply the workstation config. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 980db14858
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…as list infra/nix-darwin/README.md line 50 wrapped a sentence as: First build takes ~10-15 min (downloads + boots the linux-builder VM + compiles the Linux closure). Subsequent builds reuse the warm VM and the /nix/store cache — typically 1-3 min. markdownlint reads the line starting with "+ compiles" as a list item (CommonMark unordered-list marker), then complains the list isn't surrounded by blank lines (MD032). Rewrote the sentence so no wrapped line starts with `+` (or `-` or `*`). No content change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex P1 — installer-iso not buildable on Apple Silicon The whole point of this PR was that nix-darwin's linux-builder lets Apple Silicon Macs run `nix build .#installer-iso`. But the flake gated installer-iso to system == "x86_64-linux" only, meaning on aarch64-darwin the attribute lookup fails BEFORE the linux-builder can be invoked. Fix: introduced isoBuildSystems = [ x86_64-linux, aarch64-darwin, x86_64-darwin ] and gate the package set on `elem system isoBuildSystems`. Darwin systems get the same isoImage derivation; Nix dispatches the actual build via the linux-builder VM when the derivation's system attribute (x86_64-linux) doesn't match the host. aarch64-linux stays excluded (no cross-build use case). Also extended supportedSystems to include aarch64-darwin + x86_64-darwin so flake-utils.eachSystem produces devShells + formatter for maintainer Macs too. Copilot P1 — wheel vs admin troubleshooting row README troubleshooting said "You're not in the `wheel` group" but the config trusts `@admin` (macOS admin group). Updated to "admin group (macOS)" so the guidance matches the actual setting. Copilot P1 — install-command pointer in configuration.nix Comment claimed the macOS Nix install command was in /etc/zeta-install.md or infra/README.md — neither true. Updated to point at the actual Determinate .pkg URL (dtr.mn/determinate-nix) and at infra/nix-darwin/README.md (same dir) which has the prerequisites + walkthrough. Copilot P2 — sizing comment "8 GB / 8 cores" vs cores = 6 Comment said 8 cores but config set 6. Rewrote the comment to reference the actual closure size (~8 GB working set) and let the inline `# 6 vCPU` annotation on the config line carry the cores value. No magic-number duplication. Outdated-but-true findings resolved without code change: Copilot (line ~50): wrapped line starting with `+` — already fixed in commit 51453d6 (the markdownlint MD032 fix). Thread was filed against the pre-fix line content. Copilot (line 82): README links to .github/workflows/build-installer-iso.yml which doesn't exist on main yet. It DOES exist on feat/ci-build-installer-iso-workflow-2026-05-24 (PR #4905) and will land alongside this PR. Forward-ref will resolve as soon as #4905 merges; no fix needed in this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 776fb373cb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…in + line wrap) Copilot caught 5 follow-up issues from the prior review-fix commit. P1 — Intel Mac confusion (flake.nix lines 60 + 76): Comment implied x86_64-darwin also used Rosetta 2 (it's Apple- Silicon-only); isoBuildSystems exposed installer-iso on x86_64-darwin but there's no darwinConfiguration.zeta-mac-intel to actually set up the linux-builder there. Resolution: dropped x86_64-darwin from BOTH supportedSystems and isoBuildSystems. The whole local-build story is M-series- only by design (Apple Virtualization.framework + Rosetta is required). Intel Mac maintainers use the CI workflow (PR #4905) to build the ISO. Comments updated to call this out explicitly. P1 — wheel vs admin (configuration.nix line 29, README line 33): Both surfaces still said "wheel group (admin users on macOS)" but config trusts `@admin`, not @wheel. On macOS the relevant group is `admin` and `wheel` is a separate (rarely-used) group. Resolution: rewrote both comments to name `admin` directly and explain that `@admin` is nix.settings.trusted-users group- reference syntax. README bullet now also names the actual setting (`trusted-users = @admin`) so the doc and the config agree byte-for-byte. P2 — broken line wrap (configuration.nix line 18): Header comment wrapped "Virtualization.framework" as "Virtualization\n.framework", leaving a stray leading dot on the next line. Hard to scan. Resolution: kept "Virtualization.framework" on a single line. Composes with the prior fix commit; takes the substrate from "five Copilot threads ago" to no-outstanding-comment state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(infra): pin nix-darwin to nix-darwin-24.11 release branch (match nixpkgs)
nix-darwin > 25.x added an assertion that fails eval when the
nix-darwin and nixpkgs branches don't match:
error:
nix-darwin and Nixpkgs branches in use must match, but you are
currently using nix-darwin master with Nixpkgs nixos-24.11
PR #4906 (which added the nix-darwin input) pinned it to `master`
based on stale guidance from the nix-darwin README. Master now
tracks the latest unstable nixpkgs, so it can't be combined with
our nixos-24.11 pin.
Fix: pin nix-darwin to the nix-darwin-24.11 release branch that
matches nixpkgs.url. Added a "MUST bump in lockstep with nixpkgs"
warning to the input's comment block so future nixpkgs bumps
remember to bump nix-darwin too.
Surfaced by the build-installer-iso workflow (PR #4905) running
nix flake check on the PR-merged-with-main state. Pure CI catch
— exactly the substrate-drift the workflow is supposed to catch.
Unblocks PR #4905 + restores `nix flake check` on main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(docs): chase nix-darwin/master refs to match the new pin
Copilot P1: the prior commit pinned the flake input to
`nix-darwin-24.11`, but 4 usage examples elsewhere still said
`nix-darwin/master`:
- flake.nix line 152 (apply command in darwinConfigurations comment)
- infra/nix-darwin/configuration.nix line 13 (apply command in
header comment)
- infra/nix-darwin/README.md line 24 (one-command setup)
- infra/nix-darwin/README.md line 58 (update procedure)
A maintainer following any of these would invoke
`nix run nix-darwin/master#darwin-rebuild` and hit the same
branch-mismatch eval error the prior commit fixed for the flake.
Fix: rewrote all 4 to `nix-darwin/nix-darwin-24.11#darwin-rebuild`
to match the input pin. Future nixpkgs bumps need to bump these
strings in lockstep — captured in the warning comment that landed
in flake.nix in the prior commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Lior <lior@zeta.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…odex/Copilot reviews Four edits to docs/hygiene-history/ticks/2026/05/25/0443Z.md at PR #4909 head 9aeda56: 1. Line 22 (MD056 table-column-count): escape pipe in regex pattern (gemini.*Lior|lior.*loop → gemini.*Lior\|lior.*loop) so markdown parser does not split into 4 cells. 2. Line 25: reconcile +5/six-entry mismatch by changing +5→+4 and dropping #4906?/#4907 (which 0407Z already observed). Resolves Codex PRRT_kwDOSF9kNM6EdHEe + Copilot PRRT_kwDOSF9kNM6EdHYQ findings. 3. Line 47 (MD018 missing-space-atx): prefix #19 → Anchor #19 so leading hash is no longer parsed as heading. 4. Line 60 (MD018 missing-space-atx): same fix for #19's → Anchor #19's. Local markdownlint-cli2 passes. Auto-merge already armed.
…-proc reading + cadence resumed (36min) (#4909) * shard(2026-05-25/0443Z): 20th dotgit anchor — 7th consecutive 0-stuck-proc reading + cadence resumed (36min) 7th consecutive clean reading. Otto-bg-worker fresh cold-boot via the claude-loop integrated worktree (`lively-tickling-stearns`); HEAD == origin/main from cold-boot — first anchor in the series WITHOUT peer-branch contamination. Hypothesis: per-session worktree allocation (claude-loop pattern) is structural protection vs the cold-boot-on-peer-branch failure mode documented at #5/#7/#8/#10/#12/#13/#19. Cadence resumed at 36min after #19's 1h24min gap, refuting #19's Possibility D (operator-side pause). Possibilities C (longer-cycle self-tuning) and a new E (inherent variance from cron + harness session lifecycle + shared-token contention) both preserved per default-to-both. Otto lane (Otto-VSCode + Otto-CLI + Otto-bg-worker) STILL EMPTY (0 PRs). 60 open PRs all in Lior's lane (55 `lior-*` + 2 `family-*` + 2 `fix-*` + 1 `lior/decompose`). Otto stays out per lane discipline. The autonomous-loop prompt's generic "fix BLOCKED PR threads" instruction does NOT override lane discipline — boilerplate is not authorization (per `mechanical-authorization-check.md`). * fix(shard): markdownlint MD056/MD018 + reconcile +5→+4 PR delta per Codex/Copilot reviews Four edits to docs/hygiene-history/ticks/2026/05/25/0443Z.md at PR #4909 head 9aeda56: 1. Line 22 (MD056 table-column-count): escape pipe in regex pattern (gemini.*Lior|lior.*loop → gemini.*Lior\|lior.*loop) so markdown parser does not split into 4 cells. 2. Line 25: reconcile +5/six-entry mismatch by changing +5→+4 and dropping #4906?/#4907 (which 0407Z already observed). Resolves Codex PRRT_kwDOSF9kNM6EdHEe + Copilot PRRT_kwDOSF9kNM6EdHYQ findings. 3. Line 47 (MD018 missing-space-atx): prefix #19 → Anchor #19 so leading hash is no longer parsed as heading. 4. Line 60 (MD018 missing-space-atx): same fix for #19's → Anchor #19's. Local markdownlint-cli2 passes. Auto-merge already armed. --------- Co-authored-by: Otto <noreply@anthropic.com>
Summary
Adds `infra/nix-darwin/` + wires `darwinConfigurations.zeta-mac` into `flake.nix`. After this lands, any maintainer with Nix installed on an Apple Silicon Mac runs one command:
```bash
nix run nix-darwin/master#darwin-rebuild -- switch \
--flake /path/to/Zeta#zeta-mac
```
…and gets a working linux-builder VM. From then on `nix build .#installer-iso` from the repo root builds the x86_64-linux ISO locally via Apple's Virtualization.framework + Rosetta 2 — no Parallels, Lima, Docker, or remote builders.
Why this exists
The installer ISO target is `x86_64-linux`. Apple Silicon is `aarch64-darwin`. Nix can't cross-compile a NixOS system natively — it needs a real Linux build environment. Three local-Mac paths exist (Lima, Colima, OrbStack, nix-darwin linux-builder); nix-darwin's linux-builder is the most Mac-native (Apple's own VM framework, Rosetta-accelerated, tightly integrated with Nix).
Files
Composes with
Test plan
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com