apple-container: package Apple's container CLI + nix-darwin wiring with activation guards - #14
Merged
Conversation
Replace the home-manager module with a nix-darwin module that installs the package via environment.systemPackages and adds two activation guards: - preActivation: refuse to switch when a non-Nix 'container' install is detected (pkgutil receipt com.apple.container-installer, /usr/local/bin/container, or a Homebrew binary), with removal instructions. - postActivation: when the registered apiserver's baked CONTAINER_INSTALL_ROOT differs from the new store path, best-effort stop the old runtime in the primary user's launchd domain (launchctl asuser + sudo -u) and prompt to 'container system start'. All commands are ||-guarded so a failed stop never aborts the switch. The enable flag moves from home-manager.users.k to the system-level kriswill block. The CLI registers its services in user/<uid> (not gui/<uid>); the asuser bridge reaches that domain either way.
Code-review fixes: - version/hash as plain let bindings (ccglass parity; the override surface could only produce hash mismatches) - drop gzip from nativeBuildInputs (already in stdenv initialPath) - mv instead of cp -a in installPhase (no double-write of the payload) - trim wrapper rationale comments to pointers at the canonical README - guard the root re-export with optionalAttrs so adding a non-darwin system to the root systems list doesn't break eval
The module moves to flakes/apple-container/darwin-module.nix, exported as darwinModules.apple-container (alias: default); the in-tree module file is now a one-line re-export from inputs, and the overlay is gone — the module defaults kriswill.apple-container.package to the sub-flake's own package via self, so consumers need no pkgs wiring. Portability: plain mkEnableOption/mkOption instead of the repo-custom mkProgramOption. Also hardens the activation guards per code review: - Guard 1 moved to system.checks (darwin-rebuild check exercises it), consistent -x probes, ghost pkgutil receipts only warn, remediation labeled per install type incl. brew uninstall, and the error notes the profile already points at the new generation (rollback before reboot) - Guard 2 degrades loudly: warns when CONTAINER_INSTALL_ROOT can't be read (upstream layout drift) or the best-effort stop fails (headless/ SSH), resolves the user's home via dscl instead of hardcoding /Users, and registers system.requiresPrimaryUser for a friendly assertion Host system store path is unchanged by the module relocation.
kriswill
added a commit
that referenced
this pull request
Jun 29, 2026
nix-darwin has no programs.* for most of these tools, so port them the way
the rest of the repo already does (cf. tmux/zsh/yazi): static config lives in
the stow tree (home/) and the /nix/store-derived bits are generated and linked
during activation. The shell integrations (fzf/zoxide/direnv/hstr) already live
in the stow zshrc, so only the FZF_* env vars needed re-adding there.
Moved out of modules/home-manager/core.nix (now deleted) into system-level
modules/darwin/<feature>.nix + home/<pkg> stow packages:
git/gh modules/darwin/git.nix + home/git, home/gh (bare-name
helpers, no stale store paths)
ssh modules/darwin/ssh.nix + home/ssh
zk modules/darwin/zk.nix
diffnav modules/darwin/diffnav.nix installs a delta wrapped with the
kanagawa theme (matches HM's
finalPackage; diffnav bundles its
own delta and is unaffected)
kitty modules/darwin/kitty.nix + home/kitty
neovide modules/darwin/neovide.nix toggle only; pkg is per-host
direnv modules/darwin/direnv.nix + home/direnv; nix-direnv stdlib
linked into ~/.config/direnv/lib
direnv-nom modules/darwin/direnv-nom.nix nom wrapper generated + linked
htop modules/darwin/htop.nix immutable htoprc generated + linked
qmd-sqlite modules/darwin/qmd-sqlite.nix extension-enabled sqlite (hiPrio so
it wins the sqlite3 collision with
neovim's plain sqlite) + qmd link
bat jq nix-index lazygit rmpc fzf go nodejs_24 yamlfmt -> user-packages.nix
FZF_* env vars -> home/zsh/.config/zsh/.zshrc
The home-manager master `kriswill.enable` toggle and core.nix were removed;
home-manager stays wired only for the GUI/per-host modules (brave, firefox,
vscode, podman-desktop, claude-account-selector). All three host configs build
(nix flake check) and the HM->stow config handoff is clean (the old HM configs
were store symlinks HM removes on switch, then stow/activation redeploys).
flake.lock: `nix flake update` bumps nixpkgs, home-manager and yazi-plugins.
This is a closure no-op for host k -- the new lock evaluates to the identical
darwin-system derivation (verified: same .drv d15di5x4..., same output
axiznxg2...). It is included so future builds and the other hosts track the
newer inputs.
nvd diff -- previous generation (system-184, pre-migration) -> this commit's
closure (== the running gen-185 system). The Selection-state / Added / Removed
sections are this port (note the hm_* generated configs removed and the
delta-wrapped/delta-config -> delta-kanagawa swap); the Version changes are the
already-committed af761b8 nixpkgs bump that the pre-port generation had not yet
realized.
<<< /nix/store/7kx6ii60i6fwp4gsn4l987r9klgwd909-darwin-system-26.11.a1fa429
>>> /nix/store/axiznxg2fgby564shlpxjkjp208wa6p8-darwin-system-26.11.a1fa429
Version changes:
[U*] #1 buf 1.70.0 -> 1.71.0
[U.] #2 expat 2.8.0 -> 2.8.1
[U*] #3 fastfetch 2.64.2, 2.64.2-man -> 2.65.1, 2.65.1-man
[U.] #4 ffmpeg-headless 8.1-bin, 8.1-data, 8.1-lib -> 8.1.1-bin, 8.1.1-data, 8.1.1-lib
[U.] #5 fftw-double 3.3.10 -> 3.3.11
[U*] #6 fish 4.7.1, 4.7.1-doc -> 4.8.0, 4.8.0-doc
[U.] #7 freetype 2.14.2 -> 2.14.3
[U+] #8 gh 2.94.0 -> 2.95.0
[U*] #9 ghostscript 10.07.0, 10.07.0-fonts, 10.07.0-man -> 10.07.1, 10.07.1-fonts, 10.07.1-man
[U*] #10 go 1.26.3 -> 1.26.4
[U.] #11 icu4c 76.1, 76.1-dev -> 78.3, 78.3-dev
[U.] #12 ijs 10.07.0 -> 10.07.1
[U*] #13 imagemagick 7.1.2-23 -> 7.1.2-24
[U.] #14 just 1.51.0, 1.51.0-man -> 1.54.0, 1.54.0-man
[U.] #15 krb5 1.22.1-lib -> 1.22.2-lib
[U.] #16 libde265 1.0.18 -> 1.1.1
[U.] #17 libgcrypt 1.11.2-lib -> 1.12.2-lib
[U.] #18 libheif 1.21.2-lib -> 1.23.0-lib
[U.] #19 libpng-apng 1.6.56 -> 1.6.58
[U*] #20 libxml2 2.15.2, 2.15.2-bin -> 2.15.3, 2.15.3-bin
[U*] #21 lua-language-server 3.18.1 -> 3.18.2
[D.] #22 nix 2.34.7+1 x2 -> 2.34.7 x2
[D.] #23 nix-cmd 2.34.7+1 -> 2.34.7
[D.] #24 nix-expr 2.34.7+1 -> 2.34.7
[D.] #25 nix-fetchers 2.34.7+1 -> 2.34.7
[D.] #26 nix-flake 2.34.7+1 -> 2.34.7
[D.] #27 nix-main 2.34.7+1 -> 2.34.7
[D.] #28 nix-store 2.34.7+1 -> 2.34.7
[D.] #29 nix-util 2.34.7+1 -> 2.34.7
[U*] #30 nodejs 24.15.0 -> 24.16.0
[C.] #31 nodejs-slim 22.22.3, 24.15.0, 24.15.0-corepack, 24.15.0-npm -> 22.23.1, 24.16.0, 24.16.0-corepack, 24.16.0-npm
[U.] #32 openapv 0.2.1.2 -> 0.2.1.3
[U.] #33 openexr 3.4.10 -> 3.4.11
[U.] #34 podman 5.8.2, 5.8.2-man -> 5.8.3, 5.8.3-man
[U.] #35 publicsuffix-list 0-unstable-2026-03-26 -> 0-unstable-2026-05-13
[U.] #36 rsync 3.4.1 -> 3.4.4
[U*] #37 rumdl 0.2.16 -> 0.2.21
[U.] #38 simdjson 4.6.0 -> 4.6.4
[C*] #39 sqlite 3.51.2 x2, 3.51.2-bin x2, 3.51.2-dev, 3.51.2-man x2 -> 3.51.2 x2, 3.51.2-bin x2, 3.51.2-dev, 3.51.2-man
[U.] #40 unbound 1.25.0-lib -> 1.25.1-lib
[U.] #41 uv 0.11.19 -> 0.11.22
[D*] #42 vscode-langservers-extracted 4.10.0 -> 1.121.03429
Selection state changes:
[C+] #1 direnv 2.37.1
[C+] #2 git-lfs 3.7.1
[C+] #3 htop 3.5.1, 3.5.1-man
[C+] #4 kitty 0.47.4, 0.47.4-terminfo
Added packages:
[A+] #1 delta-kanagawa <none>
[A.] #2 delta-kanagawa.gitconfig <none>
[A.] #3 fastfetch-unwrapped 2.65.1, 2.65.1-man
[A.] #4 home-manager-agent-domains <none>
[A.] #5 org.nix-community.home.claude-config-dir.domain <none>
[A.] #6 zz-nom-wrapper.sh <none>
Removed packages:
[R.] #1 delta-config <none>
[R.] #2 delta-wrapped <none>
[R.] #3 direnv-config <none>
[R.] #4 empty-directory <none>
[R.] #5 gh-config.yml <none>
[R.] #6 hm_.sshconfig <none>
[R.] #7 hm_direnvlibzznomwrapper.sh <none>
[R.] #8 hm_gitallowed_signers <none>
[R.] #9 hm_gitconfig <none>
[R.] #10 hm_gitignore <none>
[R.] #11 hm_kanagawa.conf <none>
[R.] #12 hm_kittydiff.conf <none>
[R.] #13 hm_kittykitty.conf <none>
[R.] #14 yyjson 0.12.0
Closure size: 624 -> 616 (611 paths added, 619 paths removed, delta -8, disk usage -53.1MiB).
kriswill
added a commit
that referenced
this pull request
Jun 29, 2026
Completes the migration. The home-manager bridge, flake input, and the last runtime hook are gone; the repo is now pure nix-darwin + the GNU Stow tree under home/. - flake.nix: drop the home-manager input (flake.lock: removed the home-manager and home-manager/nixpkgs nodes). - modules/home.nix deleted -- the darwin <- home-manager bridge. - modules/darwin/core.nix: drop pkgs.home-manager from environment.systemPackages. - home/zsh/.config/zsh/.zshrc: drop the hm-session-vars.sh source line. - AGENTS.md / CLAUDE.md updated to describe a darwin-only repo (no home.nix, no modules/home-manager/, no mkOutOfStoreSymlink mechanism). Terminfo is unaffected -- the one real risk. nix-darwin's own set-environment already exports the full TERMINFO_DIRS (the Ghostty.app bundle, the per-user and system nix profiles, and /usr/share/terminfo), so xterm-kitty (in the system profile's share/terminfo) and xterm-ghostty (Ghostty's bundle) still resolve without the home-manager session-vars line. Verified with infocmp. nix flake check passes for k, mini, SOC. nvd diff -- previous generation (system-186, home-manager present) -> this commit (system-187, now active). Removes the entire home-manager apparatus plus the deps nothing else in the closure used (man-db, groff, diffutils, inetutils, libpipeline). <<< /nix/store/gf8hnm6w88ng2kajiy4x5n29k55nq0wz-darwin-system-26.11.a1fa429 >>> /run/current-system Removed packages: [R.] #1 activation-k <none> [R.] #2 check-link-targets.sh <none> [R.] #3 cleanup <none> [R.] #4 diffutils 3.12 [R.] #5 groff 1.24.1 [R.] #6 hm-modules-messages <none> [R.] #7 hm-session-vars.sh <none> [R.] #8 hm_LibraryFonts.homemanagerfontsversion <none> [R.] #9 hm_Usersk.cache.keep <none> [R.] #10 hm_Usersk.localstate.keep <none> [R-] #11 home-manager 0-unstable-2026-04-24 [R.] #12 home-manager-agent-domains <none> [R.] #13 home-manager-agents <none> [R.] #14 home-manager-applications <none> [R.] #15 home-manager-files <none> [R.] #16 home-manager-fonts <none> [R.] #17 home-manager-generation <none> [R.] #18 home-manager-path <none> [R.] #19 home-manager-source <none> [R.] #20 home-manager.sh <none> [R.] #21 inetutils 2.7 [R.] #22 libpipeline 1.5.8 [R.] #23 link <none> [R.] #24 man-db 2.13.1 [R.] #25 nixos-option <none> [R.] #26 nixos-option.nix <none> Closure size: 613 -> 587 (6 paths added, 32 paths removed, delta -26, disk usage -20.7MiB).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Packages Apple's native macOS
containerCLI (v1.0.0) as a self-owned sub-flake and wires it into the system through nix-darwin, with defensive activation guards.Commit 1 — package (
flakes/apple-container/).pkg(no source build — keeps Apple's code signature and entitlements intact viadontFixup).containerwithmakeWrapperso the CLI execs from the store path: upstream derives its read-only install root from its own executable path without resolving symlinks, so an unwrapped binary on a Nix profile can't find its plugins. The wrapper pins install-root to$out, which works identically from any profile.flakes/ccglass/extraction pattern: relative-path input, re-export inmodules/packages.nix, inline overlay inmodules/overlays.nix.Commit 2 — nix-darwin wiring (migrated off home-manager)
modules/darwin/apple-container.nix(kriswill.apple-container.enable), package viaenvironment.systemPackages.preActivation): refuses the switch when a non-Nixcontainerinstall is detected (pkgutil receiptcom.apple.container-installer,/usr/local/bin/container, or a Homebrew binary), printing removal instructions. Nix never writes those locations, so the signals are unambiguous.postActivation): when the registered apiserver's bakedCONTAINER_INSTALL_ROOTdiffers from the new store path, best-effort stops the old runtime in the primary user's launchd domain (launchctl asuser <uid> sudo -u <user> container system stop) and prompts tocontainer system start. All commands|| true-guarded so a failed stop never aborts the switch.Testing
nix flake checkgreen;nix build .#darwinConfigurations.k.systembuilds; both guards verified present in the generatedactivatescript.k:container --version→ 1.0.0;container system status→ running withinstallRooton the store path;container system stop/startround-trips cleanly throughuser/<uid>; end-to-endcontainer run --rm alpine echo …pulls, boots the VM (Kata kernel), and runs.