Author SHA1 Message Date
Emma ThorpeandClaude Opus 4.8 781c45a47c docs(editor): document nvim-cmp completion menu keybindings
CI / flake (pull_request) Successful in 4m3s
Add a completion-menu table to KEYBINDINGS.md covering the new cmp
mappings (Tab/S-Tab and C-n/C-p to move, C-Space to open, Enter to
confirm, C-e to dismiss), and reword the Neovim summary accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:28:39 +01:00
Emma ThorpeandClaude Opus 4.8 304b9a413a feat(editor): add nvim-cmp completion keymaps
CI / flake (pull_request) Successful in 4m13s
nvim-cmp ships no default mappings, so the completion menu (including the
path source) appeared but nothing could navigate or accept it. Bind the
usual set: C-n/C-p and Tab/S-Tab to move, C-Space to open, C-e to abort,
and <CR> to confirm with select=false so a bare Enter stays a newline
unless an entry is explicitly highlighted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:17:05 +01:00
65 changed files with 266 additions and 604 deletions
+6
View File
@@ -8,6 +8,12 @@ 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
+1 -4
View File
@@ -1,7 +1,4 @@
modules/firmware/* system/modules/firmware/*
# vim swap files # vim swap files
*.swp *.swp
# Local scratch project, not part of this flake.
tf-inspect/
+20 -143
View File
@@ -8,110 +8,20 @@ single flake.
Defined in the host table in [`flake.nix`](./flake.nix): Defined in the host table in [`flake.nix`](./flake.nix):
| Configuration | System | Machine | | Configuration | System | Machine |
| --------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------- | | --------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------- |
| `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](./system/machine/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](./system/machine/MacPro31/README.md) |
| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) — [notes](./hosts/EDaaS/README.md) | | `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) |
| `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](./system/machine/RPi5/README.md) |
| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) — [notes](./hosts/Darwin/README.md) | | `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) |
Shared layers: `home` (home-manager: shell, git, editor), Shared layers: `lyrathorpe/home` (home-manager: shell, git, editor),
`modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches), `system/modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches),
`modules/workstation.nix` (physical graphical hosts: audio, thermald, `system/modules/workstation.nix` (physical graphical hosts: audio, thermald,
earlyoom, fwupd), `modules/laptop.nix` (laptops: Wi-Fi, Bluetooth, power, earlyoom, fwupd), `system/modules/laptop.nix` (laptops: Wi-Fi, Bluetooth, power,
lid), `modules/desktop.nix` (wired desktops: NetworkManager), and lid), and `system/modules/ssh.nix` (key-only sshd). The x86 hosts also pull
`modules/ssh.nix` (key-only sshd). The x86 hosts also pull `nixos-hardware` `nixos-hardware` profiles.
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
Identity is data, kept separate from the reusable modules:
- [`users/registry.nix`](./users/registry.nix) — one entry per user (display
name, email, supplementary groups, authorized + signing keys). This is the
single source of identity; no user data is hardcoded in the modules.
- Each host's table entry declares a `users` set keyed by username; every entry
lists that user's home-module composition (the shared `./home` bundle plus any
per-user modules, e.g. [`users/emmathorpe/work.nix`](./users/emmathorpe/work.nix))
and optional per-host-user system bits such as `linger`.
- `mkHost` builds each account from the registry and injects the matching
identity into that user's home config as the `identity` module arg. A host can
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)
The home config is also exposed for use beyond these hosts:
- `homeConfigurations."<user>@<system>"` — a standalone home-manager profile
(the portable subset: shell + git + editor + claude) that can be activated on a
machine this flake does **not** manage:
`home-manager switch --flake .#"lyrathorpe@x86_64-linux"`. The desktop/sway
modules are intentionally excluded (they rely on a NixOS-provided Sway/Firefox
binary).
- `homeModules` — the reusable modules exported so another flake can import them
(`inputs.<this>.homeModules.default`). Consumers must supply the module args
these expect: `inputs` always, `identity` for git/desktop, `portable` for sway.
## Applying ## Applying
@@ -122,58 +32,28 @@ 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):
[`home/README.md`](./home/README.md). [`lyrathorpe/home/README.md`](./lyrathorpe/home/README.md).
- All Sway / tmux / foot / zsh keyboard shortcuts: - All Sway / tmux / foot / zsh keyboard shortcuts:
[`home/KEYBINDINGS.md`](./home/KEYBINDINGS.md). [`lyrathorpe/home/KEYBINDINGS.md`](./lyrathorpe/home/KEYBINDINGS.md).
## Login / greeter ## Login / greeter
Graphical (Sway) hosts log in through a Wayland greeter — `greetd` running Graphical (Sway) hosts log in through a Wayland greeter — `greetd` running
ReGreet inside the `cage` kiosk compositor — implemented in ReGreet inside the `cage` kiosk compositor — implemented in
[`modules/sway.nix`](./modules/sway.nix), gated on [`lyrathorpe/swaywm.nix`](./lyrathorpe/swaywm.nix), gated on
`features.swayDesktop.enable` (the option is declared in `features.swayDesktop.enable` (the option is declared in
[`modules/features.nix`](./modules/features.nix), so headless hosts [`system/modules/features.nix`](./system/modules/features.nix), so headless hosts
can leave it off without importing `modules/sway.nix`). The greeter is forced to Dvorak can leave it off without importing `swaywm.nix`). The greeter is forced to Dvorak
to match the console and Sway session. Headless hosts (the WSL work box and the to match the console and Sway session. Headless hosts (the WSL work box and the
Raspberry Pi server) keep plain TTY login. The target account needs a password Raspberry Pi server) keep plain TTY login. The target account needs a password
(`passwd <user>`) before it can log in. (`passwd <user>`) before it can log in.
## MacBook (Asahi) firmware ## MacBook (Asahi) firmware
The MBP host references `modules/firmware/` for Apple peripheral The MBP host references `system/modules/firmware/` for Apple peripheral
firmware (Wi-Fi/Bluetooth). These blobs are **committed** (tracked) even though firmware (Wi-Fi/Bluetooth). These blobs are **committed** (tracked) even though
`.gitignore` lists the directory: the flake is `git+file`, so it only sees `.gitignore` lists the directory: the flake is `git+file`, so it only sees
tracked files — untracking them breaks `lyrathorpe-mbp` evaluation (and the CI tracked files — untracking them breaks `lyrathorpe-mbp` evaluation (and the CI
@@ -183,7 +63,7 @@ redistributable; the repo is private.
To refresh them, copy the firmware extracted during the Asahi install (from To refresh them, copy the firmware extracted during the Asahi install (from
`/etc/nixos/firmware`, or re-extract per the `/etc/nixos/firmware`, or re-extract per the
[Asahi NixOS docs](https://github.com/tpwrules/nixos-apple-silicon)) into [Asahi NixOS docs](https://github.com/tpwrules/nixos-apple-silicon)) into
`modules/firmware/` and commit with `git add -f`. `system/modules/firmware/` and commit with `git add -f`.
## Development ## Development
@@ -202,7 +82,4 @@ 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. It always runs (no `paths:` NixOS and Darwin host configuration on push/PR.
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
-17
View File
@@ -191,22 +191,6 @@
"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": [
@@ -389,7 +373,6 @@
"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",
+77 -134
View File
@@ -23,7 +23,7 @@
# Provides mkFlake: the systems/perSystem scaffolding used below. # Provides mkFlake: the systems/perSystem scaffolding used below.
flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.url = "github:hercules-ci/flake-parts";
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
# Declarative Firefox add-ons (e.g. the Catppuccin theme); see modules/users.nix. # Declarative Firefox add-ons (e.g. the Catppuccin theme); see lyrathorpe/user.nix.
firefox-addons = { firefox-addons = {
url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons"; url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -46,7 +46,7 @@
url = "github:cachix/git-hooks.nix"; url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# Declarative Neovim (the editor; see home/editor.nix). Release # Declarative Neovim (the editor; see lyrathorpe/home/editor.nix). Release
# branch matched to the pinned nixpkgs (26.05); follows our nixpkgs to keep a # branch matched to the pinned nixpkgs (26.05); follows our nixpkgs to keep a
# single nixpkgs in the closure. editor.nix sets programs.nixvim.nixpkgs.source # single nixpkgs in the closure. editor.nix sets programs.nixvim.nixpkgs.source
# to this same input so the home module doesn't warn about the pin. # to this same input so the home module doesn't warn about the pin.
@@ -60,13 +60,6 @@
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 =
@@ -100,11 +93,10 @@
# 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".
userRegistry = import ./users/registry.nix;
# nixpkgs + nix-daemon settings shared by NixOS and Darwin hosts. # nixpkgs + nix-daemon settings shared by NixOS and Darwin hosts.
commonModule = { commonModule = {
nixpkgs.overlays = overlays; nixpkgs.overlays = overlays;
@@ -120,9 +112,9 @@
# Shared scaffolding for every NixOS host: common user, settings, home-manager. # Shared scaffolding for every NixOS host: common user, settings, home-manager.
baseModules = [ baseModules = [
./modules/users.nix ./lyrathorpe/user.nix
./modules/common-nixos.nix ./system/modules/common-nixos.nix
./modules/features.nix ./system/modules/features.nix
commonModule commonModule
home-manager.nixosModules.home-manager home-manager.nixosModules.home-manager
{ {
@@ -134,13 +126,18 @@
} }
]; ];
# Build one NixOS host. `users` is an attrset keyed by username (home # mkHost :: { system, username, fullName, modules, homeModules } -> nixosSystem
# modules + optional per-user system bits). See README "Users". # Builds one machine by appending its host-specific modules to the shared
# baseModules. The user identity (username/fullName) is threaded through
# specialArgs so user.nix and the home modules stay host-agnostic, and the
# home-manager profile is keyed by the host's username.
mkHost = mkHost =
{ {
system, system,
username,
fullName,
modules, modules,
users, homeModules,
# Host form factor. Laptops inherit the default; a desktop host sets # Host form factor. Laptops inherit the default; a desktop host sets
# `portable = false` to drop mobile components (battery block, # `portable = false` to drop mobile components (battery block,
# brightness keys) from the home-manager Sway config. # brightness keys) from the home-manager Sway config.
@@ -151,7 +148,8 @@
specialArgs = { specialArgs = {
inherit inherit
inputs inputs
userRegistry username
fullName
portable portable
; ;
}; };
@@ -159,15 +157,16 @@
baseModules baseModules
++ modules ++ modules
++ [ ++ [
{ _module.args.hostUsers = users; }
{ {
home-manager.extraSpecialArgs = { inherit inputs portable; }; home-manager.extraSpecialArgs = {
home-manager.users = lib.mapAttrs (name: spec: { inherit
imports = spec.homeModules; inputs
_module.args.identity = userRegistry.${name} // { username
username = name; fullName
portable
;
}; };
}) users; home-manager.users.${username}.imports = homeModules;
} }
]; ];
}; };
@@ -186,17 +185,19 @@
} }
]; ];
# Darwin counterpart of mkHost: single-user (macOS owns the account), # mkDarwinHost :: { system, username, fullName, modules, homeModules } -> darwinSystem
# identity still from the registry. See README "Users". # Darwin counterpart of mkHost. macOS already owns the login user, so we
# only attach the platform and home-manager; no NixOS user module here.
mkDarwinHost = mkDarwinHost =
{ {
system, system,
username, username,
fullName,
modules, modules,
homeModules, homeModules,
}: }:
nix-darwin.lib.darwinSystem { nix-darwin.lib.darwinSystem {
specialArgs = { inherit inputs username; }; specialArgs = { inherit inputs username fullName; };
modules = modules =
darwinBaseModules darwinBaseModules
++ modules ++ modules
@@ -205,41 +206,40 @@
nixpkgs.hostPlatform = system; nixpkgs.hostPlatform = system;
# macOS owns the account; point home-manager at its home dir. # macOS owns the account; point home-manager at its home dir.
users.users.${username}.home = "/Users/${username}"; users.users.${username}.home = "/Users/${username}";
home-manager.extraSpecialArgs = { inherit inputs; }; home-manager.extraSpecialArgs = { inherit inputs username fullName; };
home-manager.users.${username} = { home-manager.users.${username}.imports = homeModules;
imports = homeModules;
_module.args.identity = userRegistry.${username} // {
inherit username;
};
};
} }
]; ];
}; };
# Host table — one entry per machine, realised into a nixosConfiguration # Host table — declarative registry of every machine. To add a host:
# of the same name below. See README "Hosts" / "Users". # give it a name, its `system`, the owning user, and the module lists.
# mapAttrs below turns each entry into a nixosConfiguration of the same name.
hosts = { hosts = {
lyrathorpe-mbp = { lyrathorpe-mbp = {
system = "aarch64-linux"; system = "aarch64-linux";
username = "lyrathorpe";
fullName = "Lyra Thorpe";
modules = [ modules = [
./hosts/MBP-Asahi/configuration.nix ./system/machine/MBP-Asahi/configuration.nix
./modules/laptop.nix ./system/modules/laptop.nix
nixos-apple-silicon.nixosModules.default nixos-apple-silicon.nixosModules.default
./modules/sway.nix ./lyrathorpe/swaywm.nix
]; ];
users.lyrathorpe.homeModules = [ homeModules = [
./home ./lyrathorpe/home
./users/lyrathorpe/home.nix ./lyrathorpe/home/desktop.nix
./home/desktop.nix
]; ];
}; };
lyrathorpe-t400 = { lyrathorpe-t400 = {
system = "x86_64-linux"; system = "x86_64-linux";
username = "lyrathorpe";
fullName = "Lyra Thorpe";
modules = [ modules = [
./hosts/T400/configuration.nix ./system/machine/T400/configuration.nix
./modules/laptop.nix ./system/modules/laptop.nix
./modules/ssh.nix ./system/modules/ssh.nix
# No t400-specific profile exists; compose the generic ThinkPad + # No t400-specific profile exists; compose the generic ThinkPad +
# laptop/SSD/Intel building blocks (tp_smapi/acpi_call for battery # laptop/SSD/Intel building blocks (tp_smapi/acpi_call for battery
# thresholds, SSD + microcode defaults). # thresholds, SSD + microcode defaults).
@@ -247,82 +247,78 @@
inputs.nixos-hardware.nixosModules.common-pc-laptop inputs.nixos-hardware.nixosModules.common-pc-laptop
inputs.nixos-hardware.nixosModules.common-pc-laptop-ssd inputs.nixos-hardware.nixosModules.common-pc-laptop-ssd
inputs.nixos-hardware.nixosModules.common-cpu-intel inputs.nixos-hardware.nixosModules.common-cpu-intel
./modules/sway.nix ./lyrathorpe/swaywm.nix
]; ];
users.lyrathorpe.homeModules = [ homeModules = [
./home ./lyrathorpe/home
./users/lyrathorpe/home.nix ./lyrathorpe/home/desktop.nix
./home/desktop.nix
]; ];
}; };
lyrathorpe-macpro31 = { lyrathorpe-macpro31 = {
system = "x86_64-linux"; system = "x86_64-linux";
username = "lyrathorpe";
fullName = "Lyra Thorpe";
portable = false; portable = false;
modules = [ modules = [
./hosts/MacPro31/configuration.nix ./system/machine/MacPro31/configuration.nix
./modules/desktop.nix ./system/modules/desktop.nix
./modules/ssh.nix ./system/modules/ssh.nix
inputs.nixos-hardware.nixosModules.common-pc-ssd inputs.nixos-hardware.nixosModules.common-pc-ssd
inputs.nixos-hardware.nixosModules.common-cpu-intel inputs.nixos-hardware.nixosModules.common-cpu-intel
./modules/sway.nix ./lyrathorpe/swaywm.nix
]; ];
users.lyrathorpe.homeModules = [ homeModules = [
./home ./lyrathorpe/home
./users/lyrathorpe/home.nix ./lyrathorpe/home/desktop.nix
./home/desktop.nix
]; ];
}; };
emmathorpe-edaas = { emmathorpe-edaas = {
system = "x86_64-linux"; system = "x86_64-linux";
username = "emmathorpe";
fullName = "Emma Thorpe";
modules = [ modules = [
./hosts/EDaaS/configuration.nix ./system/machine/EDaaS/configuration.nix
nixos-wsl.nixosModules.default nixos-wsl.nixosModules.default
./modules/sway.nix ./lyrathorpe/swaywm.nix
]; ];
users.emmathorpe = {
homeModules = [ homeModules = [
./home ./lyrathorpe/home
./users/emmathorpe/work.nix ./lyrathorpe/home/work.nix
]; ];
# Keep the systemd --user instance alive without a login session so
# the renovate-review home timer fires on schedule.
linger = true;
};
}; };
lyrathorpe-rpi5 = { lyrathorpe-rpi5 = {
system = "aarch64-linux"; system = "aarch64-linux";
username = "lyrathorpe";
fullName = "Lyra Thorpe";
portable = false; portable = false;
# Headless server: Docker host + nginx reverse proxy. No sway.nix # Headless server: Docker host + nginx reverse proxy. No swaywm.nix
# (no desktop); the raspberry-pi-5 profile supplies kernel/firmware, # (no desktop); the raspberry-pi-5 profile supplies kernel/firmware,
# ssh.nix adds key-only sshd. # ssh.nix adds key-only sshd.
modules = [ modules = [
./hosts/RPi5/configuration.nix ./system/machine/RPi5/configuration.nix
inputs.nixos-hardware.nixosModules.raspberry-pi-5 inputs.nixos-hardware.nixosModules.raspberry-pi-5
./modules/ssh.nix ./system/modules/ssh.nix
];
users.lyrathorpe.homeModules = [
./home
./users/lyrathorpe/home.nix
]; ];
homeModules = [ ./lyrathorpe/home ];
}; };
}; };
# Darwin host table — macOS machines built via mkDarwinHost. The shared # Darwin host table — macOS machines built via mkDarwinHost. The shared
# ./home bundle (shell, git, editor) is reused directly; the Linux-only # ./lyrathorpe/home modules (shell, git, editor) are reused; the Linux-only
# desktop/sway modules are intentionally left out. # desktop/sway modules are intentionally left out.
darwinHosts = { darwinHosts = {
lyrathorpe-mac = { lyrathorpe-mac = {
system = "aarch64-darwin"; system = "aarch64-darwin";
username = "lyrathorpe"; username = "lyrathorpe";
fullName = "Lyra Thorpe";
modules = [ modules = [
./hosts/Darwin/configuration.nix ./system/machine/Darwin/configuration.nix
]; ];
homeModules = [ homeModules = [
./home ./lyrathorpe/home
./users/lyrathorpe/home.nix
]; ];
}; };
}; };
@@ -413,59 +409,6 @@
# Realise the host tables: each entry becomes a {nixos,darwin}Configuration. # Realise the host tables: each entry becomes a {nixos,darwin}Configuration.
flake.nixosConfigurations = lib.mapAttrs (_name: mkHost) hosts; flake.nixosConfigurations = lib.mapAttrs (_name: mkHost) hosts;
flake.darwinConfigurations = lib.mapAttrs (_name: mkDarwinHost) darwinHosts; flake.darwinConfigurations = lib.mapAttrs (_name: mkDarwinHost) darwinHosts;
# Reusable home modules, exported for use off these hosts. See README
# "Portable home" for the consumer module-arg expectations.
flake.homeModules = {
default = ./home;
shell = ./home/shell.nix;
git = ./home/git.nix;
editor = ./home/editor.nix;
claude = ./home/claude.nix;
desktop = ./home/desktop.nix;
sway = ./home/sway.nix;
};
# Standalone home-manager configs (portable bundle) for machines not
# managed by this flake. See README "Portable home".
flake.homeConfigurations =
let
mkHome =
{
system,
name,
}:
home-manager.lib.homeManagerConfiguration {
pkgs = import nixpkgs {
inherit system overlays;
config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) unfreePackages;
};
extraSpecialArgs = {
inherit inputs;
portable = true;
identity = userRegistry.${name} // {
username = name;
};
};
modules = [
./home
{
home.username = name;
home.homeDirectory = "/home/${name}";
}
];
};
in
{
"lyrathorpe@x86_64-linux" = mkHome {
system = "x86_64-linux";
name = "lyrathorpe";
};
"lyrathorpe@aarch64-linux" = mkHome {
system = "aarch64-linux";
name = "lyrathorpe";
};
};
} }
); );
} }
-51
View File
@@ -1,51 +0,0 @@
# 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
@@ -1,53 +0,0 @@
# 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,6 +1,6 @@
# Catppuccin Mocha palette. Raw 6-digit hex (no leading "#"); consumers add a # Catppuccin Mocha palette. Raw 6-digit hex (no leading "#"); consumers add a
# "#" where their format needs it. Shared by the Sway desktop theming # "#" where their format needs it. Shared by the Sway desktop theming
# (home/sway.nix) and the ReGreet greeter (modules/sway.nix) so the two stay in sync. # (home/sway.nix) and the ReGreet greeter (swaywm.nix) so the two stay in sync.
{ {
base = "1e1e2e"; base = "1e1e2e";
mantle = "181825"; mantle = "181825";
+7 -9
View File
@@ -15,10 +15,8 @@ Keyboard shortcuts have their own reference: [`KEYBINDINGS.md`](./KEYBINDINGS.md
| GUI apps, GTK/Firefox theming, cursor | [`desktop.nix`](./desktop.nix) (graphical hosts only) | | GUI apps, GTK/Firefox theming, cursor | [`desktop.nix`](./desktop.nix) (graphical hosts only) |
Shared by every host via [`default.nix`](./default.nix); the work box also layers Shared by every host via [`default.nix`](./default.nix); the work box also layers
[`work.nix`](../users/emmathorpe/work.nix) on top (its own ssh config, extra [`work.nix`](./work.nix) on top (work email, its own ssh config, extra packages,
packages, and the C#/Helm language servers). The committer identity (name, email, and the C#/Helm language servers).
signing key) comes from the user registry
([`../users/registry.nix`](../users/registry.nix)), not this module.
--- ---
@@ -57,7 +55,7 @@ signing key) comes from the user registry
| `hyperfine` / `sd` | command-line benchmarking; saner find-and-replace than sed | | `hyperfine` / `sd` | command-line benchmarking; saner find-and-replace than sed |
**Theming:** `fzf`, `bat`, `btop`, `lazygit` and `git`'s `delta` pager are all **Theming:** `fzf`, `bat`, `btop`, `lazygit` and `git`'s `delta` pager are all
Catppuccin Mocha, driven from the shared `../lib/catppuccin-mocha.nix` palette / the Catppuccin Mocha, driven from the shared `../catppuccin-mocha.nix` palette / the
catppuccin upstream themes. catppuccin upstream themes.
**Env & defaults:** `xdg.enable` on; `PAGER`/`MANPAGER` (bat) set in `default.nix` **Env & defaults:** `xdg.enable` on; `PAGER`/`MANPAGER` (bat) set in `default.nix`
@@ -111,7 +109,7 @@ 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 |
@@ -124,7 +122,7 @@ place of the old (inert) ALE.
| 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_ls` (Nix), `lua_ls`, `pyright` (Python), `terraformls` | | LSP | nvim-cmp completion + servers `nil` (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 |
@@ -150,7 +148,7 @@ current (`gc`/`fetch.writeCommitGraph`) so `lg` stays fast.
| `cz` `cc` | `git cz <sub>` (e.g. `git cz c`) and `git cc` → commitizen prompt | | `cz` `cc` | `git cz <sub>` (e.g. `git cz c`) and `git cc` → commitizen prompt |
| Behaviour | | | Behaviour | |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Pulls | rebase, with autostash + autosquash | | Pulls | rebase, with autostash + autosquash |
| Fetch | prune deleted remote branches | | Fetch | prune deleted remote branches |
| Conflicts | `zdiff3` (shows the common ancestor) | | Conflicts | `zdiff3` (shows the common ancestor) |
@@ -159,7 +157,7 @@ current (`gc`/`fetch.writeCommitGraph`) so `lg` stays fast.
| Commit editor | full diff shown (`commit.verbose`) | | Commit editor | full diff shown (`commit.verbose`) |
| Misc | branches sorted by date, `column.ui = auto`, `help.autocorrect = prompt`, `push.autoSetupRemote` | | Misc | branches sorted by date, `column.ui = auto`, `help.autocorrect = prompt`, `push.autoSetupRemote` |
| Global ignores | `result`, `result-*`, `.direnv`, `*.swp`, `.DS_Store` | | Global ignores | `result`, `result-*`, `.direnv`, `*.swp`, `.DS_Store` |
| Signing | SSH commit + tag signing (`mkDefault`, so a host without the key in its agent can disable it). Name, email and signing key all come from the per-user `identity` (the user registry, `../users/registry.nix`). | | Signing | SSH commit + tag signing (`mkDefault`, so a host without the key in its agent can disable it). Personal email `iam@emmathe.dev`; the work box overrides email + signing. |
## ssh ## ssh
@@ -1,13 +1,12 @@
# Graphical desktop layer: GUI apps, Wayland session env, and cursor theme. # Graphical desktop layer: GUI apps, Wayland session env, and cursor theme.
# Imported only on hosts that run Sway (MBP, T400, Mac Pro); never pulled onto # Imported only on hosts that run Sway (MBP, T400, Mac Pro); never pulled onto
# the headless WSL host. Login (and the Sway session launch) is handled by the # the headless WSL host. Login (and the Sway session launch) is handled by the
# greetd/ReGreet greeter -- see ../modules/sway.nix -- so there is no tty1 # greetd/ReGreet greeter -- see ../swaywm.nix -- so there is no tty1 autostart.
# autostart.
{ {
pkgs, pkgs,
config, config,
inputs, inputs,
identity, username,
... ...
}: }:
{ {
@@ -90,7 +89,7 @@
}; };
# Firefox is themed at the browser level (it does not follow the GTK theme). # Firefox is themed at the browser level (it does not follow the GTK theme).
# The system installs the binary (programs.firefox in ../modules/users.nix); here # The system installs the binary (programs.firefox in ../user.nix); here
# home-manager owns only the profile, hence package = null. Apply the # home-manager owns only the profile, hence package = null. Apply the
# Catppuccin Mocha theme add-on (only the mauve accent is packaged upstream; # Catppuccin Mocha theme add-on (only the mauve accent is packaged upstream;
# the rest of the desktop uses blue) and make content + UI dark. # the rest of the desktop uses blue) and make content + UI dark.
@@ -102,7 +101,7 @@
# stateVersion<26.05 default-change warning (the new XDG path depends on # stateVersion<26.05 default-change warning (the new XDG path depends on
# Firefox's own profile support). # Firefox's own profile support).
configPath = ".mozilla/firefox"; configPath = ".mozilla/firefox";
profiles.${identity.username} = { profiles.${username} = {
id = 0; id = 0;
isDefault = true; isDefault = true;
extensions = { extensions = {
+15 -10
View File
@@ -1,13 +1,13 @@
# Version control: git + delta + commitizen + lazygit. Committer identity comes # Version control: git + delta pager + commitizen + lazygit. The work host
# from the per-user `identity` arg (the registry). See README "Users". # layers commit signing and an email override on top (see work.nix).
{ {
pkgs, pkgs,
lib, lib,
identity, fullName,
... ...
}: }:
let let
ctp = import ../lib/catppuccin-mocha.nix; ctp = import ../catppuccin-mocha.nix;
in in
{ {
home.packages = [ home.packages = [
@@ -18,9 +18,10 @@ in
enable = true; enable = true;
package = pkgs.gitFull; package = pkgs.gitFull;
settings = { settings = {
user.name = identity.fullName; user.name = fullName;
# mkDefault so a host-specific module can still override it. # Personal identity. mkDefault so the work module overrides it on the work
user.email = lib.mkDefault identity.email; # host (and to merge cleanly with that plain definition there).
user.email = lib.mkDefault "iam@emmathe.dev";
push.autoSetupRemote = true; push.autoSetupRemote = true;
init.defaultBranch = "main"; init.defaultBranch = "main";
@@ -76,10 +77,14 @@ in
cc = "!cz commit"; cc = "!cz commit";
}; };
# SSH signing, key from the registry. mkDefault so a host lacking the key # SSH commit signing. This personal key is the default; the work module
# in its agent can set gpgsign = false instead of failing every commit. # (work.nix) overrides it with the work key on the EDaaS host, the same way
# user.email is overridden -- so mkDefault here lets that plain definition
# win instead of conflicting. gpgsign is mkDefault too, so a host without
# the key in its ssh-agent can override it to false rather than fail every
# commit.
gpg.format = "ssh"; gpg.format = "ssh";
user.signingkey = lib.mkDefault identity.signingKey; user.signingkey = lib.mkDefault "key::ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDxHvdMTOzpFWUFMtCP7C/4tIOUO3GIO2QPvaifSnWH lyrathorpe@Lyra-MBA";
commit.gpgsign = lib.mkDefault true; commit.gpgsign = lib.mkDefault true;
tag.gpgsign = lib.mkDefault true; tag.gpgsign = lib.mkDefault true;
}; };
+1 -1
View File
@@ -8,7 +8,7 @@
}: }:
let let
# Shared Catppuccin Mocha palette: raw 6-hex strings, no leading "#". # Shared Catppuccin Mocha palette: raw 6-hex strings, no leading "#".
ctp = import ../lib/catppuccin-mocha.nix; ctp = import ../catppuccin-mocha.nix;
in in
{ {
imports = [ imports = [
+6 -5
View File
@@ -2,7 +2,7 @@
# Imported via ./desktop.nix, so only graphical hosts get it. # Imported via ./desktop.nix, so only graphical hosts get it.
# #
# The compositor binary, PAM and the polkit *daemon* come from the system-level # The compositor binary, PAM and the polkit *daemon* come from the system-level
# programs.sway (see ../modules/sway.nix); package = null below reuses it instead of # programs.sway (see ../swaywm.nix); package = null below reuses it instead of
# pulling a second Sway. The polkit authentication *agent* (the thing that draws # pulling a second Sway. The polkit authentication *agent* (the thing that draws
# the GUI auth dialog) is a user service started here. home-manager owns the user # the GUI auth dialog) is a user service started here. home-manager owns the user
# config (~/.config/sway) and wires the systemd user session (sway-session.target), # config (~/.config/sway) and wires the systemd user session (sway-session.target),
@@ -20,7 +20,7 @@ let
# Catppuccin Mocha (shared with the ReGreet greeter). Raw hex; prefix "#" # Catppuccin Mocha (shared with the ReGreet greeter). Raw hex; prefix "#"
# where a consumer needs it -- Sway/i3status/dunst want "#", foot/swaylock do # where a consumer needs it -- Sway/i3status/dunst want "#", foot/swaylock do
# not. # not.
ctp = import ../lib/catppuccin-mocha.nix; ctp = import ../catppuccin-mocha.nix;
# Focused-window screenshot -> swappy editor (the dotfiles' grimshot.sh logic). # Focused-window screenshot -> swappy editor (the dotfiles' grimshot.sh logic).
# Full store paths so it needs nothing on PATH. # Full store paths so it needs nothing on PATH.
@@ -334,12 +334,13 @@ in
]; ];
}; };
# Night light. Manual location (no geoclue dependency); warmer at night, # Night light. Manual location (no geoclue dependency); adjust the coordinates
# neutral by day. Coordinates come from the per-user module (e.g. # to taste. Warmer at night, neutral by day.
# users/lyrathorpe/home.nix), not this shared module.
services.gammastep = { services.gammastep = {
enable = true; enable = true;
provider = "manual"; provider = "manual";
latitude = 51.5;
longitude = -0.13; # London-ish; set to your actual location
temperature = { temperature = {
day = 6500; day = 6500;
night = 3700; night = 3700;
@@ -1,11 +1,6 @@
# Work (EDaaS/WSL) home profile: corporate toolchain + tmux tweaks. Git identity # Home-manager module for the work (EDaaS/WSL) profile: corporate git signing,
# comes from the registry (users/registry.nix), not here. # work toolchain packages and tmux tweaks. Imported only by the work host.
{ { 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).
@@ -17,6 +12,15 @@
# programs.ssh (shell.nix) take it over. The ssh-agent below still runs. # programs.ssh (shell.nix) take it over. The ssh-agent below still runs.
programs.ssh.enable = lib.mkForce false; programs.ssh.enable = lib.mkForce false;
programs.git = {
settings = {
commit.gpgsign = true;
tag.gpgsign = true;
gpg.format = "ssh";
user.signingkey = "key::ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAJMVgeRKnfX1G8coU3nAobI485aeUpGTMqH7+zbKI8o emma.thorpe@cloud.com";
user.email = "emma.thorpe@citrix.com";
};
};
home.packages = [ home.packages = [
pkgs.kubectl pkgs.kubectl
pkgs.argo-rollouts pkgs.argo-rollouts
@@ -29,6 +33,7 @@
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
@@ -52,10 +57,8 @@
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 "#(${pkgs.bash}/bin/bash ${inputs.kube-tmux}/kube.tmux 250 red black)" set -g status-right "#(/run/current-system/sw/bin/bash $HOME/code/kube-tmux/kube.tmux 250 red black)"
''; '';
}; };
programs.go = { programs.go = {
@@ -63,7 +66,7 @@
}; };
# LSP servers only relevant to work: C# (omnisharp) and Helm charts (helm_ls). # LSP servers only relevant to work: C# (omnisharp) and Helm charts (helm_ls).
# The shared editor (home/editor.nix) carries the universal ones; # The shared editor (lyrathorpe/home/editor.nix) carries the universal ones;
# these are gated to this host so the heavy omnisharp closure stays off the # these are gated to this host so the heavy omnisharp closure stays off the
# personal machines. Tree-sitter grammars (highlighting) remain global there. # personal machines. Tree-sitter grammars (highlighting) remain global there.
programs.nixvim.plugins.lsp.servers = { programs.nixvim.plugins.lsp.servers = {
+2 -2
View File
@@ -7,8 +7,8 @@
let let
cfg = config.features.swayDesktop; cfg = config.features.swayDesktop;
# Catppuccin Mocha (shared with the Sway desktop, see home/sway.nix). # Catppuccin Mocha (shared with the Sway desktop, see lyrathorpe/home/sway.nix).
ctp = import ../lib/catppuccin-mocha.nix; ctp = import ./catppuccin-mocha.nix;
in in
{ {
# The features.swayDesktop.enable option is declared in # The features.swayDesktop.enable option is declared in
+28
View File
@@ -0,0 +1,28 @@
{
config,
pkgs,
lib,
username,
fullName,
...
}:
{
programs.zsh.enable = true;
users.users.${username} = {
isNormalUser = true;
home = "/home/${username}";
description = fullName;
extraGroups = [
"wheel"
"docker"
];
shell = pkgs.zsh;
};
programs.firefox = lib.mkIf (config.features.swayDesktop.enable == true) {
enable = true;
};
programs.thunderbird = lib.mkIf (config.features.swayDesktop.enable == true) {
enable = true;
};
}
-14
View File
@@ -1,14 +0,0 @@
# sshd for the hosts that run it (T400, Mac Pro, RPi5): enable the daemon, open
# port 22, and apply a key-only policy. Authorized keys are owned per-user by the
# registry (modules/users.nix), not here.
{ ... }:
{
services.openssh.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ];
services.openssh.settings = {
PasswordAuthentication = false; # keys only
KbdInteractiveAuthentication = false; # no keyboard-interactive fallback
PermitRootLogin = "no";
};
}
-38
View File
@@ -1,38 +0,0 @@
# System user accounts, built from the registry (users/registry.nix) for the
# host's `hostUsers` set. See README "Users".
{
config,
pkgs,
lib,
hostUsers,
userRegistry,
...
}:
{
programs.zsh.enable = true;
users.users = lib.mapAttrs (
name: spec:
let
id = userRegistry.${name};
in
{
isNormalUser = true;
home = "/home/${name}";
description = id.fullName;
inherit (id) extraGroups;
openssh.authorizedKeys.keys = id.sshAuthorizedKeys;
shell = pkgs.zsh;
}
# linger opt-in (host table); left unmanaged when unset.
// lib.optionalAttrs (spec ? linger) { inherit (spec) linger; }
) hostUsers;
programs.firefox = lib.mkIf (config.features.swayDesktop.enable == true) {
enable = true;
};
programs.thunderbird = lib.mkIf (config.features.swayDesktop.enable == true) {
enable = true;
};
}
@@ -1,5 +1,5 @@
# Default nix-darwin host. Minimal macOS baseline; the user environment # Default nix-darwin host. Minimal macOS baseline; the user environment
# (shell, git, editor) is carried by the shared ./home modules, # (shell, git, editor) is carried by the shared ./lyrathorpe/home modules,
# the same ones used by the Linux hosts. nixpkgs.hostPlatform is set by # the same ones used by the Linux hosts. nixpkgs.hostPlatform is set by
# mkDarwinHost in flake.nix. # mkDarwinHost in flake.nix.
{ pkgs, username, ... }: { pkgs, username, ... }:
@@ -97,7 +97,6 @@
"llvm@21" "llvm@21"
"lld@21" "lld@21"
"python@3.14" "python@3.14"
"dosbox-staging"
]; ];
# GUI applications. macOS app bundles are managed as casks; nixpkgs darwin # GUI applications. macOS app bundles are managed as casks; nixpkgs darwin
# GUI support is unreliable, so these stay on brew for continuity. # GUI support is unreliable, so these stay on brew for continuity.
@@ -19,7 +19,9 @@
defaultUser = "emmathorpe"; defaultUser = "emmathorpe";
wslConf.automount.root = "/mnt"; wslConf.automount.root = "/mnt";
wslConf.interop.appendWindowsPath = true; wslConf.interop.appendWindowsPath = true;
wslConf.interop.register = true;
wslConf.interop.enabled = true; wslConf.interop.enabled = true;
wslConf.interop.includePath = true;
wslConf.network.generateHosts = false; wslConf.network.generateHosts = false;
startMenuLaunchers = true; startMenuLaunchers = true;
docker-desktop.enable = false; docker-desktop.enable = false;
@@ -62,11 +64,12 @@
features.swayDesktop.enable = false; features.swayDesktop.enable = false;
# NOTE: this user's systemd --user lingering -- so the home-manager renovate # Keep this user's systemd --user instance running without an open login
# timer fires without an open login session -- is enabled from the host table # session, so the home-manager user timer (renovate-review.nix) fires on
# in flake.nix (users.emmathorpe.linger = true) and applied by # schedule even when no terminal is attached. On WSL the timer still only runs
# modules/users.nix. # while the distro itself is up; Persistent=true catches up a missed run at
# next start.
users.users.emmathorpe.linger = true;
# programs.nix-ld is enabled for all NixOS hosts in common-nixos.nix. # programs.nix-ld is enabled for all NixOS hosts in common-nixos.nix.
# This value determines the NixOS release from which the default # This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions # settings for stateful data, like file locations and database versions
@@ -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 `../../modules/sway.nix` for `cage` kiosk compositor — configured centrally in `lyrathorpe/swaywm.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
@@ -26,8 +26,10 @@
# workstation.nix is the backstop). # workstation.nix is the backstop).
zramSwap.enable = true; zramSwap.enable = true;
# sshd (daemon, port 22, key-only policy) comes from ../../modules/ssh.nix; # This host accepts SSH, so open 22 (the firewall itself is enabled in
# the firewall itself is enabled in workstation.nix with a default-deny policy. # 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.
@@ -40,7 +42,7 @@
# - ATI Radeon HD 2600 XT -> "radeon" (older) or "amdgpu" KMS # - ATI Radeon HD 2600 XT -> "radeon" (older) or "amdgpu" KMS
# - NVIDIA GeForce 8800 GT -> "nouveau" KMS # - NVIDIA GeForce 8800 GT -> "nouveau" KMS
# These come up automatically via the in-tree drivers + KMS, and the graphics # These come up automatically via the in-tree drivers + KMS, and the graphics
# stack itself is enabled by modules/sway.nix. If a card needs to be forced, add it # stack itself is enabled by swaywm.nix. If a card needs to be forced, add it
# here, e.g. `services.xserver.videoDrivers = [ "radeon" ];` (or "nouveau"), # here, e.g. `services.xserver.videoDrivers = [ "radeon" ];` (or "nouveau"),
# and/or `boot.initrd.kernelModules = [ "radeon" ];` in # and/or `boot.initrd.kernelModules = [ "radeon" ];` in
# hardware-configuration.nix for early KMS. # hardware-configuration.nix for early KMS.
@@ -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
# hosts/RPi5/hardware-configuration.nix in this repo, then commit # system/machine/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,8 +28,7 @@ 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
the user registry (`../../users/registry.nix`, applied by `system/modules/ssh.nix` is the one you will connect with.
`../../modules/ssh.nix`) is the one you will connect with.
## Docker socket (security) ## Docker socket (security)
@@ -15,7 +15,7 @@
# (which selects by the local hostname) resolves without an explicit -H flag. # (which selects by the local hostname) resolves without an explicit -H flag.
networking.hostName = "lyrathorpe-rpi5"; networking.hostName = "lyrathorpe-rpi5";
# Headless server: the Sway desktop is intentionally not set up. modules/sway.nix is # Headless server: the Sway desktop is intentionally not set up. swaywm.nix is
# not imported and features.swayDesktop.enable defaults to false (declared in # not imported and features.swayDesktop.enable defaults to false (declared in
# system/modules/features.nix), so this host keeps plain TTY/SSH login. # system/modules/features.nix), so this host keeps plain TTY/SSH login.
@@ -25,12 +25,15 @@
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: the daemon, port 22 and key-only policy all come from # Remote administration. Key-only policy and the authorized key come from
# ../../modules/ssh.nix. # ../../modules/ssh.nix; here we just enable the daemon and open the port.
services.openssh.enable = true;
# Default-deny inbound; the Docker and nginx submodules open their own ports # Default-deny inbound. Open only SSH here; the Docker and nginx submodules
# (Docker via a source-restricted nftables rule, nginx via 80/443). # open their own ports (Docker via a source-restricted nftables rule, nginx
# 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";
@@ -8,10 +8,6 @@
# 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;
@@ -33,6 +29,6 @@ in
# 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 ${trustedSubnet} tcp dport 2375 accept ip saddr 10.187.1.0/24 tcp dport 2375 accept
''; '';
} }
@@ -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 `../../modules/sway.nix` for `cage` kiosk compositor — configured centrally in `lyrathorpe/swaywm.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
@@ -21,8 +21,10 @@
# 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;
# sshd (daemon, port 22, key-only policy) comes from ../../modules/ssh.nix; # This host accepts SSH, so open 22 (the firewall itself is enabled in
# the firewall itself is enabled in laptop.nix with a default-deny policy. # 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
@@ -2,7 +2,7 @@
# shared ./workstation.nix base and swaps the mobile Wi-Fi backend for wired # shared ./workstation.nix base and swaps the mobile Wi-Fi backend for wired
# NetworkManager. A desktop host also sets `portable = false` in its host-table # NetworkManager. A desktop host also sets `portable = false` in its host-table
# entry (flake.nix), which drops the battery block and brightness keybindings # entry (flake.nix), which drops the battery block and brightness keybindings
# from the Sway bar -- see home/sway.nix. # from the Sway bar -- see lyrathorpe/home/sway.nix.
{ ... }: { ... }:
{ {
imports = [ ./workstation.nix ]; imports = [ ./workstation.nix ];
@@ -2,9 +2,9 @@
# baseModules in flake.nix). Declaring the flags here -- rather than inside the # baseModules in flake.nix). Declaring the flags here -- rather than inside the
# module that implements them -- means a host can read or set a flag without # module that implements them -- means a host can read or set a flag without
# importing the (often large) implementation module. In particular, # importing the (often large) implementation module. In particular,
# features.swayDesktop.enable is read by modules/users.nix on every host, but a # features.swayDesktop.enable is read by lyrathorpe/user.nix on every host, but a
# headless host (e.g. the Pi) must be able to leave it at its default without # headless host (e.g. the Pi) must be able to leave it at its default without
# pulling in modules/sway.nix. The implementation lives in modules/sway.nix, # pulling in lyrathorpe/swaywm.nix. The implementation lives in swaywm.nix,
# gated on this flag. # gated on this flag.
{ lib, ... }: { lib, ... }:
{ {
@@ -2,7 +2,7 @@
# flake.nix. Shared graphical-workstation settings live in ./workstation.nix; # flake.nix. Shared graphical-workstation settings live in ./workstation.nix;
# the only laptop-specific bit is the Wi-Fi backend. Mobile home-manager # the only laptop-specific bit is the Wi-Fi backend. Mobile home-manager
# components (battery block, brightness keys) are gated by the `portable` flag # components (battery block, brightness keys) are gated by the `portable` flag
# threaded through mkHost -- see home/sway.nix. # threaded through mkHost -- see lyrathorpe/home/sway.nix.
{ ... }: { ... }:
{ {
imports = [ ./workstation.nix ]; imports = [ ./workstation.nix ];
+19
View File
@@ -0,0 +1,19 @@
# Key-only SSH hardening, imported by the hosts that run sshd (T400, Mac Pro).
# The host config still does `services.openssh.enable = true` and opens port 22
# next to where it documents the listening service; this module only tightens
# the policy and installs the authorized key, so a host opting into sshd cannot
# accidentally ship password/root login.
{ username, ... }:
{
services.openssh.settings = {
PasswordAuthentication = false; # keys only
KbdInteractiveAuthentication = false; # no keyboard-interactive fallback
PermitRootLogin = "no";
};
# The key permitted to log in as the primary user. Add more entries here as
# new client machines are provisioned.
users.users.${username}.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDxHvdMTOzpFWUFMtCP7C/4tIOUO3GIO2QPvaifSnWH lyrathorpe@Lyra-MBA"
];
}
-17
View File
@@ -1,17 +0,0 @@
# Lyra's personal home extras, imported on her hosts (not the work box). Keeps
# personal data out of the shared home/ modules. See README "Users".
{ pkgs, lib, ... }:
{
# Personal ssh host shortcut.
programs.ssh.settings."dockerpi.inf.cbg.emmaisvery.gay" = {
User = "emmathorpe";
};
# Night-light location for gammastep (the service itself is enabled by
# home/sway.nix on graphical hosts). Linux-guarded so Darwin, which imports
# this module but has no gammastep, skips it.
services.gammastep = lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
latitude = 51.5;
longitude = -0.13;
};
}
-28
View File
@@ -1,28 +0,0 @@
# User identity registry -- pure data, keyed by username. See README "Users".
# (`identity.username` is injected by mkHost, so it is not repeated here.)
{
lyrathorpe = {
fullName = "Lyra Thorpe";
email = "iam@emmathe.dev";
extraGroups = [
"wheel"
"docker"
];
sshAuthorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDxHvdMTOzpFWUFMtCP7C/4tIOUO3GIO2QPvaifSnWH lyrathorpe@Lyra-MBA"
];
signingKey = "key::ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDxHvdMTOzpFWUFMtCP7C/4tIOUO3GIO2QPvaifSnWH lyrathorpe@Lyra-MBA";
};
emmathorpe = {
fullName = "Emma Thorpe";
email = "emma.thorpe@citrix.com";
extraGroups = [
"wheel"
"docker"
];
# No personal key on file yet; add one if SSH login as emmathorpe is wanted.
sshAuthorizedKeys = [ ];
signingKey = "key::ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAJMVgeRKnfX1G8coU3nAobI485aeUpGTMqH7+zbKI8o emma.thorpe@cloud.com";
};
}