4.8 KiB
4.8 KiB
AGENTS.md
Purpose
This repo uses the Dendritic Pattern with flake-parts.
Design and change the configuration as a composition of features, not as a host-first tree.
For deeper design rationale and pattern descriptions, refer to .agents/dendritic-design-with-flake-parts.wiki.
Core Terms
- Feature: a flake-parts module under
modules/that defines one coherent concern. - Aspect: a reusable module published at
flake.modules.<module class>.<aspect name>. - Module class: the configuration context of an aspect. This repo primarily uses
nixosandhomeManager. - Feature module: the flake-parts module that defines aspects, flake outputs, options, or shared helpers.
In this repo, flake.nix imports ./modules recursively via inputs.import-tree. Any non-private .nix file under modules/ is therefore treated as a feature module.
Design Principles
- Work bottom-up. Define features first; assemble hosts from features.
- Keep semantic ownership local. A feature should contain the configuration for that concern across all relevant module classes.
- Name aspects semantically. The aspect name should usually match the file or directory name that defines it.
- Prefer small, composable aspects. Build larger configurations with
imports. - Import aspects unconditionally and only within the same module class.
- Put conditions inside module content with
lib.mkIforlib.mkMerge, never aroundimports. - Avoid importing the same aspect multiple times along one import path.
- Keep private helper files next to the feature that uses them and prefix them with
_soimport-treedoes not import them as feature modules. - Put shared schemas and constructors in dedicated shared modules, not ad hoc host files.
Repo Structure
modules/features/: reusable features and most aspect definitions.modules/hosts/<name>/default.nix: host features that assemble NixOS aspects intoflake.modules.nixos.<name>.modules/secrets/: secret-related features shared by hosts.modules/flake-parts.nix: flake-parts entrypoint; defines systems, formatter, andflake.nixosConfigurations.modules/lib.nix: shared constructors and helpers inconfig.meta.lib, especiallymkHostandmkCaddyReverseProxy.modules/data.nix: canonical shared repo data and account attrsets exposed throughmeta.lib.repoandmeta.lib.accounts.modules/features/meta.nix: shared metadata schema formeta.hostandmeta.user.
How Features Are Applied Here
- Reusable NixOS concerns are published as
flake.modules.nixos.<name>. - Reusable Home Manager concerns are published as
flake.modules.homeManager.<name>. - Hosts are aspects too.
orion,polaris, andzenitharenixosaspects assembled from smaller aspects. - Host modules should use
config.meta.lib.mkHostto definemeta.host, base imports, hostname, and state version. - Per-host user declarations should stay inline under
users.<name>using canonical accounts frommeta.lib.accounts, so host-local defaults stay close to the host andmkHostcan wiremeta.hostandmeta.userinto Home Manager consistently. - Features may rely on the
metacontract. Existing modules already readconfig.meta.host,config.meta.user, andconfig.meta.lib.
Preferred Aspect Patterns
- Simple Aspect: use for one self-contained concern in one or more module classes.
- Multi Context Aspect: use when one concern must configure both
nixosandhomeManager. - Inheritance Aspect: use by importing a parent aspect and extending it.
- Conditional Aspect: use
lib.mkMergepluslib.mkIffor conditional content.
Use Collector Aspect only when composition through imports or shared library helpers is insufficient.
Change Rules
- When adding a feature, add or extend aspects under
modules/features/and let hosts opt into them explicitly. - When adding a host, create
modules/hosts/<name>/default.nixand keep host-local generated files private as_hardware.nix,_disk.nix, or similar. - When a feature needs local data or helper code, keep it inside that feature directory and prefix non-feature files with
_when they live undermodules/. - Do not place arbitrary non-feature
.nixfiles undermodules/unless they are intentionally private and excluded from recursive import. - If a concern is shared across hosts, it belongs in a reusable feature, not inline in one host unless it is truly host-specific.
Practical Heuristics
- If you are about to edit a host because of a reusable concern, that concern probably wants its own feature.
- If a Home Manager module needs host or user facts, prefer reading
config.meta.hostorconfig.meta.userinstead of duplicating literals. - If a concern spans system and user space, keep both aspects in one feature so the behavior stays coherent.
- If imports would need to be conditional, redesign the aspect boundary instead.