From 0d5f7e00b60ce30f643d9de80a090513004b61a9 Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 3 Jun 2026 17:20:08 +0200 Subject: [PATCH] feat(#903): expose hyperhive.user.uid + gid options for optional UID pinning --- nix/templates/harness-base.nix | 77 ++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index 3c2ba44e..b6b62f48 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -132,9 +132,42 @@ in Constraints match `useradd`'s NAME_REGEX: lowercase / `_` start, total length ≤ 31, no special characters. UID is auto-assigned - by NixOS; no `uid =` override surface (intentional — pinning - across rebuilds isn't a concern when the home and state dirs - stay bind-mounted from the host). + by NixOS unless `hyperhive.user.uid` is explicitly set. + ''; + }; + + options.hyperhive.user.uid = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + example = 1100; + description = '' + Optional fixed UID for the per-agent unix user. `null` (default) + lets NixOS auto-assign from the normal-user range (≥ 1000), + which is the right default for most deployments — the UID stays + stable across container rebuilds because each container only has + one normal user and the assignment is written into the container's + `/etc/passwd` at activation time. + + Set an explicit value only when the host needs a predictable UID + for the agent's state files — e.g. if an operator script + references files by numeric UID, or to keep ownership stable + across full container destroy + recreate on a fresh host. + + Values must be in `[1000, 60000)`. Using UIDs < 1000 clashes with + system accounts and is rejected by NixOS. + ''; + }; + + options.hyperhive.user.gid = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + example = 1100; + description = '' + Optional fixed GID for the per-agent unix group. `null` (default) + lets NixOS auto-assign. When `uid` is set, setting `gid` to the + same value keeps uid == gid (the conventional Unix pattern for + per-user groups). Has no effect unless also setting + `hyperhive.user.uid`. ''; }; @@ -1063,14 +1096,35 @@ in `..` path segments. ''; } + { + assertion = + config.hyperhive.user.uid == null + || (config.hyperhive.user.uid >= 1000 && config.hyperhive.user.uid < 60000); + message = '' + hyperhive.user.uid must be in [1000, 60000) — values below + 1000 clash with system accounts; values ≥ 60000 are reserved + by NixOS for dynamic allocation. Leave unset (null) to let + NixOS auto-assign. + ''; + } + { + assertion = + config.hyperhive.user.gid == null + || (config.hyperhive.user.gid >= 1000 && config.hyperhive.user.gid < 60000); + message = '' + hyperhive.user.gid must be in [1000, 60000) — same range + constraint as hyperhive.user.uid. + ''; + } ]; # Per-agent unix user. Runs the hive harness + # co-process daemons under a non-root principal. UID auto-assigned by - # NixOS. The container activation script (hive-agent-user-migrate) - # chowns the bind-mounted state dir — including credential files - # written by hive-c0re before the container was built — to this user - # on every boot, so agent processes can always read their own tokens. + # NixOS unless `hyperhive.user.uid` is set. The container activation + # script (hive-agent-user-migrate) chowns the bind-mounted state dir + # — including credential files written by hive-c0re before the + # container was built — to this user on every boot, so agent + # processes can always read their own tokens. users.users.${userName} = { isNormalUser = true; home = homeDir; @@ -1082,8 +1136,15 @@ in # the system default for the root user too (see SHELL env # var declaration below). shell = pkgs.bashInteractive; + } + // lib.optionalAttrs (config.hyperhive.user.uid != null) { + uid = config.hyperhive.user.uid; }; - users.groups.${userName} = { }; + users.groups.${userName} = + { } + // lib.optionalAttrs (config.hyperhive.user.gid != null) { + gid = config.hyperhive.user.gid; + }; # `NOPASSWD: ALL` for the agent user. Lets claude's Bash tool # keep working with anything that expected root (systemctl,