Author SHA1 Message Date
Emma ThorpeandClaude Opus 4.8 87318cd04d fix(docs): reformat README module table to satisfy treefmt
CI / flake (pull_request) Successful in 10s
The module-catalogue table in README.md was committed without prettier's
alignment, so the treefmt formatting flake check fails. This has left
CI (nix flake check) red on main since the #56 docs merge and blocks
every PR that triggers the full check.

Reformat with the flake's pinned formatter; no content change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 11:10:02 +01:00
lyrathorpe ad9decdf47 Merge pull request 'docs: repo layout, module catalogue, host READMEs, stale-path fixes' (#56) from docs/audit-improvements into main
CI / flake (push) Failing after 4m2s
Reviewed-on: #56
2026-07-06 15:37:28 +01:00
lyrathorpe 819633260e docs(rpi5): fix stale module paths
CI / flake (pull_request) Successful in 19s
2026-07-06 15:27:03 +01:00
lyrathorpe 610d5d8b28 docs(macpro31): fix stale module paths 2026-07-06 15:27:02 +01:00
lyrathorpe 58c0004f20 docs(t400): fix stale module paths 2026-07-06 15:27:02 +01:00
lyrathorpe f57d6ab1f9 docs(darwin): add host README 2026-07-06 15:27:01 +01:00
lyrathorpe 574773de73 docs(edaas): add host README 2026-07-06 15:27:00 +01:00
lyrathorpe a857365cc3 docs: add repo layout, module catalogue and add-a-host guide 2026-07-06 15:26:59 +01:00
lyrathorpeandEmma Thorpe c06a57f249 chore: post-refactor cleanups (#50-#53) (#54)
CI / flake (push) Successful in 3m30s
## Summary

Follow-up cleanups from the post-refactor audit (issues #50–#53). All behaviour-preserving except the work-host changes (kube-tmux + Lens removal).

## Changes

- **#51** `refactor(ssh)` — move `services.openssh.enable` + `firewall.allowedTCPPorts = [ 22 ]` into `modules/ssh.nix`; drop the duplicated lines from T400, MacPro31, RPi5.
- **#50** `fix/feat(work)` — load kube-tmux from a pinned `flake = false` input (it is not in nixpkgs) and reference `${inputs.kube-tmux}/kube.tmux` directly, so the status line no longer depends on a manual `$HOME/code/kube-tmux` checkout. (Supersedes the interim file-existence guard.)
- **#52** `chore` — gitignore the untracked `tf-inspect/` scratch project.
- **#53** `chore` — remove the unused Lens package entirely (`pkgs.lens` + its unfree entry; `unfreePackages` is now just `claude-code`), fix the `nil`→`nil_ls` LSP doc, remove the redundant `.editorconfig` block, name the RPi5 Docker subnet in a `let` binding.

## Deferred (from #53, noted in the commit)

- `.gitignore` firmware entry — documented behaviour, low value, left as-is.
- Per-eval `nixpkgs-unstable` overlay import — inherently per-system; no clean single-import hoist.

## Verification

- `nix flake check` passes (treefmt, deadnix, statix, pre-commit, all hosts + Darwin + homeConfigurations).
- Derivation-path diff vs `main`: `lyrathorpe-mbp`, `lyrathorpe-t400`, `lyrathorpe-macpro31`, `lyrathorpe-rpi5` are byte-identical (confirms #51 and the subnet `let` binding change nothing). Only `emmathorpe-edaas` differs — the kube-tmux input (#50) and the Lens removal (#53).

Closes #50, #51, #52, #53.

---------

Co-authored-by: Emma Thorpe <emma.thorpe@citrix.com>
Reviewed-on: #54
2026-06-29 14:09:38 +01:00
17 changed files with 301 additions and 77 deletions
-6
View File
@@ -8,12 +8,6 @@ indent_size = 2
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.{nix,yaml,yml,json,md,sh,toml}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
# Markdown uses trailing whitespace for hard line breaks. # Markdown uses trailing whitespace for hard line breaks.
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false
+3
View File
@@ -2,3 +2,6 @@ modules/firmware/*
# vim swap files # vim swap files
*.swp *.swp
# Local scratch project, not part of this flake.
tf-inspect/
+99 -5
View File
@@ -12,16 +12,67 @@ Defined in the host table in [`flake.nix`](./flake.nix):
| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) | | `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) |
| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./hosts/T400/README.md) | | `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./hosts/T400/README.md) |
| `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](./hosts/MacPro31/README.md) | | `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](./hosts/MacPro31/README.md) |
| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) | | `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) — [notes](./hosts/EDaaS/README.md) |
| `lyrathorpe-rpi5` | `aarch64-linux` | Raspberry Pi 5 headless server: Docker host + nginx reverse proxy — [install notes](./hosts/RPi5/README.md) | | `lyrathorpe-rpi5` | `aarch64-linux` | Raspberry Pi 5 headless server: Docker host + nginx reverse proxy — [install notes](./hosts/RPi5/README.md) |
| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) | | `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) — [notes](./hosts/Darwin/README.md) |
Shared layers: `home` (home-manager: shell, git, editor), Shared layers: `home` (home-manager: shell, git, editor),
`modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches), `modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches),
`modules/workstation.nix` (physical graphical hosts: audio, thermald, `modules/workstation.nix` (physical graphical hosts: audio, thermald,
earlyoom, fwupd), `modules/laptop.nix` (laptops: Wi-Fi, Bluetooth, power, earlyoom, fwupd), `modules/laptop.nix` (laptops: Wi-Fi, Bluetooth, power,
lid), and `modules/ssh.nix` (key-only sshd). The x86 hosts also pull lid), `modules/desktop.nix` (wired desktops: NetworkManager), and
`nixos-hardware` profiles. `modules/ssh.nix` (key-only sshd). The x86 hosts also pull `nixos-hardware`
profiles. The full module catalogue is below.
## Repository layout
```
flake.nix # inputs, mkHost/mkDarwinHost, the host tables, dev shell + checks
flake.lock # pinned input revisions (Renovate keeps this fresh)
modules/ # reusable NixOS system modules (see "Module catalogue")
home/ # home-manager profile: shell, git, editor, claude, desktop, sway
users/ # identity registry + per-user home extras (see "Users")
hosts/<Name>/ # per-machine config: configuration.nix + hardware-configuration.nix
lib/ # small pure helpers (currently the Catppuccin Mocha palette)
.gitea/workflows/ # CI (nix flake check + per-host eval)
statix.toml # lint config (house-style lints disabled)
.editorconfig # base whitespace style
tf-inspect/ # UNRELATED scratch project (gitignored, its own git repo);
# RouterOS / home-services Terraform, not part of this flake
```
Each `nixosConfiguration` / `darwinConfiguration` is assembled in `flake.nix`
from three layers: the shared `baseModules` (or `darwinBaseModules`), the
per-form-factor and `nixos-hardware` modules listed in the host table, and the
per-machine `hosts/<Name>/configuration.nix`. Home-manager is wired in as a
system module; each user's home is composed from the `homeModules` list in that
host's table entry.
## Module catalogue
Reusable NixOS modules under [`modules/`](./modules). "Imported by" says how a
module reaches a host: **baseModules** (every NixOS host, via `flake.nix`),
**host table** (listed explicitly per host in `flake.nix`), or **transitively**
(pulled in by another module's `imports`).
| Module | Imported by | What it does / when to use it |
| ------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `common-nixos.nix` | baseModules (all NixOS) | Timezone/locale, store hygiene (auto-optimise, big download buffer, **no** auto-GC), the nix-community binary cache, `nix-ld`, base CLI (`git`, `fastfetch`), and the fleet-wide font stack. |
| `users.nix` | baseModules (all NixOS) | Builds `users.users` from the registry for the host's `hostUsers`; enables zsh; enables Firefox + Thunderbird **only** when `features.swayDesktop.enable` is on. Applies per-user `linger`. |
| `features.nix` | baseModules (all NixOS) | Declares feature-flag options (currently `features.swayDesktop.enable`) so any host can read/set them without importing the heavy implementation module. |
| `workstation.nix` | transitively (via laptop/desktop) | Form-factor-agnostic base for physical graphical hosts: turns on `swayDesktop`, Dvorak console, PipeWire, firewall (default-deny), fstrim, earlyoom, fwupd, thermald (x86), redistributable fw. |
| `laptop.nix` | host table (MBP, T400) | `imports` workstation.nix, then adds the portable bits: iwd Wi-Fi, lid suspend/lock, Bluetooth + blueman. |
| `desktop.nix` | host table (Mac Pro) | `imports` workstation.nix, then swaps Wi-Fi for wired NetworkManager. Pair with `portable = false` in the host table. |
| `sway.nix` | host table (graphical hosts) | Implementation of `features.swayDesktop`: the system Sway package, the greetd/ReGreet (cage) greeter forced to Dvorak, xdg-portal, Wayland utility packages. Home-side Sway config is in `home/sway.nix`. |
| `ssh.nix` | host table (T400, Mac Pro, RPi5) | Enables sshd, opens port 22, enforces a key-only policy (no password / keyboard-interactive, no root). Authorized keys come from the registry via `users.nix`. |
| `firmware/` | referenced by MBP host config | Committed Apple peripheral firmware blobs for the Asahi MBP (see "MacBook (Asahi) firmware"). |
Form-factor decision: a **laptop** imports `laptop.nix` (default
`portable = true`); a **wired desktop** imports `desktop.nix` and sets
`portable = false`; a **headless server** imports neither (leaves
`features.swayDesktop.enable` at its default `false`) and adds only what it
serves. `portable` is threaded through to `home/sway.nix`, which drops the
battery block and brightness keys on desktops.
## Users ## Users
@@ -38,6 +89,16 @@ Identity is data, kept separate from the reusable modules:
identity into that user's home config as the `identity` module arg. A host can identity into that user's home config as the `identity` module arg. A host can
therefore declare any number of users. therefore declare any number of users.
Per-user home extras live under `users/<name>/`:
- [`users/lyrathorpe/home.nix`](./users/lyrathorpe/home.nix) — personal extras
(an ssh host shortcut, gammastep coordinates); imported on Lyra's hosts.
- [`users/emmathorpe/work.nix`](./users/emmathorpe/work.nix) — the work
toolchain (kubectl/helm/az/etc.), work-only LSP servers, and the corporate ssh
handling; imports
[`users/emmathorpe/renovate-review.nix`](./users/emmathorpe/renovate-review.nix),
the daily headless Renovate-PR review timer (EDaaS only).
### Portable home (off-NixOS / external consumers) ### Portable home (off-NixOS / external consumers)
The home config is also exposed for use beyond these hosts: The home config is also exposed for use beyond these hosts:
@@ -61,6 +122,36 @@ sudo nixos-rebuild switch --flake .#<configuration>
darwin-rebuild switch --flake .#lyrathorpe-mac darwin-rebuild switch --flake .#lyrathorpe-mac
``` ```
On a host whose `networking.hostName` matches its flake attribute (the WSL box
and the Pi are set up this way), `nh os switch` resolves the configuration from
the hostname with no `--flake`/`-H` flag.
## Adding a new host
1. **Create `hosts/<Name>/`.** Add `configuration.nix` with the host-specific
bits only: `networking.hostName`, bootloader (firmware-specific — it is
deliberately not set in the shared modules), and any per-machine hardware
quirks. Keep anything reusable in `modules/` instead.
2. **Hardware config.** Generate `hardware-configuration.nix` on the real
machine with `nixos-generate-config` and commit it. If the machine does not
exist yet, commit a clearly-labelled placeholder so the host still evaluates
in CI (see the existing T400 / RPi5 placeholders), and replace it at install.
These files are excluded from the formatter and linters.
3. **Add a host-table entry in `flake.nix`.** Under `hosts` (NixOS) or
`darwinHosts` (macOS), set `system`, the `modules` list (host config + form
factor + any `nixos-hardware` profiles), and the `users` map (each user's
`homeModules`). Choose the form factor per the decision note above; a headless
host imports neither `laptop.nix` nor `desktop.nix`.
4. **Users.** If the host introduces a new person, add them to
`users/registry.nix` first; otherwise reference an existing username.
5. **Verify.** `nix flake check` formats, lints, and evaluates every host —
including the new one — so a broken entry fails locally before CI. Then
`sudo nixos-rebuild switch --flake .#<configuration>` on the machine.
No change to CI is needed: the host-eval step discovers hosts from the flake
(`attrNames` of the configuration sets), so a new entry is picked up
automatically.
## Shell environment & keybindings ## Shell environment & keybindings
- Interactive shell features (zsh, tmux, git, ssh, CLI tools, auto-tmux): - Interactive shell features (zsh, tmux, git, ssh, CLI tools, auto-tmux):
@@ -111,4 +202,7 @@ A dev shell and a formatting/lint gate are wired through the flake:
[`.gitea/workflows/ci.yaml`](./.gitea/workflows/ci.yaml) runs `nix flake check` [`.gitea/workflows/ci.yaml`](./.gitea/workflows/ci.yaml) runs `nix flake check`
(formatting, `deadnix`, `statix`, the pre-commit hooks) and evaluates every (formatting, `deadnix`, `statix`, the pre-commit hooks) and evaluates every
NixOS and Darwin host configuration on push/PR. NixOS and Darwin host configuration on push/PR. It always runs (no `paths:`
filter) so the required check never hangs pending; the heavy Nix steps are
skipped when a PR touches no `.nix`/lockfile/workflow file, and the job still
reports green.
Generated
+38 -21
View File
@@ -25,11 +25,11 @@
}, },
"locked": { "locked": {
"dir": "pkgs/firefox-addons", "dir": "pkgs/firefox-addons",
"lastModified": 1782619356, "lastModified": 1782014564,
"narHash": "sha256-sde3f0uM5mEUwl6Bxom69P+9KOdpJ9YnSj0umSB2qdQ=", "narHash": "sha256-F/royQHyJAyKWKrV8AaG4Yf1yjzxa+PFk5xvTdvBrzk=",
"owner": "rycee", "owner": "rycee",
"repo": "nur-expressions", "repo": "nur-expressions",
"rev": "9ad2019bb522c7eeecd9e3e2d18dd681f697f4d0", "rev": "d6668e34bbce788459883a1097bf0ee170f49c61",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@@ -177,11 +177,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1782704057, "lastModified": 1781981105,
"narHash": "sha256-G1I1gd32F7mp9LAe1DaZ4ZL7NX5gyiKwdCMwro1Vrck=", "narHash": "sha256-/1nNBbA7PrSQpTc9Qazkhl4kIPg+TNl0CjxS3UQJKlw=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "868d0a692de703c2de98fab61968e4e310b7c28e", "rev": "7bfff44b465909f69a442701293bc0badcf476dc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -191,6 +191,22 @@
"type": "github" "type": "github"
} }
}, },
"kube-tmux": {
"flake": false,
"locked": {
"lastModified": 1779714285,
"narHash": "sha256-l1wjg2ReWKCI7h/K11vvX2ykYTs/mVD+tfz/mQsjn/E=",
"owner": "jonmosco",
"repo": "kube-tmux",
"rev": "8b7e1d127c16b6dc87ff5743f4d775b245198b69",
"type": "github"
},
"original": {
"owner": "jonmosco",
"repo": "kube-tmux",
"type": "github"
}
},
"nix-darwin": { "nix-darwin": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -237,11 +253,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1782636943, "lastModified": 1782030356,
"narHash": "sha256-ripjZa7BBLwL1uS5VJF3s/VpZpWt5ZIQEvkJ/FJNpQw=", "narHash": "sha256-h4WpMr455AfRub0FXBaon6Vcpe0waUyJ4GivIW6oyd4=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nix-index-database", "repo": "nix-index-database",
"rev": "058b1f9381fa79fcda49982370a750ff92dbba43", "rev": "3017088b49efd404f78e3b104f553b97e4af786b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -258,11 +274,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1782734462, "lastModified": 1781520503,
"narHash": "sha256-0HguXu/4KDgCL1mehqwhQXD96hbR85HS+o0zh73E8AQ=", "narHash": "sha256-XuqQQG1qRyc3o8ld937sDLQNx+QrGV852KJ0dNglJDg=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixos-apple-silicon", "repo": "nixos-apple-silicon",
"rev": "12e3b92363d21fcc550b500370d73a0747484e43", "rev": "43043ad207529650f9fa68e1705f7cf9c08bfdeb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -278,11 +294,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1782562157, "lastModified": 1781622756,
"narHash": "sha256-a7+T6QSeowynwZ1ZJJbP8T8ntAytvrui8kFGJmIZt2c=", "narHash": "sha256-JrPh4M6S7aPsEE9tOENuZrxC6o2szSLlK+t4+nLke9s=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "a9cf7546a938c737b079e738de73934a13de9784", "rev": "08018c72174a4df5657f8d94178ac69fb9c243e5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -314,11 +330,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1782535326, "lastModified": 1781216227,
"narHash": "sha256-ZeRxu4yn6shd3SNF5ZUQb4r7BaVo1zBKMjRhfoNSBmw=", "narHash": "sha256-9mUW6gNwoN2SWc/l0fW4svPNOulXLl8ijqKyeSOGgJE=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "714a5f8c4ead6b31148d829288440ed033ccc041", "rev": "a0374025a863d007d98e3297f6aa46cc3141c2f0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -330,11 +346,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1782467914, "lastModified": 1781577229,
"narHash": "sha256-pGvFkM8N0xEkIIXDe5YYfbEAvHrk4IxBrjB/x8OomhE=", "narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e73de5be04e0eff4190a1432b946d469c794e7b4", "rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -373,6 +389,7 @@
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"git-hooks": "git-hooks", "git-hooks": "git-hooks",
"home-manager": "home-manager", "home-manager": "home-manager",
"kube-tmux": "kube-tmux",
"nix-darwin": "nix-darwin", "nix-darwin": "nix-darwin",
"nix-homebrew": "nix-homebrew", "nix-homebrew": "nix-homebrew",
"nix-index-database": "nix-index-database", "nix-index-database": "nix-index-database",
+7 -2
View File
@@ -60,6 +60,13 @@
url = "github:NixOS/nixos-hardware"; url = "github:NixOS/nixos-hardware";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# kube-tmux: kube context/namespace for the tmux status line on the work
# host. Not in nixpkgs and not a flake -- pinned here as a plain source so
# the script is always in the store (no manual checkout). See work.nix.
kube-tmux = {
url = "github:jonmosco/kube-tmux";
flake = false;
};
}; };
outputs = outputs =
@@ -93,8 +100,6 @@
# Unfree packages permitted to be built (replaces blanket allowUnfree). # Unfree packages permitted to be built (replaces blanket allowUnfree).
unfreePackages = [ unfreePackages = [
"claude-code" "claude-code"
"lens"
"lens-desktop"
]; ];
# Per-user identity, keyed by username. See README "Users". # Per-user identity, keyed by username. See README "Users".
+17 -17
View File
@@ -110,23 +110,23 @@ declaratively with **nixvim**, so the same plugins and config are baked in on
every host. Migrated from plain vim; the practical gain is a real LSP stack in every host. Migrated from plain vim; the practical gain is a real LSP stack in
place of the old (inert) ALE. place of the old (inert) ALE.
| Feature | Notes | | Feature | Notes |
| -------------- | -------------------------------------------------------------------------------------- | | -------------- | ----------------------------------------------------------------------------------------- |
| Colorscheme | Catppuccin Mocha (matches the terminal and the rest of the desktop) | | Colorscheme | Catppuccin Mocha (matches the terminal and the rest of the desktop) |
| File tree | nvim-tree, toggled with `,,` (comma twice; was nerdtree) | | File tree | nvim-tree, toggled with `,,` (comma twice; was nerdtree) |
| Fuzzy finder | telescope (+fzf-native): `<leader>ff` files, `<leader>fg` grep, `<leader>fb` buffers | | Fuzzy finder | telescope (+fzf-native): `<leader>ff` files, `<leader>fg` grep, `<leader>fb` buffers |
| Format on save | conform-nvim (nixfmt, stylua, ruff, shfmt, prettier, gofumpt; LSP fallback otherwise) | | Format on save | conform-nvim (nixfmt, stylua, ruff, shfmt, prettier, gofumpt; LSP fallback otherwise) |
| Git | fugitive (`:Git …`) + gitsigns gutter signs/blame | | Git | fugitive (`:Git …`) + gitsigns gutter signs/blame |
| Diagnostics | inline + trouble list (`<leader>xx`) | | Diagnostics | inline + trouble list (`<leader>xx`) |
| Completion | nvim-cmp (LSP/buffer/path) with luasnip snippet expansion | | Completion | nvim-cmp (LSP/buffer/path) with luasnip snippet expansion |
| Indent guides | indent-blankline, on by default (was vim-indent-guides) | | Indent guides | indent-blankline, on by default (was vim-indent-guides) |
| Statusline | lualine (Catppuccin theme) | | Statusline | lualine (Catppuccin theme) |
| Editing | which-key hints, comment (`gc`/`gcc`), autopairs, treesitter textobjects | | Editing | which-key hints, comment (`gc`/`gcc`), autopairs, treesitter textobjects |
| Pane nav | vim-tmux-navigator — `Ctrl`+`h/j/k/l` moves across vim splits and tmux panes | | Pane nav | vim-tmux-navigator — `Ctrl`+`h/j/k/l` moves across vim splits and tmux panes |
| Syntax | tree-sitter (nix, lua, bash, markdown, groovy, c#, python, terraform, yaml) | | Syntax | tree-sitter (nix, lua, bash, markdown, groovy, c#, python, terraform, yaml) |
| LSP | nvim-cmp completion + servers `nil` (Nix), `lua_ls`, `pyright` (Python), `terraformls` | | LSP | nvim-cmp completion + servers `nil_ls` (Nix), `lua_ls`, `pyright` (Python), `terraformls` |
| Indentation | 2-wide hard tabs (`noexpandtab`, `tabstop`/`shiftwidth` = 2); line numbers on | | Indentation | 2-wide hard tabs (`noexpandtab`, `tabstop`/`shiftwidth` = 2); line numbers on |
| Filetypes | `*Jenkinsfile` → groovy | | Filetypes | `*Jenkinsfile` → groovy |
Leader is `Space`. LSP keymaps (`gd`, `gr`, `K`, `<leader>rn`, `<leader>ca`) and Leader is `Space`. LSP keymaps (`gd`, `gr`, `K`, `<leader>rn`, `<leader>ca`) and
the file-tree toggle are listed in the file-tree toggle are listed in
+51
View File
@@ -0,0 +1,51 @@
# macOS (nix-darwin) — `lyrathorpe-mac`
Flake host: `lyrathorpe-mac` (`aarch64-darwin`). Apple Silicon Mac managed by
**nix-darwin** from this same flake. Built via `mkDarwinHost` (single-user —
macOS owns the account; identity still comes from the registry). Files:
`configuration.nix`.
## What this host is
A macOS workstation. The interactive user environment (shell, git, editor,
Claude) is the **shared `../../home` bundle** — the same modules the Linux hosts
use — so the terminal experience matches. The Linux-only `desktop.nix`/`sway.nix`
are intentionally left out. This host config covers the macOS-specific layer:
system packages, Homebrew, and macOS UI defaults.
## Package sourcing
- **nixpkgs** (`environment.systemPackages`) for CLI tooling and libraries.
- **Homebrew**, owned declaratively by `nix-homebrew` (Rosetta enabled for
x86_64 formulae). The `brews`/`casks` lists are **authoritative**:
`onActivation.cleanup = "zap"` uninstalls anything not declared. GUI apps are
casks (nixpkgs darwin GUI support is unreliable); a few version-pinned
toolchains and the PWA host stay on brew for continuity.
- **Mac App Store** apps are **not** declarative: nix-darwin 26.05 runs
activation as root, and `mas` cannot reach the App Store session from root.
Install them by hand with `mas install <id>` from a GUI Terminal (the `mas`
CLI is in `environment.systemPackages`).
## macOS integration
- `security.pam.services.sudo_local`**Touch ID for sudo** (and
`darwin-rebuild`'s sudo prompt), kept in `sudo_local` so it survives OS
updates. `reattach` pulls in `pam_reattach` so Touch ID works inside tmux
(which the terminals auto-start).
- `system.defaults` — declarative dock / finder / global / trackpad preferences,
applied on activation and reversible. This is the main reason to run nix-darwin
beyond package management.
- The JetBrainsMono Nerd Font is installed to `/Library/Fonts`; set it in
iTerm2 (Settings → Profiles → Text → Font) so the tmux statusline glyphs
render.
## stateVersion
`system.stateVersion = 5` (the nix-darwin state version, an integer — not a
NixOS release string). Read `darwin-rebuild changelog` before changing it.
## Apply
```sh
darwin-rebuild switch --flake .#lyrathorpe-mac
```
+53
View File
@@ -0,0 +1,53 @@
# Work WSL box — `emmathorpe-edaas`
Flake host: `emmathorpe-edaas` (`x86_64-linux`). NixOS running under
**NixOS-WSL** on the corporate Windows machine. Headless: no Sway desktop
(`features.swayDesktop.enable = false`), plain WSL shell login. Files:
`configuration.nix`.
## What this host is
The day-to-day work environment. It layers the corporate Kubernetes / Helm /
Terraform / cloud toolchain and a couple of work-only editor language servers on
top of the shared home profile. The system config here is thin — it is mostly
WSL plumbing; the user-facing tooling lives in
[`../../users/emmathorpe/work.nix`](../../users/emmathorpe/work.nix).
## WSL specifics
- `wsl.enable`, default user `emmathorpe`, Windows PATH interop and start-menu
launchers on. `/etc/hosts` generation is off (`generateHosts = false`).
- **Docker Desktop integration**, not the native daemon as the primary path:
`wsl.extraBin` shims the coreutils/`groupadd`/`usermod` binaries Docker
Desktop's `wsl-distro-proxy` expects, and `docker-desktop-proxy.script` is
patched to the real proxy path. The native `virtualisation.docker` is also
enabled (with `enableOnBoot` + `autoPrune`).
- `programs.ssh.systemd-ssh-proxy.enable = false` — the NixOS-WSL store is a
read-only VHD owned by `nobody`, and OpenSSH rejects the generated
`ssh-proxy` Include as "Bad owner or permissions", which would break ssh/git
for every command. The vsock proxy it provides is unused under WSL.
- `networking.hostName = "emmathorpe-edaas"` matches the flake attribute so
`nh os switch` resolves without `-H`.
## Renovate review timer
The host-table entry sets `users.emmathorpe.linger = true` so the user's
`systemd --user` instance stays alive without an open login session. That keeps
the daily headless **Renovate PR review** timer firing — defined in
[`../../users/emmathorpe/renovate-review.nix`](../../users/emmathorpe/renovate-review.nix)
(imported only from `work.nix`, so it exists on this machine alone). See that
file's header for the auth (Vertex AI ADC), triage policy, and caveats.
## stateVersion
`system.stateVersion = "24.11"` — the release this box was first installed on.
Leave it; it freezes stateful defaults and is not meant to track the current
nixpkgs.
## Apply
```sh
sudo nixos-rebuild switch --flake .#emmathorpe-edaas
# or, since the hostname matches the attribute:
nh os switch
```
+1 -1
View File
@@ -48,7 +48,7 @@ gigabit ports.
## Login ## Login
Graphical login via a Wayland greeter — `greetd` running ReGreet inside the Graphical login via a Wayland greeter — `greetd` running ReGreet inside the
`cage` kiosk compositor — configured centrally in `lyrathorpe/swaywm.nix` for `cage` kiosk compositor — configured centrally in `../../modules/sway.nix` for
every Sway host (gated on `features.swayDesktop.enable`). The greeter is forced every Sway host (gated on `features.swayDesktop.enable`). The greeter is forced
to the Dvorak layout to match the console and Sway session. Set the user to the Dvorak layout to match the console and Sway session. Set the user
password (`passwd lyrathorpe`) after install, or the greeter cannot password (`passwd lyrathorpe`) after install, or the greeter cannot
+2 -4
View File
@@ -26,10 +26,8 @@
# workstation.nix is the backstop). # workstation.nix is the backstop).
zramSwap.enable = true; zramSwap.enable = true;
# This host accepts SSH, so open 22 (the firewall itself is enabled in # sshd (daemon, port 22, key-only policy) comes from ../../modules/ssh.nix;
# workstation.nix with a default-deny policy). # the firewall itself is enabled in workstation.nix with a default-deny policy.
services.openssh.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ];
# Dual Harpertown Xeon microcode. Redistributable firmware (GPU/NIC blobs) is # Dual Harpertown Xeon microcode. Redistributable firmware (GPU/NIC blobs) is
# enabled in workstation.nix. # enabled in workstation.nix.
+3 -2
View File
@@ -15,7 +15,7 @@ Headless `aarch64-linux` server with two roles:
```sh ```sh
nixos-generate-config --root /mnt nixos-generate-config --root /mnt
# copy /mnt/etc/nixos/hardware-configuration.nix over # copy /mnt/etc/nixos/hardware-configuration.nix over
# system/machine/RPi5/hardware-configuration.nix in this repo, then commit # hosts/RPi5/hardware-configuration.nix in this repo, then commit
``` ```
`hardware-configuration.nix` in this directory is a **placeholder** committed `hardware-configuration.nix` in this directory is a **placeholder** committed
only so the host evaluates in CI. The machine will not boot correctly until it only so the host evaluates in CI. The machine will not boot correctly until it
@@ -28,7 +28,8 @@ Headless `aarch64-linux` server with two roles:
nh os switch nh os switch
``` ```
4. Give the login user a password (`passwd lyrathorpe`) and confirm the key in 4. Give the login user a password (`passwd lyrathorpe`) and confirm the key in
`system/modules/ssh.nix` is the one you will connect with. the user registry (`../../users/registry.nix`, applied by
`../../modules/ssh.nix`) is the one you will connect with.
## Docker socket (security) ## Docker socket (security)
+4 -7
View File
@@ -25,15 +25,12 @@
boot.loader.grub.enable = false; boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = true; boot.loader.generic-extlinux-compatible.enable = true;
# Remote administration. Key-only policy and the authorized key come from # Remote administration: the daemon, port 22 and key-only policy all come from
# ../../modules/ssh.nix; here we just enable the daemon and open the port. # ../../modules/ssh.nix.
services.openssh.enable = true;
# Default-deny inbound. Open only SSH here; the Docker and nginx submodules # Default-deny inbound; the Docker and nginx submodules open their own ports
# open their own ports (Docker via a source-restricted nftables rule, nginx # (Docker via a source-restricted nftables rule, nginx via 80/443).
# via 80/443). List-valued, so these merge with the submodule definitions.
networking.firewall.enable = true; networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ];
# See `man configuration.nix` / the stateVersion docs before changing. # See `man configuration.nix` / the stateVersion docs before changing.
system.stateVersion = "26.05"; system.stateVersion = "26.05";
+5 -1
View File
@@ -8,6 +8,10 @@
# secure upgrade path is mutual TLS on 2376 (--tlsverify with client certs); # secure upgrade path is mutual TLS on 2376 (--tlsverify with client certs);
# that needs out-of-band cert provisioning and is intentionally not wired here. # that needs out-of-band cert provisioning and is intentionally not wired here.
{ ... }: { ... }:
let
# LAN allowed to reach the unauthenticated Docker TCP socket (see SECURITY above).
trustedSubnet = "10.187.1.0/24";
in
{ {
virtualisation.docker.enable = true; virtualisation.docker.enable = true;
@@ -29,6 +33,6 @@
# CIDR to match the LAN that should reach the Docker API. # CIDR to match the LAN that should reach the Docker API.
networking.nftables.enable = true; networking.nftables.enable = true;
networking.firewall.extraInputRules = '' networking.firewall.extraInputRules = ''
ip saddr 10.187.1.0/24 tcp dport 2375 accept ip saddr ${trustedSubnet} tcp dport 2375 accept
''; '';
} }
+1 -1
View File
@@ -35,7 +35,7 @@ change and `radeon` stays idle.
## Login ## Login
Graphical login via a Wayland greeter — `greetd` running ReGreet inside the Graphical login via a Wayland greeter — `greetd` running ReGreet inside the
`cage` kiosk compositor — configured centrally in `lyrathorpe/swaywm.nix` for `cage` kiosk compositor — configured centrally in `../../modules/sway.nix` for
every Sway host (gated on `features.swayDesktop.enable`). The greeter is forced every Sway host (gated on `features.swayDesktop.enable`). The greeter is forced
to the Dvorak layout to match the console and Sway session. Set the user to the Dvorak layout to match the console and Sway session. Set the user
password (`passwd lyrathorpe`) after install, or the greeter cannot password (`passwd lyrathorpe`) after install, or the greeter cannot
+2 -4
View File
@@ -21,10 +21,8 @@
# Low-RAM host (4 GiB max): a compressed RAM swap reduces disk paging. # Low-RAM host (4 GiB max): a compressed RAM swap reduces disk paging.
zramSwap.enable = true; zramSwap.enable = true;
# This host accepts SSH, so open 22 (the firewall itself is enabled in # sshd (daemon, port 22, key-only policy) comes from ../../modules/ssh.nix;
# laptop.nix with a default-deny policy). # the firewall itself is enabled in laptop.nix with a default-deny policy.
services.openssh.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ];
# Intel Core 2 (Penryn) microcode. Redistributable firmware (enabled in # Intel Core 2 (Penryn) microcode. Redistributable firmware (enabled in
# workstation.nix) supplies the iwlwifi blobs (Intel WiFi Link 5100/5300) and # workstation.nix) supplies the iwlwifi blobs (Intel WiFi Link 5100/5300) and
+6 -3
View File
@@ -1,8 +1,11 @@
# Key-only sshd hardening, imported by hosts that run sshd (T400, Mac Pro, # sshd for the hosts that run it (T400, Mac Pro, RPi5): enable the daemon, open
# RPi5). Authorized keys are owned per-user by the registry (modules/users.nix), # port 22, and apply a key-only policy. Authorized keys are owned per-user by the
# not here. # registry (modules/users.nix), not here.
{ ... }: { ... }:
{ {
services.openssh.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ];
services.openssh.settings = { services.openssh.settings = {
PasswordAuthentication = false; # keys only PasswordAuthentication = false; # keys only
KbdInteractiveAuthentication = false; # no keyboard-interactive fallback KbdInteractiveAuthentication = false; # no keyboard-interactive fallback
+9 -3
View File
@@ -1,6 +1,11 @@
# Work (EDaaS/WSL) home profile: corporate toolchain + tmux tweaks. Git identity # Work (EDaaS/WSL) home profile: corporate toolchain + tmux tweaks. Git identity
# comes from the registry (users/registry.nix), not here. # comes from the registry (users/registry.nix), not here.
{ pkgs, lib, ... }: {
pkgs,
lib,
inputs,
...
}:
{ {
# Host-scoped extras for this machine only (the EDaaS/WSL host). # Host-scoped extras for this machine only (the EDaaS/WSL host).
@@ -24,7 +29,6 @@
pkgs.powershell pkgs.powershell
pkgs.nuget pkgs.nuget
pkgs.gedit pkgs.gedit
pkgs.lens
pkgs.python3 pkgs.python3
pkgs.gnumake pkgs.gnumake
pkgs.gcc pkgs.gcc
@@ -48,8 +52,10 @@
docker = "/run/current-system/sw/bin/docker"; docker = "/run/current-system/sw/bin/docker";
}; };
programs.tmux = { programs.tmux = {
# kube context/namespace in the status line. kube-tmux is pinned as a flake
# input (it is not in nixpkgs), so the script is always present in the store.
extraConfig = '' extraConfig = ''
set -g status-right "#(/run/current-system/sw/bin/bash $HOME/code/kube-tmux/kube.tmux 250 red black)" set -g status-right "#(${pkgs.bash}/bin/bash ${inputs.kube-tmux}/kube.tmux 250 red black)"
''; '';
}; };
programs.go = { programs.go = {