Skip to content

feat(darwin): activate the homes and hjem configurations inside a machine - #105

Merged
clhodapp merged 2 commits into
mainfrom
feat/darwin-children
Oct 17, 2026
Merged

clhodapp merged 2 commits into
mainfrom
feat/darwin-children

Conversation

@clhodapp

@clhodapp clhodapp commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

A macOS machine now activates the homes and hjem configurations declared inside it, as a NixOS machine does.

This PR carries #95 onto main. #95 merged into feat/hjem after #94 had already merged feat/hjem into main, so its two commits, 182fb7e and bde5418, are on feat/hjem and feat/darwin-children but not on main. This PR has the same branch and the same commits, with main as the base. The rest of #95's description follows unchanged.

The machine of a home

A home or hjem configuration used to read its machine as nearest.nixos. The new caisson.integrations.nearestOf [ "nixos" "darwin" ] returns the nearer of the nearest NixOS configuration and the nearest darwin configuration. The nearer one is the one with more ancestors. A home inside a darwin machine gets osConfig, osClass = "darwin" and darwinConfig, as home-manager's nix-darwin module gives them, and it is published as <user>@<machine>. Only a NixOS machine gives a hjem configuration hjem's systemd.nix, because hjem's nix-darwin module does not import it. Both kinds of machine give osOptions.

caisson's default darwin module

A darwin configuration had no default module. The new one does three things:

  • Through caisson.forChildren, it gives each home the home-manager module darwin-parent and each hjem configuration the hjem module darwin-parent. They set what home-manager's and hjem's nix-darwin modules set from the machine's account: the home directory, the user ID read with tryEval as home-manager reads it, and the Nix of the machine.
  • It imports home-manager-activation. That module adds the postActivation step of home-manager's nix-darwin module, which runs each home's activate as its user through launchctl asuser and sudo. As in caisson's NixOS activation, activate runs without a driver version, so the user's home-manager profile keeps working with home-manager switch.
  • It imports hjem-activation. That module writes the two launchd agents of hjem's nix-darwin module, org.hjem.activate and org.nix.link-nix-apps, with upstream's scripts. It also adds the users' packages and records their extraDependencies in /etc/hjem/extra-dependencies.

The hjem module differs from upstream in two places.

  1. nix-darwin installs launchd.user.agents into the home of system.primaryUser alone, so upstream's agent runs for the primary user only. Upstream also requires system.primaryUser to be set. caisson declares the agents in launchd.agents, which nix-darwin installs in /Library/LaunchAgents. launchd loads agents from there for every user at login. Each agent exits for a user that no hjem configuration of the machine names.
  2. nix-darwin's activation script runs a fixed list of steps (modules/system/activation-scripts.nix). Upstream restarts the activation agent from a step named hjem-activate-kick, which is not on that list, so the restart never runs. It restarts the applications agent from applications, which runs before the launchd step installs the new agents. caisson restarts both agents from postActivation, which runs after the agents are installed.

Tests

  • The darwin probe declares a second machine, family, with a home and a hjem configuration for its user. The bare machine still builds exactly the derivation darwinSystem builds.
  • A new pinned-world test checks four things about family:
    • Both configurations take their directory, and the home its uid, from the account.
    • postActivation runs the home's activation wrapper through launchctl asuser and restarts org.hjem.activate.
    • The agent sits in launchd.agents, and launchd.user.agents is empty.
    • The hjem manifest equals what hjem's nix-darwin module makes from the same user module, and the configurations are published as probe@family and probe.
  • checks.aarch64-darwin.darwin-system now builds both systems, so the macOS job builds the home's activation package and hjem's CLI.
  • A unit test covers nearestOf.

Activation on a running Mac is not tested.

Caisson's evaluation overhead in the eval-weight check is 4.18 MB, against 4.15 MB on #94, and the ceiling is 4.28 MB. All 18 workspace consumers pass nix flake check --no-build at #96, which contains this branch. That run puts the branch in place of both caisson pins of each consumer: the root input, and the tests/dependencies lock that the checks partition reads. An earlier run on this branch alone overrode only the root input, so those partitions then ran against the caisson they pin.

🤖 Generated with Claude Code

clhodapp and others added 2 commits October 17, 2026 00:57
…hine

A home or hjem configuration now takes as its machine the nearest NixOS
or darwin configuration above it, found with the new
`caisson.integrations.nearestOf`. A home inside a darwin machine gets
`osClass = "darwin"` and `darwinConfig`, and is published as
`<user>@<machine>`.

caisson's new `default` darwin module gives each home the home-manager
module `darwin-parent` and each hjem configuration the hjem module
`darwin-parent`, which set the options that come from the machine's
account. It imports `home-manager-activation`, which activates the
homes in `postActivation` as home-manager's nix-darwin module does, and
`hjem-activation`, which writes the launchd agents of hjem's nix-darwin
module. The agents are in `/Library/LaunchAgents`, so they run for
every user and need no `system.primaryUser`, and they are restarted
from `postActivation`, after nix-darwin installs them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The applications agent of `hjem-activation` started with the case that
sets the CLI of the user, which that agent does not run, and shellcheck
refused the unused variable when the system was built on macOS. It now
starts with a case that only exits for a user no hjem configuration
here names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@clhodapp clhodapp self-assigned this Oct 17, 2026
@clhodapp
clhodapp merged commit 44cfa4a into main Oct 17, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant