diff --git a/home/README.md b/home/README.md index 77a2392d..024d7c18 100644 --- a/home/README.md +++ b/home/README.md @@ -15,7 +15,7 @@ home/ │ │ └── ... │ └── ... ├── hosts/ # Host-specific home manager entry modules -│ ├── linux/ # Linux host home modules (idols-ai, 12kingdoms-shoukei, idols-kana, idols-ruby) +│ ├── linux/ # Linux host home modules (idols-ai, 12kingdoms-shoukei, idols-aquamarine, idols-kana, idols-ruby) │ └── darwin/ # macOS host home modules (fern, frieren) └── darwin/ # macOS-specific home manager configurations ├── proxy/ # Proxy configurations diff --git a/home/base/tui/shell/default.nix b/home/base/tui/shell/default.nix index fba7ef59..01f33052 100644 --- a/home/base/tui/shell/default.nix +++ b/home/base/tui/shell/default.nix @@ -1,17 +1,26 @@ { config, + lib, nu_scripts, ... }: { - programs.nushell = { + options.modules.shell.secrets.enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Source ~/.secrets/nushell-secrets.nu, which only the desktops sync."; + }; + + config.programs.nushell = { # the file must exist, otherwise nushell will complain about it! # # currently, nushell does not support conditional sourcing of files # https://github.com/nushell/nushell/issues/8214 extraConfig = '' - # decrypted secret: AI agents must not read it - source ${config.home.homeDirectory}/.secrets/nushell-secrets.nu + ${lib.optionalString config.modules.shell.secrets.enable '' + # decrypted secret: AI agents must not read it + source ${config.home.homeDirectory}/.secrets/nushell-secrets.nu + ''} # Directories in this constant are searched by the # `use` and `source` commands. diff --git a/home/hosts/README.md b/home/hosts/README.md index a526ef72..171a736b 100644 --- a/home/hosts/README.md +++ b/home/hosts/README.md @@ -10,8 +10,9 @@ This directory contains host-specific Home Manager entry modules. ## Conventions 1. Each host output should reference only one file under `home/hosts/...`. -2. Shared home module imports should be handled in the host file itself. - - Linux hosts usually import `../../linux/core.nix` or `../../linux/gui.nix`. +2. The host file imports its profile's home role, see + [Host Profiles](../../hosts/README.md#host-profiles). + - `desktop` hosts import `../../linux/gui.nix`; `agent` hosts import `../../linux/tui.nix`. - Darwin hosts import `../../darwin`. 3. Host-specific overrides (SSH keys, desktop toggles, host-local config links) live in the same host file. diff --git a/home/hosts/linux/idols-aquamarine.nix b/home/hosts/linux/idols-aquamarine.nix new file mode 100644 index 00000000..397b249c --- /dev/null +++ b/home/hosts/linux/idols-aquamarine.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ../../linux/tui.nix + ]; +} diff --git a/home/hosts/linux/idols-kana.nix b/home/hosts/linux/idols-kana.nix index a6da9140..e1bd015b 100644 --- a/home/hosts/linux/idols-kana.nix +++ b/home/hosts/linux/idols-kana.nix @@ -1,7 +1,6 @@ { imports = [ - ../../linux/core.nix - ../../linux/gui/i3 + ../../linux/tui.nix ]; # Headless X11/i3 session for computer use. diff --git a/home/hosts/linux/idols-ruby.nix b/home/hosts/linux/idols-ruby.nix index 4577dfdf..caed30d1 100644 --- a/home/hosts/linux/idols-ruby.nix +++ b/home/hosts/linux/idols-ruby.nix @@ -1,7 +1,6 @@ { imports = [ - ../../linux/core.nix - ../../linux/gui/i3 + ../../linux/tui.nix ]; # Headless X11/i3 session for computer use. diff --git a/home/linux/gui/i3/README.md b/home/linux/gui/i3/README.md index eef8ab5f..4da8e0e7 100644 --- a/home/linux/gui/i3/README.md +++ b/home/linux/gui/i3/README.md @@ -46,8 +46,9 @@ tree the agent reads, so it stays. the systemd user manager, clears any stale `WAYLAND_DISPLAY`, and enables the AT-SPI bridge via a gsettings key. - Drivers come from the nur-ryan4yin overlay (`overlays/nur-ryan4yin.nix`). -- Host wiring: `home/hosts/linux/idols-ruby.nix`, `home/hosts/linux/idols-kana.nix`, - `hosts/idols-ruby/computer-use.nix`. +- Wiring: the `agent` profile imports both sides, off by default + ([Host Profiles](../../../../hosts/README.md#host-profiles)); + `hosts/idols-{ruby,kana}/default.nix` and `home/hosts/linux/idols-{ruby,kana}.nix` turn them on. ## The i3 config diff --git a/home/linux/tui.nix b/home/linux/tui.nix index 007b1b1a..4fed2e58 100644 --- a/home/linux/tui.nix +++ b/home/linux/tui.nix @@ -1,3 +1,5 @@ +# Home role of the `agent` profile (hosts/README.md#host-profiles): the full +# TUI stack for a headless host. { imports = [ ../base/core @@ -5,5 +7,10 @@ ../base/home.nix ./base + # off by default; a host turns it on with modules.desktop.computerUse.enable + ./gui/i3 ]; + + # ~/.secrets is synced to the desktops only. + modules.shell.secrets.enable = false; } diff --git a/hosts/README.md b/hosts/README.md index e3a950db..c05b9a75 100644 --- a/hosts/README.md +++ b/hosts/README.md @@ -5,6 +5,53 @@ This directory contains all host-specific configurations for my NixOS and macOS Manual firmware settings are recorded in [`docs/bios.md`](../docs/bios.md), and the shared Wake-on-LAN workflow is in [`docs/wake-on-lan.md`](../docs/wake-on-lan.md). +## Host Profiles + +Every host is exactly one profile. The profile fixes the system module that +`outputs//src/.nix` imports and the Home Manager role that +`home/hosts//.nix` imports. A host directory only adds hardware, networking, +persisted paths and feature switches on top. + +| Profile | System module (in outputs) | Home role (in home/hosts) | Hosts | +| --------- | --------------------------------- | ------------------------- | ------------------------------------------- | +| `desktop` | `modules/nixos/desktop.nix` | `home/linux/gui.nix` | `ai`, `shoukei` | +| `agent` | `modules/nixos/agent.nix` | `home/linux/tui.nix` | `aquamarine`, `ruby`, `kana` | +| `server` | `modules/nixos/server/server.nix` | none | `shoryu`, `shushou`, `youko`, k3s, `akane`¹ | +| `darwin` | `modules/darwin` | `home/darwin` | `fern`, `frieren` | + +¹ `akane` (aarch64) uses `server-aarch64.nix`; the k3s nodes import `server.nix` from their host +directory. + +- **desktop**: Niri session and GUI apps on top of the full TUI stack. +- **agent**: headless, with the full TUI stack from Home Manager (shell, editors, dev toolchains, + cloud CLIs, AI agents with their rules and skills), and nix-ld, rootless Podman and linger from + the system. Computer use (Xvfb + i3, VNC) is part of the profile but off by default. +- **server**: system services only, no Home Manager. + +The `agent` hosts differ only in: + +| | `aquamarine` | `ruby` | `kana` | +| ------------ | ------------------------------------------ | ------------------- | ------------- | +| Computer use | off | on, with cua-driver | on | +| Hardware | physical; 2x R9700, NPU speech recognition | libvirt guest | libvirt guest | +| Root | tmpfs; `preservation.nix` keeps home state | disk | disk | + +`outputs/x86_64-linux/tests/agent-profile` checks the `agent` rows. + +### Where a new piece of configuration goes + +1. A tool you run from a shell goes to the Home Manager role (`home/base/core`, `home/base/tui`), + never to a host's `environment.systemPackages`. +2. Anything that needs root or a system service (nix-ld, Podman, drivers, systemd units) goes to the + profile's system module, or to a `modules/nixos` module the profile imports. +3. Hardware, networking, the hostname, persisted paths and feature switches go to the host + directory. + +A host does not import files from another host's directory; shared code moves under `modules/` or +`home/` instead. Existing exceptions, between machines on identical hardware: `shushou` and `youko` +reuse `12kingdoms-shoryu`'s hardware and preservation files, and `shoukei` reuses `idols-ai`'s +preservation file. + ## Current Host Inventory ### Physical Machines diff --git a/hosts/idols-akane/default.nix b/hosts/idols-akane/default.nix index 6ad4d894..cdcb329c 100644 --- a/hosts/idols-akane/default.nix +++ b/hosts/idols-akane/default.nix @@ -15,9 +15,6 @@ in { imports = (mylib.scanPaths ./.) ++ [ disko.nixosModules.default - - ../idols-ruby/packages.nix - ../idols-ruby/oci-containers ]; # supported file systems, so we can mount any removable disks with these filesystems diff --git a/hosts/idols-aquamarine/default.nix b/hosts/idols-aquamarine/default.nix index fc5d961e..c5af4bb6 100644 --- a/hosts/idols-aquamarine/default.nix +++ b/hosts/idols-aquamarine/default.nix @@ -67,14 +67,9 @@ in # VictoriaMetrics. services.smartd.enable = true; - # The inference engine ships as an OCI image and is meant to run rootless, so - # `podman` is what this host uses to build and start it from the `~/src` checkout - # that ./preservation.nix keeps. - virtualisation.podman.enable = true; - - # Keep the user systemd manager running after logout and start it at boot, - # so enabled user services can run without an active login session. - users.users."${myvars.username}".linger = true; + # The inference engine ships as an OCI image and runs under the agent + # profile's rootless Podman (modules/nixos/agent.nix), built from the `~/src` + # checkout that ./preservation.nix keeps. # Speech recognition runs on the NPU, see ./asr.nix. The driver and the # compiler it loads come from NixOS/nixpkgs#570230, which is unmerged, so they diff --git a/hosts/idols-aquamarine/preservation.nix b/hosts/idols-aquamarine/preservation.nix index d3ec1e95..d870b9df 100644 --- a/hosts/idols-aquamarine/preservation.nix +++ b/hosts/idols-aquamarine/preservation.nix @@ -85,6 +85,84 @@ in ".config/systemd/user" ".config/containers/systemd" + + # ====================================== + # Home Manager TUI role (home/linux/tui.nix); the same paths `ai` keeps + # ====================================== + ".local/state/home-manager" + ".local/state/nix/profiles" + ".local/share/nix" + ".config/nushell" # history + + # ai agents + ".agents" # skills for all agents + ".config/agents" + ".codex" + ".dsh" # DeepSeek Harness + ".pi" + ".config/opencode" + ".local/share/opencode" + ".local/state/opencode" + # ctx7 docs lookup login + { + directory = ".config/context7"; + mode = "0700"; + } + + # editors + ".local/share/nvim" + ".local/state/nvim" + ".local/share/jupyter" + + # tuios session state, project-tape trust store and layout templates + { + directory = ".local/state/tuios"; + mode = "0700"; + } + { + directory = ".local/share/tuios"; + mode = "0700"; + } + ".config/tuios" + + # cloud native + { + directory = ".aws"; + mode = "0700"; + } + { + directory = ".aliyun"; + mode = "0700"; + } + { + directory = ".config/gcloud"; + mode = "0700"; + } + { + directory = ".config/gh"; + mode = "0700"; + } + { + directory = ".kube"; + mode = "0700"; + } + ".terraform.d/plugin-cache" + + # language package managers + ".npm" + "go" + ".cargo" + ".local/bin" + ".local/share/uv" + + { + directory = ".gnupg"; + mode = "0700"; + } + { + directory = ".ssh"; + mode = "0700"; + } ]; }; }; @@ -123,6 +201,21 @@ in group = lib.mkForce username; mode = lib.mkForce "0750"; }; + "/home/${username}/.local/state".d = { + user = username; + group = lib.mkForce username; + mode = lib.mkForce "0750"; + }; + "/home/${username}/.local/state/nix".d = { + user = username; + group = lib.mkForce username; + mode = lib.mkForce "0750"; + }; + "/home/${username}/.terraform.d".d = { + user = username; + group = lib.mkForce username; + mode = lib.mkForce "0750"; + }; }; systemd.tmpfiles.rules = [ diff --git a/hosts/idols-kana/README.md b/hosts/idols-kana/README.md index cb16ecc8..a05aac14 100644 --- a/hosts/idols-kana/README.md +++ b/hosts/idols-kana/README.md @@ -1,7 +1,7 @@ # Idols - Kana -NixOS VM running under libvirt on `shoryu`, used to run AI agents. It imports the agent and -computer-use modules from [`idols-ruby`](../idols-ruby/). +NixOS VM running under libvirt on `shoryu`, used to run AI agents. Profile: `agent`, see +[Host Profiles](../README.md#host-profiles). - Deploy from the VM with `just local`. - Host inventory and role context: [`../README.md`](../README.md). diff --git a/hosts/idols-kana/default.nix b/hosts/idols-kana/default.nix index f4bd793e..f0b7202b 100644 --- a/hosts/idols-kana/default.nix +++ b/hosts/idols-kana/default.nix @@ -16,11 +16,8 @@ let ipv4WithMask = "${ipv4}/24"; in { - imports = [ - ../idols-ruby/packages.nix - ../idols-ruby/computer-use.nix - ../idols-ruby/oci-containers - ]; + # Headless computer-use environment (Xvfb + i3 + desktop automation + browser). + modules.desktop.computerUse.enable = true; # supported file systems, so we can mount any removable disks with these filesystems boot.supportedFilesystems = [ diff --git a/hosts/idols-ruby/README.md b/hosts/idols-ruby/README.md index 5e7666ac..9d023401 100644 --- a/hosts/idols-ruby/README.md +++ b/hosts/idols-ruby/README.md @@ -1,8 +1,7 @@ # Idols - Ruby -NixOS VM running under libvirt on `shoryu`, used to run AI agents. It owns the shared agent and -computer-use modules (`packages.nix`, `computer-use.nix`, `oci-containers/`) that -[`idols-kana`](../idols-kana/) imports. +NixOS VM running under libvirt on `shoryu`, used to run AI agents. Profile: `agent`, see +[Host Profiles](../README.md#host-profiles). - Deploy from the VM with `just local`. - Host inventory and role context: [`../README.md`](../README.md). diff --git a/hosts/idols-ruby/computer-use.nix b/hosts/idols-ruby/computer-use.nix deleted file mode 100644 index fab73e0f..00000000 --- a/hosts/idols-ruby/computer-use.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ - # Headless computer-use environment (sway + desktop automation + browser). - modules.desktop.computerUse.enable = true; -} diff --git a/hosts/idols-ruby/default.nix b/hosts/idols-ruby/default.nix index cd150a3a..6586e652 100644 --- a/hosts/idols-ruby/default.nix +++ b/hosts/idols-ruby/default.nix @@ -16,11 +16,8 @@ let ipv4WithMask = "${ipv4}/24"; in { - imports = [ - ./packages.nix - ./computer-use.nix - ./oci-containers - ]; + # Headless computer-use environment (Xvfb + i3 + desktop automation + browser). + modules.desktop.computerUse.enable = true; # Enable binfmt emulation of aarch64-linux, this is required for cross compilation. boot.binfmt.emulatedSystems = [ diff --git a/hosts/idols-ruby/oci-containers/default.nix b/hosts/idols-ruby/oci-containers/default.nix deleted file mode 100644 index 3ecb1f0e..00000000 --- a/hosts/idols-ruby/oci-containers/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - mylib, - lib, - pkgs, - ... -}: -{ - imports = mylib.scanPaths ./.; - - virtualisation = { - docker.enable = true; - - oci-containers = { - backend = "docker"; - }; - }; -} diff --git a/modules/README.md b/modules/README.md index e3f15993..a476f4c7 100644 --- a/modules/README.md +++ b/modules/README.md @@ -43,6 +43,7 @@ modules/ │ ├── trash.nix # Trash retention cleanup │ ├── user-group.nix # User and group management │ └── zram.nix # ZRAM swap configuration + ├── agent.nix # `agent` profile: headless host with the TUI home role ├── desktop.nix # Desktop environment configuration ├── desktop/ # Desktop-specific modules │ ├── canokey.nix # CanoKey hardware token diff --git a/hosts/idols-ruby/packages.nix b/modules/nixos/agent.nix similarity index 50% rename from hosts/idols-ruby/packages.nix rename to modules/nixos/agent.nix index 6d6a3d2d..3f8a9fe1 100644 --- a/hosts/idols-ruby/packages.nix +++ b/modules/nixos/agent.nix @@ -1,12 +1,29 @@ { pkgs, - llm-agents, + myvars, ... }: -let - agentPackages = llm-agents.packages.${pkgs.stdenv.hostPlatform.system}; -in +############################################################# +# +# The `agent` profile (hosts/README.md#host-profiles): a headless +# host whose user tools come from the Home Manager TUI role +# (home/linux/tui.nix). This module only carries what needs root. +# +############################################################# { + imports = [ + ./server/server.nix + # off by default; a host turns it on with modules.desktop.computerUse.enable + ./desktop/computer-use.nix + ]; + + # Rootless Podman for agents and the images they build. + virtualisation.podman.enable = true; + + # Keep the user systemd manager running after logout and start it at boot, + # so agent and computer-use user services run without a login session. + users.users."${myvars.username}".linger = true; + # https://github.com/Mic92/nix-ld # # nix-ld will install itself at `/lib64/ld-linux-x86-64.so.2` so that @@ -32,60 +49,4 @@ in stdenv.cc.cc ]; }; - - environment.systemPackages = - with pkgs; - [ - nodejs_24 - pnpm - - #-- python - uv # python project package manager - (python313.withPackages ( - ps: with ps; [ - pandas - requests - pyquery - pyyaml - numpy - - # model downloaders - huggingface-hub - ] - )) - - rustc - cargo # rust package manager - go - - # cryptography - age - sops - rclone - gnupg - - # cloud-native - kubectl - istioctl - kubernetes-helm - fluxcd - terraform - - # db related - pgcli - mongosh - sqlite - - yt-dlp # youtube/bilibili/soundcloud/... video/music downloader - ] - # AI Agent Tools - ++ [ - # Agents - agentPackages.codex - agentPackages.opencode2 - agentPackages.pi - # dsh from nur-packages: llm-agents.nix's dsh cannot boot on nixpkgs node - # (node-addon-require-builtin probe), this one shims it. - pkgs.dsh - ]; } diff --git a/outputs/README.md b/outputs/README.md index eae00642..dd2c5e10 100644 --- a/outputs/README.md +++ b/outputs/README.md @@ -47,10 +47,10 @@ Some tests iterate over every configuration, so a new host is covered automatica them. Others name specific hosts — or a name prefix, like `k3s-api-endpoint`, which covers every `k3s-test-1-*` configuration — and only cover a new host if you add it. -| Kind | Tests | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Every configuration | `hostname`, `kernel`, `nix-system-features`, `security-apparmor`, `security-container-groups`, `security-exporters`, `security-firewall`, `security-k3s-kubeconfig`, `security-k3s-pod-firewall`, `security-kernel`, `security-ssh-x11` | -| Named hosts only | `btrbk`, `cpu-power`, `home-manager`, `home-manager-xdg`, `computer-use-headless`, `k3s-api-endpoint`, `k3s-node-home-manager`, `idols-ai-gpu`, `idols-ai-net`, `idols-ai-voice-input`, `idols-aquamarine`, `security-postgres-auth`, `youko-metrics`, `ups-metrics`, `shoukei-logind`, `mihomo-dns`, `colmena-parity`, `microvm-guest-parity` | +| Kind | Tests | +| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Every configuration | `hostname`, `kernel`, `nix-system-features`, `security-apparmor`, `security-container-groups`, `security-exporters`, `security-firewall`, `security-k3s-kubeconfig`, `security-k3s-pod-firewall`, `security-kernel`, `security-ssh-x11` | +| Named hosts only | `btrbk`, `cpu-power`, `home-manager`, `home-manager-xdg`, `computer-use-headless`, `agent-profile`, `k3s-api-endpoint`, `k3s-node-home-manager`, `idols-ai-gpu`, `idols-ai-net`, `idols-ai-voice-input`, `idols-aquamarine`, `security-postgres-auth`, `youko-metrics`, `ups-metrics`, `shoukei-logind`, `mihomo-dns`, `colmena-parity`, `microvm-guest-parity` | `hostname` expects each configuration's `networking.hostName` to equal its name. Niri desktop configurations are the exception (`ai-niri` → `ai`, `shoukei-niri` → `shoukei`) and are listed in diff --git a/outputs/aarch64-linux/tests/colmena-parity/expr.nix b/outputs/aarch64-linux/tests/colmena-parity/expr.nix index 034d125d..33335733 100644 --- a/outputs/aarch64-linux/tests/colmena-parity/expr.nix +++ b/outputs/aarch64-linux/tests/colmena-parity/expr.nix @@ -3,4 +3,7 @@ inputs, }: # Colmena must deploy the system that nixosConfigurations evaluates and builds. +# Every node comes out of the same rawColmenaHive code in outputs/default.nix, so +# akane covers the x86_64 nodes too; those all read the private mysecrets input, +# which CI cannot fetch. lib.genAttrs [ "akane" ] (name: inputs.self.colmenaHive.toplevel.${name}.drvPath) diff --git a/outputs/x86_64-linux/src/idols-aquamarine.nix b/outputs/x86_64-linux/src/idols-aquamarine.nix index 969e62a5..66b6411e 100644 --- a/outputs/x86_64-linux/src/idols-aquamarine.nix +++ b/outputs/x86_64-linux/src/idols-aquamarine.nix @@ -22,8 +22,8 @@ let modules = { nixos-modules = (map mylib.relativeToRoot [ - # common - "modules/nixos/server/server.nix" + # profile: agent (hosts/README.md#host-profiles) + "modules/nixos/agent.nix" # host specific "hosts/idols-${name}" ]) @@ -32,6 +32,10 @@ let # until its new host key is a recipient in nix-secrets. Enabling one is # the last step in hosts/idols-aquamarine/README.md. ]; + home-modules = map mylib.relativeToRoot [ + # host specific + "home/hosts/linux/idols-${name}.nix" + ]; }; systemArgs = modules // args; diff --git a/outputs/x86_64-linux/src/idols-kana.nix b/outputs/x86_64-linux/src/idols-kana.nix index d95e604d..95c35665 100644 --- a/outputs/x86_64-linux/src/idols-kana.nix +++ b/outputs/x86_64-linux/src/idols-kana.nix @@ -24,10 +24,9 @@ let (map mylib.relativeToRoot [ # common "secrets/nixos.nix" - "modules/nixos/server/server.nix" + # profile: agent (hosts/README.md#host-profiles) + "modules/nixos/agent.nix" "modules/nixos/server/qemu-guest-hardware-configuration.nix" - # headless computer-use environment (sway + desktop automation) - "modules/nixos/desktop/computer-use.nix" # host specific "hosts/idols-${name}" ]) diff --git a/outputs/x86_64-linux/src/idols-ruby.nix b/outputs/x86_64-linux/src/idols-ruby.nix index 6c28247b..26ba00a2 100644 --- a/outputs/x86_64-linux/src/idols-ruby.nix +++ b/outputs/x86_64-linux/src/idols-ruby.nix @@ -24,10 +24,9 @@ let (map mylib.relativeToRoot [ # common "secrets/nixos.nix" - "modules/nixos/server/server.nix" + # profile: agent (hosts/README.md#host-profiles) + "modules/nixos/agent.nix" "modules/nixos/server/qemu-guest-hardware-configuration.nix" - # headless computer-use environment (sway + desktop automation) - "modules/nixos/desktop/computer-use.nix" # host specific "hosts/idols-${name}" ]) diff --git a/outputs/x86_64-linux/tests/agent-profile/expected.nix b/outputs/x86_64-linux/tests/agent-profile/expected.nix new file mode 100644 index 00000000..c7365816 --- /dev/null +++ b/outputs/x86_64-linux/tests/agent-profile/expected.nix @@ -0,0 +1,24 @@ +{ ... }: +let + common = { + agentsFromHome = true; + devToolsFromHome = true; + nushellSecrets = false; + nixLd = true; + rootlessPodman = true; + docker = false; + linger = true; + agentsInSystem = false; + }; +in +{ + aquamarine = common // { + computerUse = false; + }; + ruby = common // { + computerUse = true; + }; + kana = common // { + computerUse = true; + }; +} diff --git a/outputs/x86_64-linux/tests/agent-profile/expr.nix b/outputs/x86_64-linux/tests/agent-profile/expr.nix new file mode 100644 index 00000000..40547965 --- /dev/null +++ b/outputs/x86_64-linux/tests/agent-profile/expr.nix @@ -0,0 +1,41 @@ +# The `agent` profile (hosts/README.md#host-profiles): every agent host gets +# the full TUI home role, and the system side only carries what needs root. +{ + myvars, + lib, + outputs, +}: +let + username = myvars.username; + hosts = [ + "aquamarine" + "ruby" + "kana" + ]; + hasPackage = name: lib.any (package: (package.pname or "") == name); +in +lib.genAttrs hosts ( + name: + let + system = outputs.nixosConfigurations.${name}.config; + home = system.home-manager.users.${username}; + in + { + # home role: home/linux/tui.nix + agentsFromHome = hasPackage "codex" home.home.packages; + devToolsFromHome = hasPackage "kubectl" home.home.packages; + # ~/.secrets is synced to the desktops only; sourcing it would break nushell. + nushellSecrets = home.modules.shell.secrets.enable; + + # system module: modules/nixos/agent.nix + nixLd = system.programs.nix-ld.enable; + rootlessPodman = system.virtualisation.podman.enable; + docker = system.virtualisation.docker.enable; + linger = system.users.users.${username}.linger; + # User tools stay out of the system profile. + agentsInSystem = hasPackage "codex" system.environment.systemPackages; + + # host switch + computerUse = system.modules.desktop.computerUse.enable; + } +) diff --git a/outputs/x86_64-linux/tests/colmena-parity/expected.nix b/outputs/x86_64-linux/tests/colmena-parity/expected.nix deleted file mode 100644 index 1d6abd36..00000000 --- a/outputs/x86_64-linux/tests/colmena-parity/expected.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ - lib, - outputs, -}: -lib.genAttrs [ "kana" ] ( - name: outputs.nixosConfigurations.${name}.config.system.build.toplevel.drvPath -) diff --git a/outputs/x86_64-linux/tests/colmena-parity/expr.nix b/outputs/x86_64-linux/tests/colmena-parity/expr.nix deleted file mode 100644 index c036ccde..00000000 --- a/outputs/x86_64-linux/tests/colmena-parity/expr.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ - lib, - inputs, -}: -# Colmena must deploy the system that nixosConfigurations evaluates and builds. -lib.genAttrs [ "kana" ] (name: inputs.self.colmenaHive.toplevel.${name}.drvPath) diff --git a/outputs/x86_64-linux/tests/idols-aquamarine/expected.nix b/outputs/x86_64-linux/tests/idols-aquamarine/expected.nix index acec3141..025af01a 100644 --- a/outputs/x86_64-linux/tests/idols-aquamarine/expected.nix +++ b/outputs/x86_64-linux/tests/idols-aquamarine/expected.nix @@ -21,7 +21,7 @@ raplPermissionsExposed = true; collectorFlagsUnique = true; inferenceUserCanRender = true; - noHomeManager = true; + homeManagerStatePersistent = true; pythonInstalled = true; staticAddressMatchesWiredInterface = true; rootIsTmpfs = true; diff --git a/outputs/x86_64-linux/tests/idols-aquamarine/expr.nix b/outputs/x86_64-linux/tests/idols-aquamarine/expr.nix index 1480b9a0..34e5ef34 100644 --- a/outputs/x86_64-linux/tests/idols-aquamarine/expr.nix +++ b/outputs/x86_64-linux/tests/idols-aquamarine/expr.nix @@ -92,8 +92,18 @@ in == builtins.length (lib.unique cfg.services.prometheus.exporters.node.enabledCollectors); inferenceUserCanRender = builtins.elem "render" cfg.users.users.${myvars.username}.extraGroups; - # Headless: no desktop session, no Home Manager. - noHomeManager = !cfg ? home-manager.users; + # Home Manager's own state has to survive the tmpfs root; the agent profile + # itself is covered by ../agent-profile. + homeManagerStatePersistent = + let + dirs = cfg.preservation.preserveAt."/persistent".users.${myvars.username}.directories; + in + builtins.all (path: builtins.any (d: d.directory == "/home/${myvars.username}/${path}") dirs) [ + ".local/state/home-manager" + ".local/state/nix/profiles" + ".codex" + ".config/opencode" + ]; pythonInstalled = builtins.any ( package: lib.hasPrefix "python3-" (package.name or "") ) cfg.environment.systemPackages;