feat hosts

This commit is contained in:
2026-07-27 21:18:00 +09:00
parent 9bb95535cf
commit 91bb7e80db
3 changed files with 161 additions and 74 deletions
+141 -56
View File
@@ -7,6 +7,10 @@ flake. `flake.nix` defines inputs and delegates flake outputs through
flake-parts. Keep configuration with the component that owns it, rather than in flake-parts. Keep configuration with the component that owns it, rather than in
the root flake or an unrelated host. the root flake or an unrelated host.
This file documents the current repository contract, not a hypothetical future
layout. When a structural, ownership, profile, host-role, or validation change
makes any statement here stale, update `AGENTS.md` in the same change.
| Path | Responsibility | | Path | Responsibility |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------- | | ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `modules/applications/` | One software component, including GUI applications, window managers, desktop environments, CLI tools, and editors | | `modules/applications/` | One software component, including GUI applications, window managers, desktop environments, CLI tools, and editors |
@@ -32,20 +36,23 @@ system foundation, service, hardware family, user, profile, or individual host.
Prefer the following placements: Prefer the following placements:
| Configuration | Placement | | Configuration | Placement |
| ---------------------------------------------------- | ------------------------------------------------ | | ---------------------------------------------------- | ------------------------------------------------------- |
| Nix settings shared by every system host | `modules/systems/nix/common.nix` | | Nix settings shared by every system host | `modules/systems/nix/common.nix` |
| NixOS-only boot configuration | `modules/systems/boot/.../nixos.nix` | | NixOS-only boot configuration | `modules/systems/boot/.../nixos.nix` |
| Ghostty-specific configuration | `modules/applications/ghostty/` | | Ghostty-specific configuration | `modules/applications/ghostty/` |
| niri-specific configuration | `modules/applications/niri/` | | niri-specific configuration | `modules/applications/niri/` |
| Applications selected for the niri environment | `modules/profiles/interface/niri/meta.nix` | | Desktop applications shared by GNOME and niri | `modules/profiles/interface/linux-desktop/meta.nix` |
| Applications and services specific to niri | `modules/profiles/interface/niri/meta.nix` |
| GNOME itself | `modules/applications/gnome/` | | GNOME itself | `modules/applications/gnome/` |
| A Linux package plus its macOS Homebrew cask | `modules/applications/<name>/home.nix` and `darwin.nix` |
| Docker daemon and Docker group membership | `modules/services/docker/nixos.nix` | | Docker daemon and Docker group membership | `modules/services/docker/nixos.nix` |
| Reusable ThinkPad-family configuration | `modules/hardwares/thinkpad/` |
| The laptop unit composition | `modules/profiles/platform/laptop/meta.nix` | | The laptop unit composition | `modules/profiles/platform/laptop/meta.nix` |
| The Intel ThinkPad X1 composition | `modules/profiles/platform/thinkpad-x1/meta.nix` |
| The development-environment unit composition | `modules/profiles/workload/development/meta.nix` | | The development-environment unit composition | `modules/profiles/workload/development/meta.nix` |
| Cross-platform fingerprint selection | `modules/profiles/security/fingerprint/meta.nix` |
| A user's OS- and Home Manager-specific configuration | `modules/users/<name>/` | | A user's OS- and Home Manager-specific configuration | `modules/users/<name>/` |
| x1g13-specific monitor layout | `hosts/x1g13/home.nix` | | Host-specific monitor layout | `hosts/<name>/home.nix` |
| x1g13-specific disk UUID | `hosts/x1g13/nixos.nix` | | Generated host disk UUIDs | `hosts/<name>/hardware-configuration.nix` |
| Package replacement or addition | `overlays/` | | Package replacement or addition | `overlays/` |
| Formatter, checks, or Git hooks | `flake/` | | Formatter, checks, or Git hooks | `flake/` |
@@ -283,12 +290,12 @@ use fully qualified unit IDs:
# modules/profiles/interface/niri/meta.nix # modules/profiles/interface/niri/meta.nix
{ {
includes = [ includes = [
"profiles.interface.linux-desktop"
"applications.niri" "applications.niri"
"applications.ghostty"
"applications.noctalia" "applications.noctalia"
"applications.vicinae" "services.ly"
"applications.nautilus" "services.swayidle"
"services.xdg-portal" "services.swaylock"
]; ];
} }
``` ```
@@ -301,41 +308,89 @@ in `meta.includes`.
Application metadata should include only dependencies technically required for Application metadata should include only dependencies technically required for
the application to work. A profile owns the user's choice to adopt several the application to work. A profile owns the user's choice to adopt several
otherwise independent applications together. For example, niri may include the otherwise independent applications together. For example, the niri application
Wayland foundation and xdg-desktop-portal as technical dependencies, while includes the Wayland foundation as a technical dependency. The
`profiles.interface.niri` selects Ghostty, Vicinae, Noctalia, and Nautilus. `profiles.interface.linux-desktop` profile selects Ghostty, Nautilus, and
Ghostty must not depend on niri, and niri-specific keybindings remain owned by Vicinae because both GNOME and niri use them, while `profiles.interface.niri`
the niri unit. selects only the niri-specific shell and services. Ghostty must not depend on
niri, and niri-specific keybindings remain owned by the niri unit.
## Profiles ## Profiles
Profiles compose units by purpose or form factor; they do not replace clear Profiles compose units by purpose or form factor; they do not replace clear
application, system, service, or hardware ownership. Suitable profile namespaces application, system, service, or hardware ownership. The current profile
include: structure is:
```text ```text
modules/profiles/ modules/profiles/
├── README.md
├── base/ ├── base/
├── interface/ ├── interface/
│ ├── niri/ │ ├── cli/
│ ├── linux-desktop/
│ ├── gnome/ │ ├── gnome/
│ ├── cli-minimal/ │ └── niri/
│ └── cli-interactive/ ├── networking/
│ ├── tailscale-client/
│ └── tailscale-subnet-router/
├── platform/ ├── platform/
│ ├── nixos/
│ ├── laptop/ │ ├── laptop/
│ ├── thinkpad/ │ ├── thinkpad-x1/
│ ├── desktop/ │ ├── desktop/
│ ├── vm/ │ └── vm/
│ └── wsl/
├── workload/ ├── workload/
│ ├── development/ │ ├── development/
│ ├── personal/ │ ├── personal/
│ ├── server/ │ ├── server/
│ └── remote/ │ └── remote-access/
└── security/ └── security/
└── secure-boot/ ├── fingerprint/
├── secrets/
├── secure-boot/
└── tpm-storage/
``` ```
`modules/profiles/README.md` is the compatibility inventory for this structure.
Whenever a profile is added, removed, renamed, changes host-class support, or
changes meaning, update that README and every affected `hosts/default.nix`
selection in the same change. Remove stale profile directories and references;
do not retain compatibility aliases.
The profile layers have these responsibilities:
- `base` contains only invariants required by every supported host. It currently
includes only `systems.nix`; optional secrets, interface, hardware, and
workloads do not belong there.
- `interface` describes how the host is operated. `interface.cli` is shared by
NixOS and macOS and includes `tio`. `interface.linux-desktop` owns the common
GNOME/niri desktop selection, including Ghostty, Nautilus, and Vicinae. GNOME
and niri remain independently selectable and do not imply CLI or personal
workloads.
- `platform` describes NixOS foundations and physical or virtual form factors.
macOS does not need an empty symmetric platform profile.
- `workload` describes optional host uses. `workload.development` and
`workload.personal` are cross-platform profiles, not `*-linux` variants.
- `networking` describes network roles and topology rather than user workloads.
- `security` describes optional security policies. Select
`security.fingerprint` instead of listing `systems.fingerprint` directly in a
host. The underlying `systems.fingerprint` unit owns NixOS fingerprint
authentication and macOS Touch ID sudo configuration through its class
fragments.
Do not split a semantic profile into `*-linux` and cross-platform variants merely
because an application is installed differently on each OS. Keep the semantic
profile cross-platform when its purpose is shared, and implement OS differences
inside the owning application unit. For example, Chrome, Vesktop, draw.io,
Slack, and Zoom use Linux Home Manager configuration in `home.nix` and macOS
Homebrew casks in `darwin.nix`. Guard a Linux-only Home Manager package with the
host platform when the same unit also has a Darwin implementation.
An explicitly OS-specific profile is appropriate when the composition itself is
OS-specific, such as `interface.linux-desktop`, `platform.nixos`, or a NixOS
subnet-router. Do not create an OS suffix for a thin package difference that the
owning application unit can express.
The `desktop/` name above is a form-factor profile under `profiles/platform/`, The `desktop/` name above is a form-factor profile under `profiles/platform/`,
not a top-level module category. not a top-level module category.
@@ -352,6 +407,10 @@ conditions holds:
- Other units depend on it. - Other units depend on it.
- It involves a daemon, permissions, or user groups. - It involves a daemon, permissions, or user groups.
`security.tpm-storage` intentionally does not own a disk identifier. A host that
selects it must define `boot.initrd.luks.devices.cryptroot.device` in its own
NixOS module.
## Registry Responsibilities ## Registry Responsibilities
Implement unit discovery with Nix standard functionality such as Implement unit discovery with Nix standard functionality such as
@@ -416,49 +475,46 @@ A host registry may use a specification like this:
```nix ```nix
# hosts/default.nix # hosts/default.nix
{ {
x1g13 = { x1g9 = {
system = "x86_64-linux"; system = "x86_64-linux";
stateVersion = "26.05";
user = "moons"; user = "moons";
path = ./x1g13; path = ./x1g9;
profiles = [ profiles = [
"base" "base"
"platform.thinkpad" "interface.cli"
"interface.niri"
"interface.gnome" "interface.gnome"
"workload.development" "interface.niri"
"platform.thinkpad-x1"
"security.fingerprint"
"workload.personal" "workload.personal"
"security.secure-boot"
];
applications = [
"codex-desktop"
];
units = [
"services.tailscale"
]; ];
}; };
macbook = { m2 = {
system = "aarch64-darwin"; system = "aarch64-darwin";
stateVersion = "26.05";
user = "moons"; user = "moons";
path = ./macbook; path = ./m2;
profiles = [ profiles = [
"base" "base"
"platform.laptop" "interface.cli"
"security.fingerprint"
"workload.development" "workload.development"
"workload.personal" "workload.personal"
]; ];
applications = [
"ghostty"
];
}; };
} }
``` ```
The current role assignment is intentional: x1g9 is a full NixOS desktop with
niri, GNOME, ly, the shared Linux desktop applications, and the personal
workload. m2 is the daily-use macOS development and personal machine. Keep the
desktop sessions independently selectable, and keep m2's development and
personal profiles usable on Darwin.
Treat entries in `profiles` and `applications` as IDs relative to their Treat entries in `profiles` and `applications` as IDs relative to their
respective category roots. Add the category prefixes during host construction: respective category roots. Add the category prefixes during host construction:
@@ -471,24 +527,29 @@ selectedUnits =
``` ```
`units` is an escape hatch for fully qualified service, system, hardware, or `units` is an escape hatch for fully qualified service, system, hardware, or
other unit IDs. Prefer profiles for the main composition; do not make hosts list other unit IDs. Do not use it when an existing profile expresses the concern or
large numbers of low-level units directly. when the concern is reusable enough to deserve a small profile. For example,
select `security.fingerprint`; do not write
`units = [ "systems.fingerprint" ];`. Prefer profiles for the main composition
and keep `units` empty unless a genuinely exceptional low-level selection is
required.
Host modules use normal Nix module semantics and are not Registry-guarded Host modules use normal Nix module semantics and are not Registry-guarded
configuration fragments. For example: configuration fragments. For example:
```text ```text
hosts/x1g13/ hosts/
├── nixos.nix ├── x1g9/
├── home.nix │ ├── nixos.nix
├── hardware-configuration.nix │ └── hardware-configuration.nix
└── disko.nix └── m2/
└── darwin.nix
``` ```
`hosts/x1g13/nixos.nix` may explicitly load `hardware-configuration.nix` and `hosts/x1g9/nixos.nix` explicitly loads `hardware-configuration.nix` with the
`disko.nix` with the normal top-level Nix module `imports`. Do not confuse these normal top-level Nix module `imports`. A future host-local `disko.nix` would be
host imports with the prohibition on top-level `imports` in unit configuration loaded the same way. Do not confuse these host imports with the prohibition on
fragments. top-level `imports` in unit configuration fragments.
Derive the system class from the host's `system`: Derive the system class from the host's `system`:
@@ -527,6 +588,16 @@ When implementing or modifying modules:
- Do not override a path-derived unit ID from `meta.nix`. - Do not override a path-derived unit ID from `meta.nix`.
- Keep technical application dependencies separate from the applications a - Keep technical application dependencies separate from the applications a
personal environment chooses to combine in a profile. personal environment chooses to combine in a profile.
- Keep cross-platform profile names semantic. Put Linux package installation in
an application's `home.nix` and the corresponding macOS Homebrew cask in its
`darwin.nix`; do not create a thin `*-linux` profile for that difference.
- Keep shared GNOME/niri selections in `profiles.interface.linux-desktop` and
session-specific applications or services in the respective GNOME or niri
profile.
- Keep `modules/profiles/README.md`, the profile directories, and host profile
selections synchronized whenever any of them changes.
- Prefer an existing or newly justified profile over a host-level `units` entry
for reusable concerns such as fingerprint authentication.
- Do not rely on module-list ordering to override values. Use Nix module - Do not rely on module-list ordering to override values. Use Nix module
priorities such as `lib.mkDefault`, `lib.mkForce`, `lib.mkBefore`, or priorities such as `lib.mkDefault`, `lib.mkForce`, `lib.mkBefore`, or
`lib.mkAfter` explicitly when required. `lib.mkAfter` explicitly when required.
@@ -563,6 +634,20 @@ dependency closure through `meta.includes`, missing-unit errors, and class
dispatch. Verify that helper files are ignored until explicitly imported and dispatch. Verify that helper files are ignored until explicitly imported and
that directories without a directly contained reserved file remain namespaces. that directories without a directly contained reserved file remain namespaces.
For profile changes, additionally:
- Check for stale profile IDs after every add, removal, or rename.
- Evaluate every affected real host without switching it.
- Evaluate a cross-platform profile on both NixOS and nix-darwin, even when only
one current host selects it.
- Confirm `modules/profiles/README.md` accurately states compatibility and any
required host-owned values.
- When adding a Darwin application fragment, verify the resulting
`homebrew.casks` selection as well as module evaluation.
- Preserve the intended host roles: x1g9 provides niri, GNOME, ly, and the
personal application set, while m2 remains the daily-use development and
personal machine.
## Commit and Pull Request Guidelines ## Commit and Pull Request Guidelines
Recent history favors short, lowercase, imperative subjects such as `fix` and Recent history favors short, lowercase, imperative subjects such as `fix` and
+3 -2
View File
@@ -8,9 +8,11 @@
profiles = [ profiles = [
"base" "base"
"interface.cli" "interface.cli"
"interface.gnome"
"interface.niri"
"platform.thinkpad-x1" "platform.thinkpad-x1"
"security.fingerprint" "security.fingerprint"
"security.secrets" "workload.personal"
]; ];
}; };
@@ -24,7 +26,6 @@
"base" "base"
"interface.cli" "interface.cli"
"security.fingerprint" "security.fingerprint"
"security.secrets"
"workload.development" "workload.development"
"workload.personal" "workload.personal"
]; ];
+5 -4
View File
@@ -44,10 +44,11 @@ when removing it from any supported host would make that host invalid.
| `security.tpm-storage` | NixOS with a host-defined LUKS device | | `security.tpm-storage` | NixOS with a host-defined LUKS device |
Select independent concerns independently in `hosts/default.nix`. For example, Select independent concerns independently in `hosts/default.nix`. For example,
a minimal NixOS laptop can combine `base`, `platform.thinkpad-x1`, and a NixOS desktop can combine `interface.gnome` and `interface.niri` to provide
`interface.cli`, while a daily-use macOS development machine can add both sessions while sharing `interface.linux-desktop`; niri supplies ly. A
`workload.development` and `workload.personal`. Hardware support does not daily-use macOS development machine can add `workload.development` and
implicitly select an interface or workload. `workload.personal`. Hardware support does not implicitly select an interface
or workload.
`security.tpm-storage` deliberately does not own a disk identifier. A host that `security.tpm-storage` deliberately does not own a disk identifier. A host that
selects it must define `boot.initrd.luks.devices.cryptroot.device` in its selects it must define `boot.initrd.luks.devices.cryptroot.device` in its