From 3c87e62948d7d4d8a4fb5c0300e4774b634d8c40 Mon Sep 17 00:00:00 2001 From: Chris Hodapp Date: Sat, 17 Oct 2026 10:50:08 -0700 Subject: [PATCH 1/2] feat(system-manager): activate the homes and hjem configurations declared inside a machine A system-manager configuration now acts as the parent of the configurations declared inside it, as a NixOS or darwin machine does. Its new registered `default` module imports: - `home-manager-activation` and `hjem-activation`, the NixOS modules registered a second time for the `systemManager` class. They import the core module of the class they are evaluated in, and the hjem module keeps extra dependencies only where the class declares `system.extraDependencies`. - `caisson.forChildren` settings that give homes and hjem configurations the `system-manager-parent` module, the `nixos-parent` module under a second name, which reads the nearest NixOS or system-manager machine. system-manager counts as a machine for naming homes (`user@machine`), for the `osConfig` and `utils` a hjem configuration receives, and for the check that leaves out what an inner machine activates. A pinned-world test declares a home and a hjem configuration inside a system-manager machine and checks the units, the packages and the published home name. Co-Authored-By: Claude Opus 5.5 --- README.md | 4 +- lib-overlays/hjem/default.nix | 28 ++++++--- lib-overlays/home-manager/default.nix | 4 +- modules/darwin/hjem-activation/default.nix | 11 +++- .../home-manager-activation/default.nix | 9 ++- modules/hjem/nixos-parent/default.nix | 37 +++++++----- modules/hjem/system-manager-parent | 1 + modules/homeManager/nixos-parent/default.nix | 24 +++++--- modules/homeManager/system-manager-parent | 1 + modules/nixos/hjem-activation/default.nix | 51 +++++++++++++---- .../nixos/home-manager-activation/default.nix | 49 ++++++++++++---- .../systemManager/default/caisson/default.nix | 13 +++++ .../default/caisson/forChildren.nix | 33 +++++++++++ modules/systemManager/default/default.nix | 36 ++++++++++++ modules/systemManager/hjem-activation | 1 + modules/systemManager/home-manager-activation | 1 + tests/pinned-world/default.nix | 57 +++++++++++++++++++ 17 files changed, 299 insertions(+), 61 deletions(-) create mode 120000 modules/hjem/system-manager-parent create mode 120000 modules/homeManager/system-manager-parent create mode 100644 modules/systemManager/default/caisson/default.nix create mode 100644 modules/systemManager/default/caisson/forChildren.nix create mode 100644 modules/systemManager/default/default.nix create mode 120000 modules/systemManager/hjem-activation create mode 120000 modules/systemManager/home-manager-activation diff --git a/README.md b/README.md index 8baee178..0a25670c 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ source by. | `nixos-minimal` | `nixpkgs` | the `nixos` class under the minimal evaluator (no NixOS base modules); a second integration over the class `nixos` owns | | `home-manager` | `home-manager` | Home Manager configurations | | `home-manager-minimal` | `home-manager` | the `homeManager` class with Home Manager's necessary modules alone, the configuration importing what it uses from `modulesPath`; a second integration over the class `home-manager` owns | -| `hjem` | `hjem` | the files and packages of a user, as hjem manifests; activated by the NixOS configuration they are declared inside, or applied with `hjem standalone` | +| `hjem` | `hjem` | the files and packages of a user, as hjem manifests; activated by the NixOS, darwin or system-manager configuration they are declared inside, or applied with `hjem standalone` | | `terranix` | `terranix` | Terraform configurations written as terranix modules, published as `terranixConfigurations` of the `config.tf.json` files Terraform reads | | `caisson-colmena` | `colmena` | colmena configurations whose machines are NixOS configurations declared inside them, published as the hive colmena's command reads; the colmena integration we recommend for new configurations | | `colmena` | `colmena` | a hive written for colmena, taken as it is, with each node a NixOS configuration; for porting an existing hive and the modules written for it | @@ -138,7 +138,7 @@ source by. | `topology` | `nix-topology` | the diagram of the NixOS configurations declared inside it, as nix-topology renders it; the configuration named `default` is published as `topology.` | | `kubenix` | `kubenix` | Kubernetes resources written as kubenix modules, published as `kubenixConfigurations..`, the evaluation, whose `config.kubernetes.result` is the JSON `kubectl apply -f` reads | | `nixidy` | `nixidy` | an Argo CD environment written as nixidy modules, evaluated with nixidy's `mkEnv` and published as the `nixidyEnvs..` the nixidy CLI reads | -| `system-manager` | `system-manager` | the systems of machines running another Linux distribution, published as the `systemConfigs` that `system-manager switch` reads | +| `system-manager` | `system-manager` | the systems of machines running another Linux distribution, published as the `systemConfigs` that `system-manager switch` reads; they activate the homes and hjem configurations declared inside them | | `structural` | none | a configuration that only exports and holds configurations beneath it: the top of a repository whose point is what it exports, such as caisson itself, or a layer at any depth | Each integration finds its ecosystem in this order: the `ecosystemSrc` diff --git a/lib-overlays/hjem/default.nix b/lib-overlays/hjem/default.nix index 0238c0d4..14ab0b94 100644 --- a/lib-overlays/hjem/default.nix +++ b/lib-overlays/hjem/default.nix @@ -130,12 +130,15 @@ null; # The machine the configuration is declared inside, when there - # is one: the childless evaluation of the nearest NixOS or - # darwin configuration above it, which hjem's modules receive - # as `osConfig`, null when there is none. + # is one: the childless evaluation of the nearest NixOS, + # darwin or system-manager configuration above it, which hjem's + # modules receive as `osConfig`, null when there is none. A + # system-manager evaluation is its system, with the + # configuration and the options in its `passthru`. machine = selection.nearestOf [ "nixos" "darwin" + "system-manager" ] manifest; osConfig = if machine != null then machine.value.config else null; @@ -143,10 +146,21 @@ # `modules/nixos/systemd.nix`, which declares the user's # systemd units and links them into the user's files, with the # `utils` and the options of the machine. A configuration inside - # a NixOS machine that has `utils` gets the same; the minimal - # NixOS evaluator gives none. - machineArgs = if machine != null then (machine.value._module or { }).args or { } else { }; - nixosUser = machine != null && machine.type == "nixos" && machineArgs ? utils; + # a NixOS or system-manager machine that has `utils` gets the + # same; the minimal NixOS evaluator gives none. + machineArgs = + if machine == null then + { } + else + (machine.value._module or { }).args + or (((machine.value.options or { })._module or { }).args.value or { }); + nixosUser = + machine != null + && builtins.elem machine.type [ + "nixos" + "system-manager" + ] + && machineArgs ? utils; # What hjem's OS modules set on a user that caisson sets too: # the user's name from the name the configuration is declared diff --git a/lib-overlays/home-manager/default.nix b/lib-overlays/home-manager/default.nix index 909e915b..a8f62a9e 100644 --- a/lib-overlays/home-manager/default.nix +++ b/lib-overlays/home-manager/default.nix @@ -235,7 +235,8 @@ }; # The machine the home is declared inside, when there is one: - # the nearest NixOS or darwin configuration above the home, so + # the nearest NixOS, darwin or system-manager configuration + # above the home, so # a structural configuration between the two changes nothing. # The manifest holds the childless evaluation of that machine, # which is the evaluation that leaves out the configurations @@ -476,6 +477,7 @@ machineTypes = [ "nixos" "darwin" + "system-manager" ]; # A home is published as `homeConfigurations.`, the diff --git a/modules/darwin/hjem-activation/default.nix b/modules/darwin/hjem-activation/default.nix index dece6258..40dd5b60 100644 --- a/modules/darwin/hjem-activation/default.nix +++ b/modules/darwin/hjem-activation/default.nix @@ -48,9 +48,14 @@ let isActivatedHere = entry: entry.manifest.type == "hjem" - && !(builtins.any (segment: segment.type == "nixos" || segment.type == "darwin") ( - lib.lists.init entry.path - )) + && !(builtins.any ( + segment: + builtins.elem segment.type [ + "nixos" + "darwin" + "system-manager" + ] + ) (lib.lists.init entry.path)) && entry.manifest.system == evalManifest.system; users = diff --git a/modules/darwin/home-manager-activation/default.nix b/modules/darwin/home-manager-activation/default.nix index ef0f7fc0..dda143e1 100644 --- a/modules/darwin/home-manager-activation/default.nix +++ b/modules/darwin/home-manager-activation/default.nix @@ -51,7 +51,14 @@ let above = lib.lists.init entry.path; in entry.manifest.type == "home-manager" - && !(builtins.any (segment: segment.type == "nixos" || segment.type == "darwin") above) + && !(builtins.any ( + segment: + builtins.elem segment.type [ + "nixos" + "darwin" + "system-manager" + ] + ) above) && entry.manifest.system == evalManifest.system; homes = builtins.map (entry: { diff --git a/modules/hjem/nixos-parent/default.nix b/modules/hjem/nixos-parent/default.nix index e1e2294c..3fcd734e 100644 --- a/modules/hjem/nixos-parent/default.nix +++ b/modules/hjem/nixos-parent/default.nix @@ -1,29 +1,36 @@ # SPDX-License-Identifier: MIT # -# A hjem module for a hjem configuration declared inside a NixOS -# configuration. It sets the home directory of the user from the -# account the machine declares, as hjem's NixOS module does for the -# users it embeds, and refuses a user the machine has no account for. +# A hjem module for a hjem configuration declared inside a NixOS or +# system-manager configuration. It sets the home directory of the user +# from the account the machine declares, as hjem's NixOS module does for +# the users it embeds, and refuses a user the machine has no account +# for. # -# caisson does not use hjem's NixOS module. A NixOS configuration adds -# this module to the modules that the hjem configurations declared -# inside it import by default; caisson's `default` NixOS module does -# that, through `caisson.forChildren`. A hjem configuration that -# passes `moduleImports` selects its modules itself, and gets this +# caisson does not use hjem's NixOS module. A NixOS or system-manager +# configuration adds this module to the modules that the hjem +# configurations declared inside it import by default; caisson's +# `default` module of either class does that, through +# `caisson.forChildren`. The module is registered as `nixos-parent` and, +# for the same file, as `system-manager-parent`. A hjem configuration +# that passes `moduleImports` selects its modules itself, and gets this # module only if it lists it. # # The module reads the machine through the manifest of the hjem # configuration, so it can only be imported by a hjem configuration -# that has a NixOS configuration above it. +# that has a NixOS or system-manager configuration above it. { ... }: # `lib` is the library of the hjem configuration, and `config` is its # configuration. { lib, config, ... }: let - # The configuration of the NixOS configuration the hjem configuration - # is declared inside, in its childless evaluation, which leaves out - # the configurations declared inside it. - machine = lib.caisson.evalManifest.nearest.nixos.value.config; + # The configuration of the machine the hjem configuration is declared + # inside, in its childless evaluation, which leaves out the + # configurations declared inside it. + machine = + (lib.caisson.integrations.nearestOf [ + "nixos" + "system-manager" + ] lib.caisson.evalManifest).value.config; # The account of the user, when the machine declares one. account = (machine.users.users or { }).${config.user} or null; @@ -34,7 +41,7 @@ in assertions = [ { assertion = config.enable -> account != null && (account.enable or true); - message = "The hjem user `${config.user}` has no enabled account in `users.users` of the NixOS configuration it is declared inside."; + message = "The hjem user `${config.user}` has no enabled account in `users.users` of the machine it is declared inside."; } ]; } diff --git a/modules/hjem/system-manager-parent b/modules/hjem/system-manager-parent new file mode 120000 index 00000000..dc907f13 --- /dev/null +++ b/modules/hjem/system-manager-parent @@ -0,0 +1 @@ +nixos-parent \ No newline at end of file diff --git a/modules/homeManager/nixos-parent/default.nix b/modules/homeManager/nixos-parent/default.nix index 7d08e402..ba550e91 100644 --- a/modules/homeManager/nixos-parent/default.nix +++ b/modules/homeManager/nixos-parent/default.nix @@ -16,22 +16,28 @@ # homes declared inside it import by default. caisson's `default` # NixOS module does that, through `caisson.forChildren`. A home that # passes `moduleImports` selects its modules itself, and gets this -# module only if it lists it. +# module only if it lists it. A system-manager configuration does the +# same with this file, registered also as `system-manager-parent`. # # The module reads the machine through the manifest of the home, so it -# can only be imported by a home that has a NixOS configuration above -# it. +# can only be imported by a home that has a NixOS or system-manager +# configuration above it. { ... }: # `lib` is the library of the home, and `config` is the configuration # of the home. { lib, config, ... }: let - # The configuration of the NixOS configuration the home is declared - # inside. The home reads the childless evaluation of the NixOS - # configuration, which is the evaluation that leaves out the - # configurations declared inside it. The home is one of those - # configurations, so it cannot read an evaluation that includes it. - machine = lib.caisson.evalManifest.nearest.nixos.value.config; + # The configuration of the machine the home is declared inside, the + # nearest NixOS or system-manager configuration above it. The home + # reads the childless evaluation of the machine, which is the + # evaluation that leaves out the configurations declared inside it. + # The home is one of those configurations, so it cannot read an + # evaluation that includes it. + machine = + (lib.caisson.integrations.nearestOf [ + "nixos" + "system-manager" + ] lib.caisson.evalManifest).value.config; # The account of the user of the home, when the NixOS configuration # declares one in `users.users`. It is null when the NixOS diff --git a/modules/homeManager/system-manager-parent b/modules/homeManager/system-manager-parent new file mode 120000 index 00000000..dc907f13 --- /dev/null +++ b/modules/homeManager/system-manager-parent @@ -0,0 +1 @@ +nixos-parent \ No newline at end of file diff --git a/modules/nixos/hjem-activation/default.nix b/modules/nixos/hjem-activation/default.nix index af0f9b43..20e11bde 100644 --- a/modules/nixos/hjem-activation/default.nix +++ b/modules/nixos/hjem-activation/default.nix @@ -39,6 +39,15 @@ # The module reads the hjem configurations from the full evaluation of # the NixOS configuration; in the childless evaluation, and in an # evaluation without systemd options, it defines nothing. +# +# The same module is registered for the `systemManager` class, under +# the same name, and a system-manager machine activates the hjem +# configurations declared inside it the same way. system-manager has no +# `system.extraDependencies`, so there the module keeps no extra +# dependencies in the system. The module reads +# `config.caisson.exports.configurations`, which the core module +# declares; it imports the core module of the class of the evaluation, +# read from its manifest. { closure-lib, ... }: { config, @@ -52,17 +61,33 @@ let # True when this evaluation declares the options the module defines. hasUnits = - options ? systemd - && options.systemd ? services - && options.systemd ? targets - && options ? users - && options ? system - && options.system ? extraDependencies; + options ? systemd && options.systemd ? services && options.systemd ? targets && options ? users; + + # True when the evaluation keeps extra dependencies in the system. + hasExtraDependencies = options ? system && options.system ? extraDependencies; + + # The core module of the class of this evaluation: of the + # `systemManager` class in a system-manager evaluation, else of the + # `nixos` class. The registered core modules of the two classes are + # one file under two names, so a module imports the one its + # evaluation already holds. + coreModule = + if (lib.caisson-core.evalManifest.type or null) == "system-manager" then + closure-lib.caisson.modules.systemManager.core + else + closure-lib.caisson.modules.nixos.core; + + # The integrations whose configurations are machines. + machineTypes = [ + "nixos" + "darwin" + "system-manager" + ]; isActivatedHere = entry: entry.manifest.type == "hjem" - && !(builtins.any (segment: segment.type == "nixos") (lib.lists.init entry.path)) + && !(builtins.any (segment: builtins.elem segment.type machineTypes) (lib.lists.init entry.path)) && entry.manifest.system == evalManifest.system; # What the units need of each enabled hjem configuration. @@ -119,9 +144,9 @@ let ); in { - # The module reads `config.caisson.exports.configurations`. The core - # module declares that option. - imports = [ closure-lib.caisson.modules.nixos.core ]; + # The core module of the class of this evaluation, which declares + # `config.caisson.exports.configurations`. + imports = [ coreModule ]; # `optionalAttrs` and not `mkIf` decides whether the options are # defined at all: NixOS refuses a definition, even under `mkIf false`, @@ -131,7 +156,7 @@ in assertions = [ { assertion = repeated == [ ]; - message = "caisson: this NixOS configuration activates more than one hjem configuration for ${lib.concatStringsSep ", " repeated}."; + message = "caisson: this machine activates more than one hjem configuration for ${lib.concatStringsSep ", " repeated}."; } ]; @@ -142,7 +167,9 @@ in }) users ); - system.extraDependencies = builtins.concatMap (user: user.extraDependencies) users; + system = lib.optionalAttrs hasExtraDependencies { + extraDependencies = builtins.concatMap (user: user.extraDependencies) users; + }; systemd.targets.hjem = { description = "hjem file management"; diff --git a/modules/nixos/home-manager-activation/default.nix b/modules/nixos/home-manager-activation/default.nix index 016b4bc6..020c4ebc 100644 --- a/modules/nixos/home-manager-activation/default.nix +++ b/modules/nixos/home-manager-activation/default.nix @@ -23,8 +23,8 @@ # NixOS configuration passes up: the homes declared inside it, also # through a structural configuration, that its # `caisson.home-manager.exported` selector selects. It leaves out two -# kinds. A home declared inside a NixOS configuration that is itself -# inside this one belongs to that inner machine. A home evaluated for +# kinds. A home declared inside a machine that is itself inside this +# one belongs to that inner machine. A home evaluated for # a system other than the system of this machine cannot run here. # # Which unit @@ -69,6 +69,15 @@ # that has no systemd options and no assertions. A NixOS configuration # of that kind cannot run units, so there the module also defines # nothing. +# +# The same module is registered for the `systemManager` class, under +# the same name, and a system-manager machine activates the homes +# declared inside it the same way: system-manager declares +# `users.users` and runs the units, and starts those wanted by +# `multi-user.target` with `system-manager.target`. The module reads +# `config.caisson.exports.configurations`, which the core module +# declares; it imports the core module of the class of the evaluation, +# read from its manifest. { closure-lib, ... }: { config, @@ -89,6 +98,24 @@ let && options.systemd ? user && options ? users; + # The core module of the class of this evaluation: of the + # `systemManager` class in a system-manager evaluation, else of the + # `nixos` class. The registered core modules of the two classes are + # one file under two names, so a module imports the one its + # evaluation already holds. + coreModule = + if (lib.caisson-core.evalManifest.type or null) == "system-manager" then + closure-lib.caisson.modules.systemManager.core + else + closure-lib.caisson.modules.nixos.core; + + # The integrations whose configurations are machines. + machineTypes = [ + "nixos" + "darwin" + "system-manager" + ]; + # True for an entry this machine activates. `entry.path` is the path # from this NixOS configuration to the configuration of the entry, a # list of `{ type, name }` segments. @@ -98,9 +125,9 @@ let above = lib.lists.init entry.path; in entry.manifest.type == "home-manager" - # A NixOS configuration on the path is a machine inside this - # machine, and the home belongs to it. - && !(builtins.any (segment: segment.type == "nixos") above) + # A machine on the path is a machine inside this machine, and the + # home belongs to it. + && !(builtins.any (segment: builtins.elem segment.type machineTypes) above) && entry.manifest.system == evalManifest.system; # What the units need of each home. @@ -225,9 +252,9 @@ let ); in { - # The module reads `config.caisson.exports.configurations`. The core - # module declares that option. - imports = [ closure-lib.caisson.modules.nixos.core ]; + # The core module of the class of this evaluation, which declares + # `config.caisson.exports.configurations`. + imports = [ coreModule ]; options.caisson.home-manager.activation.usersDeclaredElsewhere = lib.mkOption { type = lib.types.listOf lib.types.str; @@ -265,11 +292,11 @@ in { assertion = withoutAccount == [ ]; message = '' - caisson: this NixOS configuration has a home whose user has no account in `users.users`. + caisson: this machine has a home whose user has no account in `users.users`. ${lib.concatMapStringsSep "\n" ( home: " the home `${home.name}` is for the user `${home.username}`" ) withoutAccount} - Check `home.username` of the home, and that the home is declared inside the right NixOS configuration. + Check `home.username` of the home, and that the home is declared inside the right machine. If something other than `users.users` provides the account, as systemd-homed does, add the user to `caisson.home-manager.activation.usersDeclaredElsewhere`. ''; } @@ -283,7 +310,7 @@ in { assertion = repeated == [ ]; message = '' - caisson: this NixOS configuration activates more than one home for the same user. + caisson: this machine activates more than one home for the same user. ${lib.concatMapStringsSep "\n" ( username: " ${username}: ${ diff --git a/modules/systemManager/default/caisson/default.nix b/modules/systemManager/default/caisson/default.nix new file mode 100644 index 00000000..48313128 --- /dev/null +++ b/modules/systemManager/default/caisson/default.nix @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: MIT +# +# The `caisson.*` part of the default system-manager module. +{ closure-lib, ... }: +{ ... }: +{ + imports = [ + # `forChildren.nix` sets options under `caisson.forChildren`. The + # core module declares those options. + closure-lib.caisson.modules.systemManager.core + (import ./forChildren.nix { inherit closure-lib; }) + ]; +} diff --git a/modules/systemManager/default/caisson/forChildren.nix b/modules/systemManager/default/caisson/forChildren.nix new file mode 100644 index 00000000..3366dcdf --- /dev/null +++ b/modules/systemManager/default/caisson/forChildren.nix @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: MIT +# +# What a system-manager configuration gives the homes and the hjem +# configurations declared inside it. +# +# A configuration gives things to the configurations declared inside +# it through the options under `caisson.forChildren`. This file adds the +# home-manager module `system-manager-parent` to the modules that a home +# declared inside this system-manager configuration imports by default, +# and the hjem module `system-manager-parent` to those of an hjem +# configuration declared inside it. Each sets the options that come from +# the machine: the home directory from the account of the user that the +# machine declares in `users.users`, and for a home the user ID and the +# Nix of the machine. +# +# A system-manager configuration that passes `moduleImports` selects +# its modules itself, and then nothing inside it gets these modules from +# here. A home or hjem configuration that passes `moduleImports` +# selects its modules itself, and gets the module only if it lists it. +# +# `closure-lib` is the library of caisson, where the modules are +# registered. +{ closure-lib }: +{ ... }: +{ + caisson.forChildren.extraDefaultModuleImports.homeManager = _lib: [ + closure-lib.caisson.modules.homeManager.system-manager-parent + ]; + + caisson.forChildren.extraDefaultModuleImports.hjem = _lib: [ + closure-lib.caisson.modules.hjem.system-manager-parent + ]; +} diff --git a/modules/systemManager/default/default.nix b/modules/systemManager/default/default.nix new file mode 100644 index 00000000..39c8fbaa --- /dev/null +++ b/modules/systemManager/default/default.nix @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: MIT +# +# The `default` system-manager module caisson registers. A +# system-manager configuration imports every registered module named +# `default` unless it passes `moduleImports`, so this is what it gets +# from caisson when it does not select its modules itself. +# +# It holds what a machine running another Linux distribution does for +# the configurations declared inside it, as caisson's `default` NixOS +# module does for a NixOS machine: +# +# - `./caisson/forChildren.nix` gives each home the home-manager module +# `system-manager-parent` and each hjem configuration the hjem module +# `system-manager-parent`, which set the options that come from the +# account of the user on the machine. +# - The registered modules `home-manager-activation` and +# `hjem-activation`, the same modules a NixOS machine uses, write the +# systemd units that activate the homes and the hjem configurations +# when the machine is activated. +# +# The parts read options of caisson's core module. The core module +# leaves its options out when the library of the evaluation is not a +# caisson composition, so the parts are left out in the same case. +{ closure-lib, mkModule, ... }: +{ lib, ... }: +{ + imports = + if lib ? caisson && lib ? caisson-core then + [ + (mkModule ./caisson) + closure-lib.caisson.modules.systemManager.home-manager-activation + closure-lib.caisson.modules.systemManager.hjem-activation + ] + else + [ ]; +} diff --git a/modules/systemManager/hjem-activation b/modules/systemManager/hjem-activation new file mode 120000 index 00000000..f6ababfd --- /dev/null +++ b/modules/systemManager/hjem-activation @@ -0,0 +1 @@ +../nixos/hjem-activation \ No newline at end of file diff --git a/modules/systemManager/home-manager-activation b/modules/systemManager/home-manager-activation new file mode 120000 index 00000000..27511a77 --- /dev/null +++ b/modules/systemManager/home-manager-activation @@ -0,0 +1 @@ +../nixos/home-manager-activation \ No newline at end of file diff --git a/tests/pinned-world/default.nix b/tests/pinned-world/default.nix index 0d6df59d..20f75abf 100644 --- a/tests/pinned-world/default.nix +++ b/tests/pinned-world/default.nix @@ -1950,6 +1950,63 @@ let in top.nixidyEnvs.x86_64-linux.prod.environmentPackage.drvPath == upstream.environmentPackage.drvPath; + # A system-manager machine activates the home and the hjem + # configuration declared inside it, as a NixOS machine does: both + # take their directory from the account the machine declares, the + # machine writes the units that activate them and installs the + # packages of the hjem user, and the home is published as + # `probe@box`. + systemManagerActivatesWhatIsDeclaredInside = + let + top = hiveLib.caisson.structural.mkTopConfiguration { + moduleImports = _modules: [ ]; + configModule = + { lib, ... }: + { + caisson.system-manager.configurations.box = lib.caisson.system-manager.mkConfiguration { + ecosystemSrc = inputs.system-manager; + configModule = + { lib, ... }: + { + system-manager.allowAnyDistro = true; + users.groups.probe.gid = 5000; + users.users.probe = { + isNormalUser = true; + uid = 5000; + group = "probe"; + home = "/home/probe"; + }; + caisson.home-manager.configurations.probe = lib.caisson.home-manager.mkConfiguration { + ecosystemSrc = inputs.home-manager; + configModule.home.stateVersion = "25.05"; + }; + caisson.hjem.configurations.probe = lib.caisson.hjem.mkConfiguration { + ecosystemSrc = inputs.hjem; + configModule = + { pkgs, ... }: + { + files.".config/probe/greeting".text = "hello"; + packages = [ pkgs.hello ]; + }; + }; + }; + }; + }; + }; + box = top.systemConfigs.box.config; + children = + top.caisson.manifest.children.system.x86_64-linux.children.system-manager.box.children.system.x86_64-linux.children; + home = children.home-manager.probe.value.config; + hjem = children.hjem.probe.value.config; + names = packages: builtins.map (package: package.name or "") packages; + in + home.home.homeDirectory == "/home/probe" + && hjem.directory == "/home/probe" + && box.systemd.services ? home-manager-probe + && box.systemd.services ? "hjem-activate@" + && builtins.elem pkgs.hello.name (names box.users.users.probe.packages) + && builtins.attrNames top.homeConfigurations == [ "probe@box" ]; + # A terranix configuration that is a top is its `config.tf.json` # file, built from the package set of the composition with the # evaluated configuration in its `passthru`, and its modules get the From 0522d1cc3e569a552684482a6b6b63c0afc0a59c Mon Sep 17 00:00:00 2001 From: Chris Hodapp Date: Sun, 18 Oct 2026 10:34:05 -0700 Subject: [PATCH 2/2] docs: write "a hjem" in the system-manager integration Co-Authored-By: Claude Opus 5.5 --- modules/systemManager/default/caisson/forChildren.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/systemManager/default/caisson/forChildren.nix b/modules/systemManager/default/caisson/forChildren.nix index 3366dcdf..283ec3ca 100644 --- a/modules/systemManager/default/caisson/forChildren.nix +++ b/modules/systemManager/default/caisson/forChildren.nix @@ -7,7 +7,7 @@ # it through the options under `caisson.forChildren`. This file adds the # home-manager module `system-manager-parent` to the modules that a home # declared inside this system-manager configuration imports by default, -# and the hjem module `system-manager-parent` to those of an hjem +# and the hjem module `system-manager-parent` to those of a hjem # configuration declared inside it. Each sets the options that come from # the machine: the home directory from the account of the user that the # machine declares in `users.users`, and for a home the user ID and the