Compare commits

..

7 Commits

Author SHA1 Message Date
kiri d252de8a3a feat: add nh 2026-04-21 17:54:30 +02:00
kiri 571082c713 feat: codex config 2026-04-21 17:54:26 +02:00
kiri 8cc4690839 feat: add nixosConfigurationPath variable 2026-04-21 17:24:11 +02:00
kiri 6f363afbc8 feat: add gemini and codex config 2026-04-21 17:06:30 +02:00
kiri 8e713666a3 chore: update flake inputs 2026-04-21 16:09:27 +02:00
kiri a73cefb9df refactor: compose hosts and home-manager features explicitly 2026-04-21 16:04:06 +02:00
kiri 8c254f2eb1 docs: add repository agent guidance 2026-04-21 16:03:55 +02:00
35 changed files with 705 additions and 393 deletions
+73
View File
@@ -0,0 +1,73 @@
# 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 `nixos` and `homeManager`.
- **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.mkIf` or `lib.mkMerge`, never around `imports`.
- 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 `_` so `import-tree` does 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 into `flake.modules.nixos.<name>`.
- `modules/secrets/`: secret-related features shared by hosts.
- `modules/flake-parts.nix`: flake-parts entrypoint; defines systems, formatter, and `flake.nixosConfigurations`.
- `modules/lib.nix`: shared constructors in `config.meta.lib`, especially `mkHost`, `mkHostUser`, and `mkCaddyReverseProxy`.
- `modules/users.nix`: canonical user attrsets exposed through `meta.lib.users`.
- `modules/features/meta.nix`: shared metadata schema for `meta.host` and `meta.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`, and `zenith` are `nixos` aspects assembled from smaller aspects.
- Host modules should use `config.meta.lib.mkHost` to define `meta.host`, base imports, hostname, and state version.
- Per-user Home Manager composition should use `config.meta.lib.mkHostUser` so `meta.host` and `meta.user` stay available to Home Manager features.
- Features may rely on the `meta` contract. Existing modules already read `config.meta.host`, `config.meta.user`, and `config.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 `nixos` and `homeManager`.
- **Inheritance Aspect**: use by importing a parent aspect and extending it.
- **Conditional Aspect**: use `lib.mkMerge` plus `lib.mkIf` for 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.nix` and 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 under `modules/`.
- Do not place arbitrary non-feature `.nix` files under `modules/` 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.host` or `config.meta.user` instead 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.
Generated
+213 -76
View File
@@ -1,15 +1,75 @@
{
"nodes": {
"blueprint": {
"inputs": {
"nixpkgs": [
"llm-agents",
"nixpkgs"
],
"systems": [
"llm-agents",
"systems"
]
},
"locked": {
"lastModified": 1776249299,
"narHash": "sha256-Dt9t1TGRmJFc0xVYhttNBD6QsAgHOHCArqGa0AyjrJY=",
"owner": "numtide",
"repo": "blueprint",
"rev": "56131e8628f173d24a27f6d27c0215eff57e40dd",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "blueprint",
"type": "github"
}
},
"bun2nix": {
"inputs": {
"flake-parts": [
"llm-agents",
"flake-parts"
],
"import-tree": "import-tree_2",
"nixpkgs": [
"llm-agents",
"nixpkgs"
],
"systems": [
"llm-agents",
"systems"
],
"treefmt-nix": [
"llm-agents",
"treefmt-nix"
]
},
"locked": {
"lastModified": 1776182890,
"narHash": "sha256-+/VOe8XGq5klpU+I19D+3TcaR7o+Cwbq67KNF7mcFak=",
"owner": "Mic92",
"repo": "bun2nix",
"rev": "648d293c51e981aec9cb07ba4268bc19e7a8c575",
"type": "github"
},
"original": {
"owner": "Mic92",
"ref": "catalog-support",
"repo": "bun2nix",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1773889306,
"narHash": "sha256-PAqwnsBSI9SVC2QugvQ3xeYCB0otOwCacB1ueQj2tgw=",
"lastModified": 1776613567,
"narHash": "sha256-gC9Cp5ibBmGD5awCA9z7xy6MW6iJufhazTYJOiGlCUI=",
"owner": "nix-community",
"repo": "disko",
"rev": "5ad85c82cc52264f4beddc934ba57f3789f28347",
"rev": "32f4236bfc141ae930b5ba2fb604f561fed5219d",
"type": "github"
},
"original": {
@@ -35,6 +95,27 @@
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"llm-agents",
"nixpkgs"
]
},
"locked": {
"lastModified": 1775087534,
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
@@ -57,11 +138,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1776184304,
"narHash": "sha256-No6QGBmIv5ChiwKCcbkxjdEQ/RO2ZS1gD7SFy6EZ7rc=",
"lastModified": 1776777932,
"narHash": "sha256-0R3Yow/NzSeVGUke5tL7CCkqmss4Vmi6BbV6idHzq/8=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "3c7524c68348ef79ce48308e0978611a050089b2",
"rev": "5d5640599a0050b994330328b9fd45709c909720",
"type": "github"
},
"original": {
@@ -85,10 +166,48 @@
"type": "github"
}
},
"import-tree_2": {
"locked": {
"lastModified": 1763762820,
"narHash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=",
"owner": "vic",
"repo": "import-tree",
"rev": "3c23749d8013ec6daa1d7255057590e9ca726646",
"type": "github"
},
"original": {
"owner": "vic",
"repo": "import-tree",
"type": "github"
}
},
"llm-agents": {
"inputs": {
"blueprint": "blueprint",
"bun2nix": "bun2nix",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_3",
"systems": "systems",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1776761469,
"narHash": "sha256-BatEItZry4ZzjLPYhak+G7sydpYaPxMRXLgmJ0s3+Vc=",
"owner": "numtide",
"repo": "llm-agents.nix",
"rev": "fe8991eb26b03e7559807c260d978cc0a987fa9e",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "llm-agents.nix",
"type": "github"
}
},
"lux-pkgs": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_3"
"flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1772315038,
@@ -108,17 +227,17 @@
"inputs": {
"niri-stable": "niri-stable",
"niri-unstable": "niri-unstable",
"nixpkgs": "nixpkgs_4",
"nixpkgs": "nixpkgs_5",
"nixpkgs-stable": "nixpkgs-stable",
"xwayland-satellite-stable": "xwayland-satellite-stable",
"xwayland-satellite-unstable": "xwayland-satellite-unstable"
},
"locked": {
"lastModified": 1776337800,
"narHash": "sha256-yZvCnzf0NDL1vfMGRBkKthRmg8V93FzQ4CQNXhxh0Wg=",
"lastModified": 1776714033,
"narHash": "sha256-O+34yexfSxigXyb5usuzqac7vRHy6gYv7BtNtzDhQNo=",
"owner": "sodiboo",
"repo": "niri-flake",
"rev": "3877b9fd1f78e831b3ea223f9e992c758d13df0f",
"rev": "32bed686f4fd8274a5e4a58d071687a74e19821e",
"type": "github"
},
"original": {
@@ -147,11 +266,11 @@
"niri-unstable": {
"flake": false,
"locked": {
"lastModified": 1776332135,
"narHash": "sha256-7cKy5sGmN4Yt47Op0+A/b3iEMk/E2Ru+UiI42KfiEPc=",
"lastModified": 1776706941,
"narHash": "sha256-nnv27JD0FOOqs1Hh67kydXFzZoEu8e0QyMf0R9AXaIw=",
"owner": "YaLTeR",
"repo": "niri",
"rev": "892470afd3dce5396828dd9b211b19210a16eaeb",
"rev": "e9c182a13c1d12762351ec01ce0ec711d41b0337",
"type": "github"
},
"original": {
@@ -162,14 +281,14 @@
},
"nix-wrapper-modules": {
"inputs": {
"nixpkgs": "nixpkgs_5"
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1776287200,
"narHash": "sha256-rBg1UXDO/EWWrlRoJvv9tj75cjCEoaAQTRO+7ISVCrQ=",
"lastModified": 1776740142,
"narHash": "sha256-kyU4D8J6AgyuNX+CTkFMBGL2ydSesa9TxEElVWFeX4Y=",
"owner": "BirdeeHub",
"repo": "nix-wrapper-modules",
"rev": "0e699fc8acd4ce08b4bbf4175f8e95cca68a3977",
"rev": "7e1b8c7be49c9f2bd3236a7b92819bbedf4e8495",
"type": "github"
},
"original": {
@@ -227,11 +346,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1776067740,
"narHash": "sha256-B35lpsqnSZwn1Lmz06BpwF7atPgFmUgw1l8KAV3zpVQ=",
"lastModified": 1776434932,
"narHash": "sha256-gyqXNMgk3sh+ogY5svd2eNLJ6oEwzbAeaoBrrxD0lKk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7e495b747b51f95ae15e74377c5ce1fe69c1765f",
"rev": "c7f47036d3df2add644c46d712d14262b7d86c0c",
"type": "github"
},
"original": {
@@ -274,6 +393,22 @@
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1776329215,
"narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b86751bc4085f48661017fa226dee99fab6c651b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1772173633,
"narHash": "sha256-MOH58F4AIbCkh6qlQcwMycyk5SWvsqnS/TCfnqDlpj4=",
@@ -289,13 +424,13 @@
"type": "github"
}
},
"nixpkgs_4": {
"nixpkgs_5": {
"locked": {
"lastModified": 1776169885,
"narHash": "sha256-l/iNYDZ4bGOAFQY2q8y5OAfBBtrDAaPuRQqWaFHVRXM=",
"lastModified": 1776548001,
"narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4bd9165a9165d7b5e33ae57f3eecbcb28fb231c9",
"rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc",
"type": "github"
},
"original": {
@@ -305,7 +440,7 @@
"type": "github"
}
},
"nixpkgs_5": {
"nixpkgs_6": {
"locked": {
"lastModified": 1775579569,
"narHash": "sha256-/m3yyS/EnXqoPGBJYVy4jTOsirdgsEZ3JdN2gGkBr14=",
@@ -321,26 +456,26 @@
"type": "github"
}
},
"nixpkgs_6": {
"nixpkgs_7": {
"locked": {
"lastModified": 1776255774,
"narHash": "sha256-bo9Hbl5yPjDRldsn1Stnbsmn/nPF0cVlowuLSGHduuA=",
"rev": "566acc07c54dc807f91625bb286cb9b321b5f42a",
"lastModified": 1776329215,
"narHash": "sha256-mBqzkn7oJti2hqeO8iTbDxKw+1ifxpP53feQ0CEXies=",
"rev": "b86751bc4085f48661017fa226dee99fab6c651b",
"type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre980800.566acc07c54d/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre981196.b86751bc4085/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
}
},
"nixpkgs_7": {
"nixpkgs_8": {
"locked": {
"lastModified": 1776169885,
"narHash": "sha256-l/iNYDZ4bGOAFQY2q8y5OAfBBtrDAaPuRQqWaFHVRXM=",
"lastModified": 1776548001,
"narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "4bd9165a9165d7b5e33ae57f3eecbcb28fb231c9",
"rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc",
"type": "github"
},
"original": {
@@ -350,7 +485,7 @@
"type": "github"
}
},
"nixpkgs_8": {
"nixpkgs_9": {
"locked": {
"lastModified": 1775888245,
"narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=",
@@ -366,33 +501,17 @@
"type": "github"
}
},
"nixpkgs_9": {
"locked": {
"lastModified": 1776329215,
"narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b86751bc4085f48661017fa226dee99fab6c651b",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"noctalia": {
"inputs": {
"nixpkgs": "nixpkgs_7",
"nixpkgs": "nixpkgs_8",
"noctalia-qs": "noctalia-qs"
},
"locked": {
"lastModified": 1776302695,
"narHash": "sha256-xZc9o1JLQpmWn2Dqui323+Tq2Ai4sSdtdvbFZCs4qLo=",
"lastModified": 1776774185,
"narHash": "sha256-riCnQWAxvltNd6KrkzQLdG2EMxODNxjQOB2Z67DA4KU=",
"owner": "noctalia-dev",
"repo": "noctalia-shell",
"rev": "a7c724181fca5d1aff2d47b18fa733504cfdbda2",
"rev": "d7b68652e79bce5813dc4fea7e51636a5da3e1b7",
"type": "github"
},
"original": {
@@ -407,15 +526,15 @@
"noctalia",
"nixpkgs"
],
"systems": "systems",
"treefmt-nix": "treefmt-nix"
"systems": "systems_2",
"treefmt-nix": "treefmt-nix_2"
},
"locked": {
"lastModified": 1775957204,
"narHash": "sha256-d4CVRtAty2GzDYXx4xYQmR+nlOjjKovyprQfZhgLckU=",
"lastModified": 1776585574,
"narHash": "sha256-j35EWhKoGhKrfcXcAOpoRVgXEPQt41Eukji/h59cnjk=",
"owner": "noctalia-dev",
"repo": "noctalia-qs",
"rev": "68e82fe34c68ee839a9c37e3466820e266af0c86",
"rev": "75d180c28a9ab4470e980f3d6f706ad6c5213add",
"type": "github"
},
"original": {
@@ -433,27 +552,27 @@
],
"home-manager": "home-manager",
"import-tree": "import-tree",
"llm-agents": "llm-agents",
"lux-pkgs": "lux-pkgs",
"niri": "niri",
"nix-wrapper-modules": "nix-wrapper-modules",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs_6",
"nixpkgs": "nixpkgs_7",
"noctalia": "noctalia",
"sops-nix": "sops-nix",
"treefmt-nix": "treefmt-nix_2",
"vicinae-extensions": "vicinae-extensions"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": "nixpkgs_8"
"nixpkgs": "nixpkgs_9"
},
"locked": {
"lastModified": 1776119890,
"narHash": "sha256-Zm6bxLNnEOYuS/SzrAGsYuXSwk3cbkRQZY0fJnk8a5M=",
"lastModified": 1776771786,
"narHash": "sha256-DRFGPfFV6hbrfO9a1PH1FkCi7qR5FgjSqsQGGvk1rdI=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "d4971dd58c6627bfee52a1ad4237637c0a2fb0cd",
"rev": "bef289e2248991f7afeb95965c82fbcd8ff72598",
"type": "github"
},
"original": {
@@ -463,6 +582,21 @@
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
@@ -477,7 +611,7 @@
"type": "github"
}
},
"systems_2": {
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
@@ -495,8 +629,7 @@
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"noctalia",
"noctalia-qs",
"llm-agents",
"nixpkgs"
]
},
@@ -516,7 +649,11 @@
},
"treefmt-nix_2": {
"inputs": {
"nixpkgs": "nixpkgs_9"
"nixpkgs": [
"noctalia",
"noctalia-qs",
"nixpkgs"
]
},
"locked": {
"lastModified": 1775636079,
@@ -561,15 +698,15 @@
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs_10",
"systems": "systems_2",
"systems": "systems_3",
"vicinae": "vicinae"
},
"locked": {
"lastModified": 1775911073,
"narHash": "sha256-Fa5JvMFVwBzbnOjEV2Cer8ak0zF/CDwdHT7+wslL30w=",
"lastModified": 1776686803,
"narHash": "sha256-eOyZdPdhcJ/z3r5JuYvIsUs/+GacSLw7wGCz/ZjvGFY=",
"owner": "vicinaehq",
"repo": "extensions",
"rev": "d12bcb134d45dedad1a28a18e1cd8807353338d0",
"rev": "c89b22546cb8015b5a116bdf016996d7f8a2cfed",
"type": "github"
},
"original": {
+1 -1
View File
@@ -6,13 +6,13 @@
flake-parts.follows = "lux-pkgs/flake-parts";
home-manager.url = "github:nix-community/home-manager";
import-tree.url = "github:vic/import-tree";
llm-agents.url = "github:numtide/llm-agents.nix";
niri.url = "github:sodiboo/niri-flake";
nix-wrapper-modules.url = "github:BirdeeHub/nix-wrapper-modules";
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
noctalia.url = "github:noctalia-dev/noctalia-shell";
sops-nix.url = "github:Mic92/sops-nix";
treefmt-nix.url = "github:numtide/treefmt-nix";
#vicinae.url = "github:vicinaehq/vicinae";
vicinae-extensions.url = "github:vicinaehq/extensions";
+71
View File
@@ -0,0 +1,71 @@
{ inputs, ... }:
let
sharedContext = ''
# AI Global Context
This shared Codex and Gemini context is intentionally minimal for now.
TODO: add persistent global AI instructions.
'';
in
{
flake.modules.nixos.ai = {
nixpkgs.overlays = [ inputs.llm-agents.overlays.default ];
nix.settings = {
extra-substituters = [ "https://cache.numtide.com" ];
extra-trusted-public-keys = [
"niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g="
];
};
};
flake.modules.homeManager.ai =
{
config,
lib,
pkgs,
...
}:
{
home.sessionVariables.GEMINI_CONFIG_DIR = "${config.xdg.configHome}/gemini";
programs.gemini-cli = {
enable = true;
package = pkgs.llm-agents.gemini-cli;
context.AGENTS = sharedContext;
settings = {
context = {
fileName = [
"AGENTS.md"
"GEMINI.md"
];
loadMemoryFromIncludeDirectories = true;
};
model.name = "gemini-3.1-pro-preview";
};
};
programs.codex = {
enable = true;
package = pkgs.llm-agents.codex;
context = sharedContext;
settings = {
model = "gpt-5.4";
model_reasoning_effort = "high";
plan_mode_reasoning_effort = "high";
tui.status_line = [
"model-with-reasoning"
"current-dir"
"git-branch"
"context-remaining"
"five-hour-limit"
];
projects.${config.meta.user.nixosConfigurationPath}.trust_level = "trusted";
sandbox_mode = "workspace-write";
personality = "pragmatic";
features.undo = true;
};
};
};
}
+18
View File
@@ -0,0 +1,18 @@
{ config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager.cli-base = {
imports = [
homeModules.terminal
homeModules.shell
homeModules.neovim
homeModules.nh
homeModules.git
homeModules.dev-tools
homeModules.podman
homeModules.ai
];
};
}
-50
View File
@@ -1,50 +0,0 @@
{ config, ... }:
let
nixosModules = config.flake.modules.nixos;
in
{
flake.modules.nixos."core-base" = {
imports = [
nixosModules."meta-host"
nixosModules."home-manager-base"
nixosModules.nix
nixosModules."region-nl"
nixosModules."sops-host"
];
};
flake.modules.nixos."server-base" = {
imports = [
nixosModules."core-base"
nixosModules.openssh
];
};
flake.modules.nixos."workstation-base" = {
imports = [
nixosModules."core-base"
nixosModules."standard-boot"
nixosModules.sddm
nixosModules.niri
nixosModules.audio
nixosModules.bluetooth
nixosModules.flatpak
nixosModules.fonts
nixosModules.networking
nixosModules.printing
nixosModules."qbittorrent-client"
];
users.mutableUsers = false;
services.dbus.implementation = "broker";
programs.nix-ld.enable = true;
environment.localBinInPath = true;
};
flake.modules.nixos."portable-host" = {
hardware.enableRedistributableFirmware = true;
services.fwupd.enable = true;
};
}
+17
View File
@@ -0,0 +1,17 @@
{ config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager.desktop-session = {
imports = [
homeModules.niri
homeModules.clipboard
homeModules.local-apps
homeModules.mpv
homeModules.vicinae
homeModules.xdg
homeModules.theme
];
};
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
flake.modules.homeManager."dev-tools" =
flake.modules.homeManager.dev-tools =
{ config, ... }:
{
home.sessionVariables.CARGO_HOME = "${config.xdg.dataHome}/cargo";
+9 -2
View File
@@ -3,7 +3,14 @@ let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager."ergon-workstation" = {
imports = [ homeModules.nix ];
flake.modules.homeManager.ergon-workstation = {
imports = [
homeModules.cli-base
homeModules.desktop-session
homeModules.personal-productivity
homeModules.ssh-client
homeModules.sops
homeModules.nix
];
};
}
-11
View File
@@ -1,11 +0,0 @@
{
flake.modules.homeManager.gemini =
{ config, ... }:
{
home.sessionVariables.GEMINI_CONFIG_DIR = "${config.xdg.configHome}/gemini";
programs.gemini-cli.enable = true;
programs.opencode.enable = true;
programs.npm.enable = true;
};
}
-26
View File
@@ -1,26 +0,0 @@
{
inputs,
config,
...
}:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.nixos."home-manager-base" =
{ ... }:
{
imports = [ inputs.home-manager.nixosModules.home-manager ];
home-manager = {
useGlobalPkgs = true;
backupFileExtension = "bak";
extraSpecialArgs = { inherit inputs; };
sharedModules = [ homeModules."meta-context" ];
};
security.sudo.extraConfig = ''
Defaults env_keep+=SSH_AUTH_SOCK
'';
};
}
+31
View File
@@ -0,0 +1,31 @@
{
config,
inputs,
...
}:
let
nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.nixos.host-base = {
imports = [
nixosModules.meta
inputs.home-manager.nixosModules.home-manager
nixosModules.ai
nixosModules.nix
nixosModules.region-nl
];
home-manager = {
useGlobalPkgs = true;
backupFileExtension = "bak";
extraSpecialArgs = { inherit inputs; };
sharedModules = [ homeModules.meta ];
};
security.sudo.extraConfig = ''
Defaults env_keep+=SSH_AUTH_SOCK
'';
};
}
+12
View File
@@ -0,0 +1,12 @@
{ config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager.kiri-server = {
imports = [
homeModules.cli-base
homeModules.syncthing
];
};
}
+8 -13
View File
@@ -3,21 +3,16 @@ let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager."kiri-workstation" = {
flake.modules.homeManager.kiri-workstation = {
imports = [
homeModules.cli-base
homeModules.desktop-session
homeModules.personal-productivity
homeModules.ssh-client
homeModules.sops
homeModules.nix
homeModules.bitwarden
homeModules.email
homeModules.pim
homeModules.mpv
homeModules.niri
homeModules.clipboard
homeModules."local-apps"
homeModules."qbittorrent-client"
homeModules.vicinae
homeModules.xdg
homeModules.theme
homeModules.noctalia
homeModules.syncthing
homeModules.qbittorrent-client
];
};
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
flake.modules.homeManager."local-apps" =
flake.modules.homeManager.local-apps =
{ pkgs, ... }:
{
home.sessionVariables.BROWSER = "vivaldi";
+8 -15
View File
@@ -20,7 +20,7 @@ let
);
userType = lib.types.submodule (
{ ... }:
{ config, ... }:
{
options = {
name = lib.mkOption {
@@ -35,6 +35,11 @@ let
type = lib.types.str;
};
nixosConfigurationPath = lib.mkOption {
type = lib.types.str;
default = "${config.homeDirectory}/.config/nixos";
};
emails = lib.mkOption {
type = lib.types.attrsOf emailType;
};
@@ -99,18 +104,6 @@ let
type = lib.types.str;
};
kind = lib.mkOption {
type = lib.types.enum [
"server"
"workstation"
];
};
traits = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
displays = lib.mkOption {
type = lib.types.attrsOf displayType;
default = { };
@@ -125,13 +118,13 @@ let
);
in
{
flake.modules.nixos."meta-host" = {
flake.modules.nixos.meta = {
options.meta.host = lib.mkOption {
type = hostType;
};
};
flake.modules.homeManager."meta-context" = {
flake.modules.homeManager.meta = {
options.meta = {
host = lib.mkOption {
type = lib.types.nullOr hostType;
+1 -1
View File
@@ -1,5 +1,5 @@
{
flake.modules.nixos."server-firewall" = {
flake.modules.nixos.server-firewall = {
networking = {
firewall.enable = true;
firewall.allowPing = false;
+10
View File
@@ -0,0 +1,10 @@
{
flake.modules.homeManager.nh =
{ config, ... }:
{
programs.nh = {
enable = true;
flake = config.meta.user.nixosConfigurationPath;
};
};
}
+7 -1
View File
@@ -1,6 +1,12 @@
{
flake.modules.nixos.nix =
{ inputs, ... }:
{
config,
inputs,
lib,
pkgs,
...
}:
{
nixpkgs.config.allowUnfree = true;
+23 -1
View File
@@ -1,3 +1,19 @@
{
config,
lib,
...
}:
let
homeModules = config.flake.modules.homeManager;
baseSettings = import ./_noctalia-config.nix;
portableSettings = lib.recursiveUpdate baseSettings {
bar.widgets.right = baseSettings.bar.widgets.right ++ [
{
id = "Battery";
}
];
};
in
{
flake.modules.homeManager.noctalia =
{
@@ -17,7 +33,13 @@
}
);
settings = import ./_noctalia-config.nix;
settings = baseSettings;
};
};
flake.modules.homeManager.noctalia-portable = {
imports = [ homeModules.noctalia ];
programs.noctalia-shell.settings = lib.mkForce portableSettings;
};
}
@@ -0,0 +1,13 @@
{ config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager.personal-productivity = {
imports = [
homeModules.bitwarden
homeModules.email
homeModules.pim
];
};
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
flake.modules.nixos."qbittorrent-client" = {
flake.modules.nixos.qbittorrent-client = {
networking.firewall = {
allowedTCPPorts = [ 43864 ];
allowedUDPPorts = [ 43864 ];
};
};
flake.modules.homeManager."qbittorrent-client" =
flake.modules.homeManager.qbittorrent-client =
{ pkgs, ... }:
{
home.packages = [ pkgs.qbittorrent ];
+1 -1
View File
@@ -1,6 +1,6 @@
{ ... }:
{
flake.modules.nixos."region-nl" = {
flake.modules.nixos.region-nl = {
time.timeZone = "Europe/Amsterdam";
i18n.defaultLocale = "en_US.UTF-8";
+2 -2
View File
@@ -1,5 +1,5 @@
{
flake.modules.nixos."deluge-service" =
flake.modules.nixos.deluge-service =
{ ... }:
{
sops.secrets.deluge-auth-file = { };
@@ -10,7 +10,7 @@
};
};
flake.modules.homeManager."deluge-client" =
flake.modules.homeManager.deluge-client =
{ pkgs, ... }:
{
home.packages = [ pkgs.deluge ];
+4 -7
View File
@@ -1,6 +1,6 @@
{ ... }:
{
flake.modules.nixos."ssh-agent-auth" = {
flake.modules.nixos.ssh-agent-auth = {
security.pam = {
sshAgentAuth.enable = true;
services.sudo.sshAgentAuth = true;
@@ -12,18 +12,15 @@
config,
...
}:
let
isServer = config.meta.host.kind == "server";
hostUserNames = builtins.attrNames config.meta.host.users;
in
{
services.openssh.openFirewall = true;
services.openssh = {
enable = true;
openFirewall = isServer;
settings = {
PermitRootLogin = "no";
PasswordAuthentication = false;
AllowUsers = hostUserNames;
AllowUsers = builtins.attrNames config.meta.host.users;
};
};
};
+1 -1
View File
@@ -1,5 +1,5 @@
{
flake.modules.homeManager."ssh-client" =
flake.modules.homeManager.ssh-client =
{ config, ... }:
{
programs.ssh = {
+1 -1
View File
@@ -1,6 +1,6 @@
{ ... }:
{
flake.modules.nixos."standard-boot" =
flake.modules.nixos.standard-boot =
{ config, pkgs, ... }:
{
boot = {
-29
View File
@@ -1,29 +0,0 @@
{ config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager."common-user-base" = {
imports = [
homeModules.terminal
homeModules.shell
homeModules.neovim
homeModules.git
homeModules."dev-tools"
homeModules.podman
homeModules.gemini
];
};
flake.modules.homeManager."server-user-base" = {
imports = [ homeModules."common-user-base" ];
};
flake.modules.homeManager."workstation-user-base" = {
imports = [
homeModules."common-user-base"
homeModules."ssh-client"
homeModules."sops-admin"
];
};
}
+29
View File
@@ -0,0 +1,29 @@
{ config, ... }:
let
nixosModules = config.flake.modules.nixos;
in
{
flake.modules.nixos.workstation-base = {
imports = [
nixosModules.host-base
nixosModules.sops-admin-key-file
nixosModules.standard-boot
nixosModules.sddm
nixosModules.niri
nixosModules.audio
nixosModules.bluetooth
nixosModules.flatpak
nixosModules.fonts
nixosModules.networking
nixosModules.printing
nixosModules.qbittorrent-client
];
users.mutableUsers = false;
services.dbus.implementation = "broker";
programs.nix-ld.enable = true;
environment.localBinInPath = true;
};
}
+18 -8
View File
@@ -1,10 +1,15 @@
{ config, ... }:
{
inputs,
config,
...
}:
let
nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager;
metaLib = config.meta.lib;
in
{
flake.modules.nixos."orion-admin" =
flake.modules.nixos.orion-admin =
{ pkgs, ... }:
{
users.users.kiri = {
@@ -21,22 +26,27 @@ in
flake.modules.nixos.orion = metaLib.mkHost {
name = "orion";
kind = "server";
users = {
inherit (metaLib.users) kiri;
};
imports = [
nixosModules."server-base"
nixosModules.host-base
nixosModules.sops-host-ssh-key
nixosModules.openssh
nixosModules.caddy
nixosModules."server-firewall"
nixosModules."ssh-agent-auth"
nixosModules."orion-admin"
nixosModules.server-firewall
nixosModules.ssh-agent-auth
nixosModules.orion-admin
nixosModules.vaultwarden
nixosModules.radicale
nixosModules.actual
nixosModules.gitea
nixosModules."user-kiri"
(metaLib.mkHostUser {
account = metaLib.users.kiri;
needsPassword = false;
homeImports = [ homeModules.kiri-server ];
})
./_hardware.nix
./_disk.nix
];
+18 -4
View File
@@ -5,12 +5,12 @@
}:
let
nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager;
metaLib = config.meta.lib;
in
{
flake.modules.nixos.polaris = metaLib.mkHost {
name = "polaris";
kind = "workstation";
displays = {
"LG Electronics LG ULTRAGEAR 103NTYT8R290" = {
@@ -33,10 +33,24 @@ in
};
imports = [
nixosModules."workstation-base"
nixosModules.workstation-base
nixosModules.steam
nixosModules."user-kiri"
nixosModules."user-ergon"
(metaLib.mkHostUser {
account = metaLib.users.kiri;
needsPassword = true;
homeImports = [
homeModules.kiri-workstation
homeModules.noctalia
];
})
(metaLib.mkHostUser {
account = metaLib.users.ergon;
needsPassword = true;
homeImports = [
homeModules.ergon-workstation
homeModules.noctalia
];
})
./_hardware.nix
]
++ (with inputs.nixos-hardware.nixosModules; [
+22 -6
View File
@@ -5,13 +5,12 @@
}:
let
nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager;
metaLib = config.meta.lib;
in
{
flake.modules.nixos.zenith = metaLib.mkHost {
name = "zenith";
kind = "workstation";
traits = [ "portable" ];
displays = {
"California Institute of Technology 0x1410 Unknown" = {
@@ -35,10 +34,27 @@ in
};
imports = [
nixosModules."workstation-base"
nixosModules."portable-host"
nixosModules."user-kiri"
nixosModules."user-ergon"
nixosModules.workstation-base
(metaLib.mkHostUser {
account = metaLib.users.kiri;
needsPassword = true;
homeImports = [
homeModules.kiri-workstation
homeModules.noctalia-portable
];
})
(metaLib.mkHostUser {
account = metaLib.users.ergon;
needsPassword = true;
homeImports = [
homeModules.ergon-workstation
homeModules.noctalia-portable
];
})
{
hardware.enableRedistributableFirmware = true;
services.fwupd.enable = true;
}
./_hardware.nix
inputs.nixos-hardware.nixosModules.lenovo-yoga-7-14ARH7-amdgpu
];
+69 -7
View File
@@ -7,8 +7,6 @@ let
mkHost =
{
name,
kind,
traits ? [ ],
displays ? { },
users ? { },
imports ? [ ],
@@ -18,9 +16,7 @@ let
meta.host = {
inherit
displays
kind
name
traits
users
;
};
@@ -52,11 +48,69 @@ let
"reverse_proxy :${toString port}"
else
''
reverse_proxy :${toString port} {
${body}
}
reverse_proxy :${toString port} {
${body}
}
'';
};
mkHostUser =
{
account,
homeImports,
needsPassword ? false,
stateVersion ? "24.05",
}:
{
config,
pkgs,
...
}:
let
name = account.name;
primaryEmails = lib.filter (email: email.primary) (builtins.attrValues account.emails);
in
{
assertions = [
{
assertion = builtins.length primaryEmails == 1;
message = "User ${name} must define exactly one primary email entry.";
}
];
programs.zsh.enable = true;
sops.secrets = lib.optionalAttrs needsPassword {
"hashed-password-${name}".neededForUsers = true;
};
users.users.${name} = {
name = account.name;
home = account.homeDirectory;
isNormalUser = true;
shell = pkgs.zsh;
extraGroups = [
"wheel"
"networkmanager"
];
}
// lib.optionalAttrs needsPassword {
hashedPasswordFile = config.sops.secrets."hashed-password-${name}".path;
};
home-manager.users.${name} = {
imports = homeImports;
meta = {
host = config.meta.host;
user = account;
};
home = {
username = account.name;
homeDirectory = account.homeDirectory;
inherit stateVersion;
};
};
};
in
{
options.meta.lib.mkHost = lib.mkOption {
@@ -73,6 +127,13 @@ in
readOnly = true;
};
options.meta.lib.mkHostUser = lib.mkOption {
type = lib.types.raw;
description = "Internal helper for explicit per-host user assembly.";
internal = true;
readOnly = true;
};
options.meta.lib.users = lib.mkOption {
type = lib.types.attrs;
description = "Canonical user attrsets shared by host definitions.";
@@ -84,6 +145,7 @@ in
inherit
mkCaddyReverseProxy
mkHost
mkHostUser
;
};
}
+20 -21
View File
@@ -1,43 +1,42 @@
{
inputs,
config,
...
}:
let
nixosModules = config.flake.modules.nixos;
sopsAdminKeyPath = "/var/lib/sops/keys.txt";
in
{
flake.modules.nixos."sops-host" =
{
config,
lib,
...
}:
flake.modules.nixos.sops = {
imports = [ inputs.sops-nix.nixosModules.sops ];
sops.defaultSopsFile = ./secrets.yaml;
};
flake.modules.nixos.sops-admin-key-file =
{ lib, ... }:
let
useHostSshKey = config.meta.host.kind == "server";
useAdminKeyFile = config.meta.host.kind != "server";
adminKeyDir = builtins.dirOf sopsAdminKeyPath;
in
{
imports = [ inputs.sops-nix.nixosModules.sops ];
imports = [ nixosModules.sops ];
sops = {
defaultSopsFile = ./secrets.yaml;
age =
lib.optionalAttrs useHostSshKey {
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
}
// lib.optionalAttrs useAdminKeyFile {
keyFile = sopsAdminKeyPath;
};
};
sops.age.keyFile = sopsAdminKeyPath;
systemd.tmpfiles.rules = lib.optionals useAdminKeyFile [
systemd.tmpfiles.rules = [
"d ${adminKeyDir} 0750 root wheel -"
"z ${sopsAdminKeyPath} 0640 root wheel -"
];
};
flake.modules.homeManager."sops-admin" =
flake.modules.nixos.sops-host-ssh-key = {
imports = [ nixosModules.sops ];
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
};
flake.modules.homeManager.sops =
{
pkgs,
...
+1 -105
View File
@@ -1,11 +1,5 @@
{
config,
lib,
...
}:
{ ... }:
let
homeModules = config.flake.modules.homeManager;
kiri = {
name = "kiri";
realName = "Jelle Spreeuwenberg";
@@ -46,98 +40,6 @@ let
};
};
};
mkUserModules =
{
name,
extraHomeImports ? [ ],
}:
let
userModuleName = "user-${name}";
workstationModuleName = "${name}-workstation";
in
{
nixos =
{
config,
pkgs,
...
}:
let
account = config.meta.host.users.${name};
primaryEmails = lib.filter (email: email.primary) (builtins.attrValues account.emails);
isWorkstation = config.meta.host.kind == "workstation";
hasWorkstationModule = builtins.hasAttr workstationModuleName homeModules;
baseModuleName = if isWorkstation then "workstation-user-base" else "server-user-base";
in
{
assertions = [
{
assertion = builtins.length primaryEmails == 1;
message = "User ${name} must define exactly one primary email entry.";
}
];
programs.zsh.enable = true;
sops.secrets = lib.optionalAttrs isWorkstation {
"hashed-password-${name}".neededForUsers = true;
};
users.users.${name} = {
name = account.name;
home = account.homeDirectory;
isNormalUser = true;
shell = pkgs.zsh;
extraGroups = [
"wheel"
"networkmanager"
];
}
// lib.optionalAttrs isWorkstation {
hashedPasswordFile = config.sops.secrets."hashed-password-${name}".path;
};
home-manager.users.${name} = {
imports = [
homeModules.${baseModuleName}
homeModules.${userModuleName}
]
++ extraHomeImports
++ lib.optionals (isWorkstation && hasWorkstationModule) [
homeModules.${workstationModuleName}
];
meta = {
host = config.meta.host;
user = account;
};
};
};
homeManager =
{ config, ... }:
let
account = config.meta.user;
in
{
home = {
username = account.name;
homeDirectory = account.homeDirectory;
stateVersion = "24.05";
};
};
};
kiriModules = mkUserModules {
name = "kiri";
extraHomeImports = [
homeModules.syncthing
];
};
ergonModules = mkUserModules {
name = "ergon";
};
in
{
meta.lib.users = {
@@ -146,10 +48,4 @@ in
kiri
;
};
flake.modules.nixos."user-kiri" = kiriModules.nixos;
flake.modules.nixos."user-ergon" = ergonModules.nixos;
flake.modules.homeManager."user-kiri" = kiriModules.homeManager;
flake.modules.homeManager."user-ergon" = ergonModules.homeManager;
}