External Systems' reusable NixOS and home-manager modules, split out of external-systems/home so they can be versioned on their own life cycle. This repo has modules only: no machines, no inventory, no clan vars, no packages or overlays (those live in external-systems/pkgs), no IP literals or account ids (those come from external-systems/secrets, or the public placeholders in external-systems/not-secrets).
# flake.nix
inputs.modules = {
url = "github:external-systems/modules";
inputs = {
org-pkgs.follows = "org-pkgs"; # if you already follow external-systems/pkgs
home-manager.follows = "home-manager";
};
};
# a machine
{inputs, ...}: {
imports = [inputs.modules.nixosModules.default];
hostSpec.services.openssh.enable = true;
home-manager.users.alice.imports = [inputs.modules.homeModules.default];
}Every module is off until its hostSpec.* (NixOS) or homeSpec.*
(home-manager) option enables it, so importing default changes nothing
else except the defaults listed below. Single modules are exported too,
e.g. nixosModules.tailscale or homeModules.starship.
- Overlay. The modules read packages built by the consumer:
pkgs.openshell,pkgs.openshell-gateway,pkgs.openshell-driver-vm,pkgs.code-sandbox,pkgs.devenv,pkgs.headroom-lean, and forhomeModules.agentspkgs.pi-coding-agent,pkgs.kimi-code,pkgs.claude-code,pkgs.artifacthub-mcpandpkgs.kubernetes-mcp-server. Apply an overlay that provides them (home'soverlays.defaultdoes).hostSpec.services.odooreadspkgs.odoo19as fixed by external-systems/pkgsoverlays.odoo(in itsoverlays.default): nixpkgs' own odoo19 does not evaluate. It runs 0..n local Odoo instances for testing athttps://<name>.odoo.localhost, reset in seconds withodoo-reset, saved withodoo-snapshot, mail caught by Mailpit, each optionally exposed to the LAN or (with Let's Encrypt) the internet; see the Odoo page. - plasma-manager.
homeModules.plasma-managersetsprograms.plasma; addinputs.plasma-manager.homeModules.plasma-managertohome-manager.sharedModules. - clan-core.
clan,git,ssh,storagebox,openshell-gatewayandodoodeclare clan vars generators, so they needclan.core(any clan machine has it). - Package choice. Modules use plain
pkgsand never pin a nixpkgs branch. To take a package from elsewhere (e.g. unstable), set the package option: the upstream one (programs.ghostty.package,services.tailscale.package, ...) or this repo's (hostSpec.services.ollama.package,hostSpec.hardware.gpu.amd.packages,hostSpec.hardware.gpu.nvidia.packages,homeSpec.programs.media.packages,homeSpec.agents.mcp.github.package). - GPUs from nixos-facter.
hostSpec.hardware.gpu.{amd,intel,nvidia}turn themselves on for the graphics cards inhardware.facter.report(clan sets it frommachines/<name>/facter.json); no tag or setting needed. The NVIDIA module also picks the kernel module and driver branch from the card: open andstablefor Turing and newer, closed andlegacy_580for Maxwell to Volta (e.g. GTX 1070 Ti),legacy_470for Kepler. Set.enable,.openor.branchto override.hostSpec.hardware.bluetoothturns on the same way. Nested KVM needs no module: kvm_amd and kvm_intel nest by default (Linux 4.20 and later), and facter loads the right one. - Users and fleet values. No module names a user, host or account.
These have no default and must be set when their module is enabled:
each
homeSpec.programs.storagebox.boxes.<name>.{user,host}(from the values flake),hostSpec.services.onepassword.polkitPolicyOwnersandhostSpec.services.whisper-dictation.user. Optional, off when unset:homeSpec.agents.machineContext.repoOwner/repoName/repoPath(the machine context's Repo line) andhomeSpec.programs.vscodium.languages.nix.flake(nixd's option source).hostSpec.services.nixtrusts onlyroot; add users withnix.settings.trusted-users. Personal shell aliases go in the consumer'sprograms.bash.shellAliases. - VSCodium.
homeModules.vscodium(homeSpec.programs.vscodium) is VSCodium only. Everything pastenableis per user:languages.{nix,go,python,rust,gleam,zig,web,yaml,shell,markdown}.enable,kubernetes.enable,containers.enable(clientdocker or podman),claude.enable(on by default),direnv.enable(on when any language is),githubActions.enable,remoteSsh.enable,envSpec.enable,todoTree.enable,tweaks.enable(layout, workflow and format-on-save preferences),theme.{color,icons,extensions}andtheme.font.{family,package,size,lineHeight,letterSpacing,ligatures}(null keeps VSCodium's defaults).extraExtensionsandextraSettingsadd more. Language servers the extensions would download (gopls, zls, lemminx, nixd, kubectl, helm) come from Nix; toolchains (go, cargo, zig, gleam, python) come from the project's shell. Extensions come frompkgs.open-vsx-release(Claude frompkgs.vscode-marketplace-release): the consumer applies org-pkgs'overlays.vscode-extensions(inoverlays.default, nix-community/nix-vscode-extensions).githubToken.enable(on by default) wrapscodiumto exportGH_TOKENfrom the user's GitHub PAT file (git.githubAuth) at launch, soghworks in VSCodium however it was started. - Storage Boxes. Mounts are per user. In a home-manager profile set
homeSpec.programs.storagebox.enabled = trueand oneboxes.<name> = {inherit (values.storageboxes.<box>.subAccounts.<user>) user host port;}per box (zero or more; the main account works the same way withvalues.storageboxes.<box>). Each box becomes an rclone remote and a FUSE mount at~/storagebox/<name>(a systemd user unit, started at login or by hand withstartOn = "manual") and an ssh aliasstoragebox-<name>, sorsync storagebox-<name>:dir/ ./dir/works.nixosModules.storageboxdeclares one clan vars key per user,storagebox-ssh-<user>; install its public half on each login withclan vars get <machine> storagebox-ssh-<user>/ssh-public-key | ssh -p23 <login>@<login>.your-storagebox.de install-ssh-key. Nothing mounts until that key is generated. The old host-levelhostSpec.services.storageboxis removed. - 1Password.
hostSpec.services.onepasswordenablesprograms._1password(the setgidopat/run/wrappers/bin/op) andprograms._1password-gui;polkitPolicyOwnershas no default, so set it to the machine's users. Don't also install_1password-clias a package: a Nix-storeopfirst onPATHcan't reach the desktop app.
nixosModules.default: imports home-manager's NixOS module;nixpkgs.config.allowUnfree,nixpkgs.hostPlatform = "x86_64-linux", tmp cleanup and GRUB EFI, allmkDefault.homeModules.default:home.stateVersion = "26.11", US keyboard,programs.home-manager.enable.
- The agent sandbox profiles (
profile-agent*) stay with the code-agent image in home. The skills themselves live in external-systems/skills;homeModules.agentsconsumes them throughinputs.skills.lib.mkSkillsTree. - User profiles (
profile-netsa,profile-wife, ...) stay with the machines in home. - The
lan/wannetworking modules are not carried over: they are replaced by the clanServices IfStatenetifservice, with addresses from the values flake.
| Path | What |
|---|---|
flake-parts/nixosModules/ |
One NixOS module per file, auto-imported with import-tree |
flake-parts/homeModules/ |
One home-manager module per file |
flake-parts/compositions.nix |
The default compositions |
tests/ |
The module eval checks and their fixtures (fixtures/, clan-stub.nix) |
devenv.yaml, devenv.nix |
The dev shell (devenv CLI, imports org-pkgs/devenv) |
documentation/ |
MkDocs site (docs/*.md) and deck (slides/slides.md); flake-module.nix holds the docs.* settings |
The repo follows the org flake standard. Needs Nix and devenv.
devenv allow # auto-activates the shell on cd (after `devenv hook`)
fmt # nix fmt: treefmt with the org linters
check # nix flake check: treefmt, lock-sync, eval, docs
docs-build # static docs to ./result-docs; open index.html from diskThe formatter, docs, slides and the org checks come from
org-pkgs.flakeModules.default, so the repo has no dev inputs and no dev
partition. checks.treefmt runs the org linters (alejandra, deadnix,
statix, nixf-diagnose, shellcheck, sizelint); checks.lock-sync keeps
flake.lock and devenv.lock on one nixpkgs (after
nix flake update org-pkgs, run sync-locks).
checks.eval-all-enabled evaluates a machine with every
hostSpec toggle and a user with every homeSpec toggle on, down to the
system derivation, using stand-ins for the consumer overlay and for
clan.core (tests/fixtures/, tests/clan-stub.nix).