Merge pull request 'Feat/audit improvements' (#24) from feat/audit-improvements into main
CI / flake (push) Successful in 3m38s

Reviewed-on: #24
This commit was merged in pull request #24.
This commit is contained in:
2026-06-10 17:08:25 +01:00
21 changed files with 446 additions and 60 deletions
+8 -3
View File
@@ -1,4 +1,5 @@
# Flake CI: formatting gate + evaluation of every host configuration. # Flake CI: full `nix flake check` (formatting + deadnix + statix + pre-commit)
# plus an explicit per-host evaluation pass for granular output.
name: CI name: CI
on: on:
@@ -27,9 +28,13 @@ jobs:
extra_nix_config: | extra_nix_config: |
experimental-features = nix-command flakes experimental-features = nix-command flakes
accept-flake-config = true accept-flake-config = true
substituters = https://cache.nixos.org https://nix-community.cachix.org
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=
- name: Check formatting # Runs every flake check: treefmt formatting, deadnix, statix, and the
run: nix build --print-build-logs '.#checks.x86_64-linux.formatting' # pre-commit hooks (so a --no-verify commit can't ship unlinted).
- name: Flake check
run: nix flake check --print-build-logs
# Evaluate (not build) each host's toplevel so eval errors fail CI cheaply. # Evaluate (not build) each host's toplevel so eval errors fail CI cheaply.
# aarch64 / darwin hosts evaluate fine on an x86_64 runner; only building # aarch64 / darwin hosts evaluate fine on an x86_64 runner; only building
+8 -4
View File
@@ -16,8 +16,11 @@ Defined in the host table in [`flake.nix`](./flake.nix):
| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) | | `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) |
Shared layers: `lyrathorpe/home` (home-manager: shell, git, editor), Shared layers: `lyrathorpe/home` (home-manager: shell, git, editor),
`system/modules/common-nixos.nix` (all NixOS hosts), and `system/modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches),
`system/modules/laptop.nix` (the physical laptops). `system/modules/workstation.nix` (physical graphical hosts: audio, thermald,
earlyoom, fwupd), `system/modules/laptop.nix` (laptops: Wi-Fi, Bluetooth, power,
lid), and `system/modules/ssh.nix` (key-only sshd). The x86 hosts also pull
`nixos-hardware` profiles.
## Applying ## Applying
@@ -74,5 +77,6 @@ A dev shell and a formatting/lint gate are wired through the flake:
## CI ## CI
[`.gitea/workflows/ci.yaml`](./.gitea/workflows/ci.yaml) gates `nixfmt` [`.gitea/workflows/ci.yaml`](./.gitea/workflows/ci.yaml) runs `nix flake check`
formatting and evaluates every NixOS and Darwin host configuration on push/PR. (formatting, `deadnix`, `statix`, the pre-commit hooks) and evaluates every
NixOS and Darwin host configuration on push/PR.
Generated
+21
View File
@@ -271,6 +271,26 @@
"type": "github" "type": "github"
} }
}, },
"nixos-hardware": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1781020964,
"narHash": "sha256-fS7xTi2j2iso5Hj7RNZLv/acDlCT+fgMVkVk40A7Uco=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "32c2cd9e46286c4eced3dc6b613c659126bf3cca",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixos-hardware",
"type": "github"
}
},
"nixos-wsl": { "nixos-wsl": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_3", "flake-compat": "flake-compat_3",
@@ -357,6 +377,7 @@
"nix-homebrew": "nix-homebrew", "nix-homebrew": "nix-homebrew",
"nix-index-database": "nix-index-database", "nix-index-database": "nix-index-database",
"nixos-apple-silicon": "nixos-apple-silicon", "nixos-apple-silicon": "nixos-apple-silicon",
"nixos-hardware": "nixos-hardware",
"nixos-wsl": "nixos-wsl", "nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable", "nixpkgs-unstable": "nixpkgs-unstable",
+17
View File
@@ -54,6 +54,12 @@
url = "github:nix-community/nixvim/nixos-26.05"; url = "github:nix-community/nixvim/nixos-26.05";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# Curated per-hardware profiles (microcode, SSD, platform quirks) for the
# physical x86 hosts.
nixos-hardware = {
url = "github:NixOS/nixos-hardware";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = outputs =
@@ -232,6 +238,14 @@
modules = [ modules = [
./system/machine/T400/configuration.nix ./system/machine/T400/configuration.nix
./system/modules/laptop.nix ./system/modules/laptop.nix
./system/modules/ssh.nix
# No t400-specific profile exists; compose the generic ThinkPad +
# laptop/SSD/Intel building blocks (tp_smapi/acpi_call for battery
# thresholds, SSD + microcode defaults).
inputs.nixos-hardware.nixosModules.lenovo-thinkpad
inputs.nixos-hardware.nixosModules.common-pc-laptop
inputs.nixos-hardware.nixosModules.common-pc-laptop-ssd
inputs.nixos-hardware.nixosModules.common-cpu-intel
./lyrathorpe/swaywm.nix ./lyrathorpe/swaywm.nix
]; ];
homeModules = [ homeModules = [
@@ -248,6 +262,9 @@
modules = [ modules = [
./system/machine/MacPro31/configuration.nix ./system/machine/MacPro31/configuration.nix
./system/modules/desktop.nix ./system/modules/desktop.nix
./system/modules/ssh.nix
inputs.nixos-hardware.nixosModules.common-pc-ssd
inputs.nixos-hardware.nixosModules.common-cpu-intel
./lyrathorpe/swaywm.nix ./lyrathorpe/swaywm.nix
]; ];
homeModules = [ homeModules = [
+8 -1
View File
@@ -177,6 +177,11 @@ across vim splits and tmux panes seamlessly. Everything else is stock vim, plus:
| ---------------------- | --------------------------------------------------------- | | ---------------------- | --------------------------------------------------------- |
| `,``,` | Toggle the file tree (nvim-tree) — comma pressed twice | | `,``,` | Toggle the file tree (nvim-tree) — comma pressed twice |
| `Ctrl`+`h`/`j`/`k`/`l` | Move between vim splits / tmux panes (vim-tmux-navigator) | | `Ctrl`+`h`/`j`/`k`/`l` | Move between vim splits / tmux panes (vim-tmux-navigator) |
| `<leader>ff` | Find files (telescope) |
| `<leader>fg` | Live grep (telescope) |
| `<leader>fb` | Switch buffer (telescope) |
| `<leader>xx` | Diagnostics list (trouble) |
| `gc` / `gcc` | Toggle comment (selection / line) |
| `gd` | Go to definition (LSP) | | `gd` | Go to definition (LSP) |
| `gr` | List references (LSP) | | `gr` | List references (LSP) |
| `K` | Hover documentation (LSP) | | `K` | Hover documentation (LSP) |
@@ -184,7 +189,9 @@ across vim splits and tmux panes seamlessly. Everything else is stock vim, plus:
| `<leader>ca` | Code action (LSP) | | `<leader>ca` | Code action (LSP) |
LSP covers Nix, Lua, Python and Terraform (the work box adds C# and Helm); LSP covers Nix, Lua, Python and Terraform (the work box adds C# and Helm);
completion (nvim-cmp) appears as you type. `:Git` opens fugitive. completion (nvim-cmp) appears as you type. Files are formatted on save
(conform-nvim). `:Git` opens fugitive; gitsigns shows gutter signs. which-key
pops up after `<leader>` to show the rest.
--- ---
+31 -15
View File
@@ -44,15 +44,18 @@ and the C#/Helm language servers).
| `eza` | modern `ls` (drives the ls aliases) | | `eza` | modern `ls` (drives the ls aliases) |
| `bat` | syntax-highlighting pager (Catppuccin Mocha theme); behaves like `cat` when piped; also the `MANPAGER` | | `bat` | syntax-highlighting pager (Catppuccin Mocha theme); behaves like `cat` when piped; also the `MANPAGER` |
| `ripgrep` / `fd` | fast search (`rg`) and find (`fd`); also back `fzf` | | `ripgrep` / `fd` | fast search (`rg`) and find (`fd`); also back `fzf` |
| `jq` / `btop` | JSON processor; resource monitor | | `jq` | JSON processor |
| `gh` / `tea` | GitHub and Gitea (`code.emmathe.dev`) CLIs; `gh` uses SSH | | `gh` / `tea` | GitHub and Gitea (`code.emmathe.dev`) CLIs; `gh` uses SSH |
| `nix-index` | `command-not-found`: an unknown command tells you which Nix package provides it (prebuilt DB, no manual indexing) | | `nix-index` | `command-not-found`: an unknown command tells you which Nix package provides it (prebuilt DB, no manual indexing) |
| `comma` (`,`) | run an uninstalled program once: `, cowsay hi` | | `comma` (`,`) | run an uninstalled program once: `, cowsay hi` |
| `nh` | nicer `nixos-rebuild`/`home-manager` with diffs; `$NH_FLAKE` set to the repo. No scheduled GC (it could reap paths a running generation still references) — collect garbage manually with `nh clean all` / `nix-collect-garbage -d` | | `nh` | nicer `nixos-rebuild`/`home-manager` with diffs; `$NH_FLAKE` set to the repo. No scheduled GC (it could reap paths a running generation still references) — collect garbage manually with `nh clean all` / `nix-collect-garbage -d` |
| `btop` | resource monitor, themed Catppuccin Mocha (vendored theme) |
| `lazygit` | git TUI for staging/rebasing, themed to match (`git.nix`) |
| `hyperfine` / `sd` | command-line benchmarking; saner find-and-replace than sed |
**Theming:** `fzf`, `bat` and `git`'s `delta` pager are all Catppuccin Mocha, **Theming:** `fzf`, `bat`, `btop`, `lazygit` and `git`'s `delta` pager are all
driven from the shared `../catppuccin-mocha.nix` palette / the catppuccin/bat Catppuccin Mocha, driven from the shared `../catppuccin-mocha.nix` palette / the
theme. 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`
(the editor owns `$EDITOR`/`$VISUAL`); `xdg.mimeApps` maps web→Firefox, (the editor owns `$EDITOR`/`$VISUAL`); `xdg.mimeApps` maps web→Firefox,
@@ -80,17 +83,22 @@ non-interactive shells. Escape hatch: `NO_TMUX=1 <terminal>` opens a bare shell.
| Clipboard | `set-clipboard on`; foot `terminal-features` advertise truecolor/sync/OSC52/title/cursor | | Clipboard | `set-clipboard on`; foot `terminal-features` advertise truecolor/sync/OSC52/title/cursor |
**Plugins:** `sensible`, `vim-tmux-navigator` (Ctrl-h/j/k/l across vim ↔ tmux), **Plugins:** `sensible`, `vim-tmux-navigator` (Ctrl-h/j/k/l across vim ↔ tmux),
`yank`, `catppuccin` (Mocha statusline), `resurrect` + `continuum` `yank`, `extrakto` (`prefix`+`Tab`: fzf-grab paths/URLs/text from the pane into
the prompt), `catppuccin` (Mocha statusline), `resurrect` + `continuum`
(sessions auto-save and restore across reboots). The statusline draws Nerd-Font (sessions auto-save and restore across reboots). The statusline draws Nerd-Font
glyphs — see Fonts. glyphs — see Fonts.
## Fonts ## Fonts
**JetBrainsMono Nerd Font** is installed on every host (in `common-nixos.nix`, **JetBrainsMono Nerd Font**, **Noto Sans** and **Noto Color Emoji** are
because tmux runs everywhere; the Mac installs it to `/Library/Fonts` via the installed on every host (in `common-nixos.nix`, because tmux/terminals run
Darwin config). foot uses it as its main font automatically. iTerm2's font is a everywhere; the Mac installs the Nerd Font to `/Library/Fonts` via the Darwin
GUI setting — set it to _JetBrainsMono Nerd Font_ (Settings → Profiles → Text → config). `fonts.fontconfig.defaultFonts` maps the generic families so anything
Font) so the tmux statusline glyphs render instead of `?`. asking for `monospace` gets the Nerd Font (with emoji fallback) — this also
gives the WSL box emoji/sans coverage it otherwise lacked. foot uses the Nerd
Font as its main font automatically. iTerm2's font is a GUI setting — set it to
_JetBrainsMono Nerd Font_ (Settings → Profiles → Text → Font) so the tmux
statusline glyphs render instead of `?`.
## Editor (Neovim) ## Editor (Neovim)
@@ -100,13 +108,19 @@ 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 |
| Format on save | conform-nvim (nixfmt, stylua, ruff, shfmt, prettier, gofumpt; LSP fallback otherwise) |
| Git | fugitive (`:Git …`) + gitsigns gutter signs/blame |
| Diagnostics | inline + trouble list (`<leader>xx`) |
| 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) |
| Git | fugitive (`:Git …`) | | Statusline | lualine (Catppuccin theme) |
| 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) — replaces `syntax enable` | | 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` (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 |
@@ -121,12 +135,14 @@ host-specific ones go in that host's module — the work box (`work.nix`) adds
## git ## git
Pager is **delta**. **commitizen** is installed on every host; `cz` defaults to Pager is **delta**. **commitizen** is installed on every host; `cz` defaults to
Conventional Commits. Conventional Commits. **lazygit** (themed) is the TUI. The commit-graph is kept
current (`gc`/`fetch.writeCommitGraph`) so `lg` stays fast.
| Aliases | | | Aliases | |
| ------------------------ | ----------------------------------------------------------------- | | ------------------------ | ------------------------------------------------------------------ |
| `st` `co` `sw` `br` `ci` | status / checkout / switch / branch / commit | | `st` `co` `sw` `br` `ci` | status / checkout / switch / branch / commit |
| `last` `unstage` | last commit / unstage | | `last` `unstage` | last commit / unstage |
| `amend` `fixup` `undo` | amend-no-edit / `commit --fixup` / soft-reset HEAD~1 (keep staged) |
| `lg` | graph log, all branches | | `lg` | graph log, all branches |
| `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 |
+1 -1
View File
@@ -1,6 +1,6 @@
# Base home-manager profile, shared by every host (graphical or headless). # Base home-manager profile, shared by every host (graphical or headless).
# Graphical hosts additionally import ./desktop.nix; the work host imports # Graphical hosts additionally import ./desktop.nix; the work host imports
# ../../system/modules/work/default.nix. See the host table in flake.nix. # ./work.nix. See the host table in flake.nix.
{ ... }: { ... }:
{ {
imports = [ imports = [
+78 -2
View File
@@ -2,7 +2,7 @@
# tree, indent guides, fugitive, tmux-navigator, Catppuccin Mocha, 2-space hard # tree, indent guides, fugitive, tmux-navigator, Catppuccin Mocha, 2-space hard
# tabs, Jenkinsfile=groovy) plus a real LSP stack in place of the inert ALE. # tabs, Jenkinsfile=groovy) plus a real LSP stack in place of the inert ALE.
# Wanted on every host; vi/vim/$EDITOR all launch nvim. # Wanted on every host; vi/vim/$EDITOR all launch nvim.
{ inputs, ... }: { inputs, pkgs, ... }:
{ {
imports = [ inputs.nixvim.homeModules.nixvim ]; imports = [ inputs.nixvim.homeModules.nixvim ];
@@ -16,6 +16,17 @@
# warn that its pinned nixpkgs was overridden by the input `follows`. # warn that its pinned nixpkgs was overridden by the input `follows`.
nixpkgs.source = inputs.nixpkgs; nixpkgs.source = inputs.nixpkgs;
# Formatter binaries for conform-nvim (below), matching the repo's treefmt
# set. On nvim's PATH only.
extraPackages = with pkgs; [
nixfmt
stylua
ruff
shfmt
prettier
gofumpt
];
globals.mapleader = " "; globals.mapleader = " ";
opts = { opts = {
@@ -77,14 +88,55 @@
cmp = { cmp = {
enable = true; enable = true;
autoEnableSources = true; autoEnableSources = true;
settings.sources = [ settings = {
snippet.expand = "function(args) require('luasnip').lsp_expand(args.body) end";
sources = [
{ name = "nvim_lsp"; } { name = "nvim_lsp"; }
{ name = "luasnip"; }
{ name = "buffer"; } { name = "buffer"; }
{ name = "path"; } { name = "path"; }
]; ];
}; };
}; };
# Fuzzy finder (files / live grep / symbols); rg + fd are already on PATH.
telescope = {
enable = true;
extensions.fzf-native.enable = true;
};
gitsigns.enable = true; # gutter signs, stage-hunk, blame
which-key.enable = true; # popup of pending keybindings (leader is Space)
trouble.enable = true; # project-wide diagnostics/quickfix list
lualine = {
enable = true;
settings.options.theme = "catppuccin";
};
comment.enable = true; # gc / gcc comment toggling
nvim-autopairs.enable = true;
treesitter-textobjects.enable = true;
luasnip.enable = true; # snippet engine (drives cmp's luasnip source above)
# Format-on-save, mirroring the repo's treefmt set. Filetypes with no
# formatter here (e.g. terraform) fall back to the LSP formatter.
conform-nvim = {
enable = true;
settings = {
formatters_by_ft = {
nix = [ "nixfmt" ];
lua = [ "stylua" ];
python = [ "ruff_format" ];
sh = [ "shfmt" ];
markdown = [ "prettier" ];
go = [ "gofumpt" ];
};
format_on_save = {
timeout_ms = 2000;
lsp_format = "fallback";
};
};
};
};
keymaps = [ keymaps = [
{ {
mode = "n"; mode = "n";
@@ -92,6 +144,30 @@
action = "<cmd>NvimTreeToggle<cr>"; action = "<cmd>NvimTreeToggle<cr>";
options.desc = "Toggle file tree"; options.desc = "Toggle file tree";
} }
{
mode = "n";
key = "<leader>ff";
action = "<cmd>Telescope find_files<cr>";
options.desc = "Find files";
}
{
mode = "n";
key = "<leader>fg";
action = "<cmd>Telescope live_grep<cr>";
options.desc = "Live grep";
}
{
mode = "n";
key = "<leader>fb";
action = "<cmd>Telescope buffers<cr>";
options.desc = "Buffers";
}
{
mode = "n";
key = "<leader>xx";
action = "<cmd>Trouble diagnostics toggle<cr>";
options.desc = "Diagnostics list";
}
]; ];
# au BufNewFile,BufRead *Jenkinsfile setf groovy # au BufNewFile,BufRead *Jenkinsfile setf groovy
+24 -2
View File
@@ -1,11 +1,14 @@
# Version control: git + delta pager + commitizen. The work host layers # Version control: git + delta pager + commitizen + lazygit. The work host
# commit signing and an email override on top (see work/default.nix). # layers commit signing and an email override on top (see work.nix).
{ {
pkgs, pkgs,
lib, lib,
fullName, fullName,
... ...
}: }:
let
ctp = import ../catppuccin-mocha.nix;
in
{ {
home.packages = [ home.packages = [
pkgs.commitizen pkgs.commitizen
@@ -31,6 +34,9 @@
}; };
fetch.prune = true; # drop deleted remote-tracking branches fetch.prune = true; # drop deleted remote-tracking branches
# Keep the commit-graph current (fast `git log --graph`, used by `lg`).
fetch.writeCommitGraph = true;
gc.writeCommitGraph = true;
merge.conflictStyle = "zdiff3"; # show the common ancestor in conflicts merge.conflictStyle = "zdiff3"; # show the common ancestor in conflicts
diff = { diff = {
algorithm = "histogram"; algorithm = "histogram";
@@ -61,6 +67,9 @@
ci = "commit"; ci = "commit";
last = "log -1 HEAD"; last = "log -1 HEAD";
unstage = "reset HEAD --"; unstage = "reset HEAD --";
amend = "commit --amend --no-edit"; # tack staged changes onto HEAD
fixup = "commit --fixup"; # `git fixup <sha>` -> autosquash on next rebase
undo = "reset --soft HEAD~1"; # undo last commit, keep the changes staged
lg = "log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)' --all"; lg = "log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)' --all";
# commitizen (Conventional Commits, its default ruleset): `git cz c` -> # commitizen (Conventional Commits, its default ruleset): `git cz c` ->
# `cz commit`, `git cz bump`, etc. `git cc` is a shortcut for the prompt. # `cz commit`, `git cz bump`, etc. `git cc` is a shortcut for the prompt.
@@ -92,4 +101,17 @@
enable = true; enable = true;
enableGitIntegration = true; enableGitIntegration = true;
}; };
# lazygit: TUI for staging/rebasing, themed to Catppuccin Mocha to match.
programs.lazygit = {
enable = true;
settings.gui.theme = {
activeBorderColor = [
"#${ctp.blue}"
"bold"
];
inactiveBorderColor = [ "#${ctp.surface1}" ];
selectedLineBgColor = [ "#${ctp.surface0}" ];
};
};
} }
+15 -2
View File
@@ -23,10 +23,22 @@ in
pkgs.ripgrep pkgs.ripgrep
pkgs.fd pkgs.fd
pkgs.jq pkgs.jq
pkgs.btop
pkgs.tea pkgs.tea
pkgs.hyperfine # command-line benchmarking
pkgs.sd # saner find-and-replace than sed
]; ];
# Resource monitor, themed Catppuccin Mocha to match the rest of the desktop.
# btop does not bundle the theme, so vendor it from catppuccin/btop (pinned).
programs.btop = {
enable = true;
settings.color_theme = "catppuccin_mocha";
};
xdg.configFile."btop/themes/catppuccin_mocha.theme".source = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/catppuccin/btop/f437574b600f1c6d932627050b15ff5153b58fa3/themes/catppuccin_mocha.theme";
hash = "sha256-THRpq5vaKCwf9gaso3ycC4TNDLZtBB5Ofh/tOXkfRkQ=";
};
programs.zsh = { programs.zsh = {
enable = true; enable = true;
# Keep zsh dotfiles under XDG (~/.config/zsh) rather than the legacy $HOME # Keep zsh dotfiles under XDG (~/.config/zsh) rather than the legacy $HOME
@@ -218,6 +230,7 @@ in
sensible sensible
vim-tmux-navigator # Ctrl-h/j/k/l across vim splits and tmux panes vim-tmux-navigator # Ctrl-h/j/k/l across vim splits and tmux panes
yank yank
extrakto # prefix+Tab: fzf-grab paths/URLs/text from the pane into the prompt
{ {
# Catppuccin Mocha statusline (v2 API: flavour + window options must be # Catppuccin Mocha statusline (v2 API: flavour + window options must be
# set before the plugin loads, which home-manager does for plugin # set before the plugin loads, which home-manager does for plugin
@@ -283,7 +296,7 @@ in
# Add the key to the agent on first use, so the passphrase is typed once per # Add the key to the agent on first use, so the passphrase is typed once per
# login session rather than per commit/push (commit signing uses this agent). # login session rather than per commit/push (commit signing uses this agent).
# The work box keeps its own ssh config (see work/default.nix), so this only # The work box keeps its own ssh config (see work.nix), so this only
# manages ~/.ssh/config on the personal hosts. # manages ~/.ssh/config on the personal hosts.
programs.ssh = { programs.ssh = {
enable = true; enable = true;
+74 -4
View File
@@ -1,11 +1,13 @@
# Declarative Sway window manager, status bar, lock, idle and notifications. # Declarative Sway window manager, status bar, lock, idle and notifications.
# 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 polkit integration come from the system-level # The compositor binary, PAM and the polkit *daemon* come from the system-level
# programs.sway (see ../swaywm.nix); package = null below reuses it instead of # programs.sway (see ../swaywm.nix); package = null below reuses it instead of
# pulling a second Sway. home-manager owns the user config (~/.config/sway) and # pulling a second Sway. The polkit authentication *agent* (the thing that draws
# wires the systemd user session (sway-session.target), which is what lets the # the GUI auth dialog) is a user service started here. home-manager owns the user
# swayidle/dunst user services start with the desktop. # config (~/.config/sway) and wires the systemd user session (sway-session.target),
# which is what lets the agent/swayidle/dunst/kanshi user services start with the
# desktop.
{ {
pkgs, pkgs,
lib, lib,
@@ -99,6 +101,16 @@ in
criteria.app_id = "launcher"; criteria.app_id = "launcher";
command = "floating enable, resize set 800 500"; command = "floating enable, resize set 800 500";
} }
# Don't let swayidle blank/lock during fullscreen video. Two rules cover
# native Wayland (app_id) and XWayland (class) clients.
{
criteria.app_id = ".*";
command = "inhibit_idle fullscreen";
}
{
criteria.class = ".*";
command = "inhibit_idle fullscreen";
}
]; ];
# Binding modes (submenus). Entered from keybindings below; each action # Binding modes (submenus). Entered from keybindings below; each action
@@ -277,6 +289,64 @@ in
# an old entry through fuzzel. # an old entry through fuzzel.
services.clipman.enable = true; services.clipman.enable = true;
# Polkit authentication agent. programs.sway (system) enables the polkit
# daemon but no agent, so GUI privilege prompts (nemo mounting a disk,
# NetworkManager/blueman editing a system resource) would otherwise fail
# silently. lxqt-policykit is a small, toolkit-light agent; bind it to the
# Sway session so it starts and stops with the desktop.
systemd.user.services.polkit-lxqt = {
Unit = {
Description = "lxqt-policykit polkit authentication agent";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.lxqt.lxqt-policykit}/bin/lxqt-policykit-agent";
Restart = "on-failure";
};
Install.WantedBy = [ "sway-session.target" ];
};
# Output/display management. Reacts to hotplug and applies per-display
# mode/scale/position. Profiles are hardware-specific: the safe default below
# just enables the internal laptop panel; add docked/desktop profiles with the
# real identifiers from `swaymsg -t get_outputs` (e.g. the Mac Pro's Apple
# Cinema Display with its scale, or a docked laptop + external monitor).
services.kanshi = {
enable = true;
settings = [
{
profile.name = "undocked";
profile.outputs = [
{
criteria = "eDP-1";
status = "enable";
}
];
}
# Example to copy per host (fill in real criteria/mode/scale/position):
# {
# profile.name = "desktop";
# profile.outputs = [
# { criteria = "Apple Computer Inc Cinema HD ..."; mode = "2560x1600"; scale = 1.0; position = "0,0"; status = "enable"; }
# ];
# }
];
};
# Night light. Manual location (no geoclue dependency); adjust the coordinates
# to taste. Warmer at night, neutral by day.
services.gammastep = {
enable = true;
provider = "manual";
latitude = 51.5;
longitude = -0.13; # London-ish; set to your actual location
temperature = {
day = 6500;
night = 3700;
};
};
# fuzzel: the dmenu picker used by clipman, themed Catppuccin Mocha to match # fuzzel: the dmenu picker used by clipman, themed Catppuccin Mocha to match
# (fuzzel colours are RRGGBBAA -- 8 hex digits). # (fuzzel colours are RRGGBBAA -- 8 hex digits).
programs.fuzzel = { programs.fuzzel = {
+8
View File
@@ -39,6 +39,14 @@
pkgs.wget pkgs.wget
pkgs.claude-code pkgs.claude-code
pkgs.google-cloud-sdk pkgs.google-cloud-sdk
# Day-to-day Kubernetes / Helm / Terraform accelerators for this box.
pkgs.k9s # cluster TUI
pkgs.kubectx # kubectx + kubens (context/namespace switch)
pkgs.stern # multi-pod log tail
pkgs.dyff # semantic YAML/manifest diffs (Helm release drift)
pkgs.tflint # Terraform linter (catches what terraformls won't)
pkgs.terraform-docs # generate Terraform module docs
pkgs.yq-go # jq for YAML
]; ];
services.ssh-agent.enable = true; services.ssh-agent.enable = true;
home.shellAliases = { home.shellAliases = {
+32
View File
@@ -151,6 +151,38 @@
}; };
}; };
# Touch ID authorises sudo (and darwin-rebuild's sudo prompt) instead of a
# typed password. sudo_local keeps the change in /etc/pam.d/sudo_local so it
# survives macOS updates.
security.pam.services.sudo_local.touchIdAuth = true;
# Declarative macOS UI defaults -- the main reason to run nix-darwin beyond
# package management. Applied on activation; all reversible.
system.defaults = {
dock = {
autohide = true;
show-recents = false;
mru-spaces = false; # don't reorder spaces by use
tilesize = 48;
};
finder = {
AppleShowAllExtensions = true;
ShowPathbar = true;
FXPreferredViewStyle = "Nlsv"; # list view
_FXShowPosixPathInTitle = true;
};
NSGlobalDomain = {
AppleInterfaceStyle = "Dark";
ApplePressAndHoldEnabled = false; # key-repeat instead of the accent popup
InitialKeyRepeat = 15;
KeyRepeat = 2;
};
trackpad = {
Clicking = true; # tap to click
TrackpadThreeFingerDrag = true;
};
};
# Used for backwards compatibility; read `darwin-rebuild changelog` before changing. # Used for backwards compatibility; read `darwin-rebuild changelog` before changing.
system.stateVersion = 5; system.stateVersion = 5;
} }
+1 -1
View File
@@ -58,7 +58,7 @@
systemd.services.docker-desktop-proxy.script = lib.mkForce ''${config.wsl.wslConf.automount.root}/wsl/docker-desktop/docker-desktop-user-distro proxy --docker-desktop-root ${config.wsl.wslConf.automount.root}/wsl/docker-desktop "C:\Program Files\Docker\Docker\resources"''; systemd.services.docker-desktop-proxy.script = lib.mkForce ''${config.wsl.wslConf.automount.root}/wsl/docker-desktop/docker-desktop-user-distro proxy --docker-desktop-root ${config.wsl.wslConf.automount.root}/wsl/docker-desktop "C:\Program Files\Docker\Docker\resources"'';
features.swayDesktop.enable = false; features.swayDesktop.enable = false;
programs.nix-ld.enable = true; # 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
# on your system were taken. It's perfectly fine and recommended to leave # on your system were taken. It's perfectly fine and recommended to leave
+1 -1
View File
@@ -12,7 +12,7 @@
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = false; boot.loader.efi.canTouchEfiVariables = false;
networking.hostName = "Emma-Asahi"; networking.hostName = "Lyra-Asahi";
# Audio (PipeWire) and the swaylock PAM stack are inherited from # Audio (PipeWire) and the swaylock PAM stack are inherited from
# workstation.nix. hardware.enableRedistributableFirmware is also set there; # workstation.nix. hardware.enableRedistributableFirmware is also set there;
@@ -22,6 +22,10 @@
networking.hostName = "MacPro31-NixOS"; networking.hostName = "MacPro31-NixOS";
# Elderly host: a compressed RAM swap softens memory pressure (earlyoom in
# workstation.nix is the backstop).
zramSwap.enable = true;
# This host accepts SSH, so open 22 (the firewall itself is enabled in # This host accepts SSH, so open 22 (the firewall itself is enabled in
# workstation.nix with a default-deny policy). # workstation.nix with a default-deny policy).
services.openssh.enable = true; services.openssh.enable = true;
+11 -1
View File
@@ -1,7 +1,7 @@
# ThinkPad T400 (NixOS). Shared laptop options live in ../../modules/laptop.nix; # ThinkPad T400 (NixOS). Shared laptop options live in ../../modules/laptop.nix;
# only host-specific settings are here. Install notes (boot variants, GPU, # only host-specific settings are here. Install notes (boot variants, GPU,
# partitions): see ./README.md. # partitions): see ./README.md.
{ ... }: { config, ... }:
{ {
imports = [ imports = [
@@ -31,6 +31,16 @@
# the radeon firmware needed by the discrete GPU below. # the radeon firmware needed by the discrete GPU below.
hardware.cpu.intel.updateMicrocode = true; hardware.cpu.intel.updateMicrocode = true;
# Battery longevity: cap charging to 75-80%. tlp itself comes from the
# nixos-hardware lenovo-thinkpad profile; tp_smapi supplies the threshold
# sysfs on this 2008-era ThinkPad (kernel-native natacpi is too new for it).
boot.kernelModules = [ "tp_smapi" ];
boot.extraModulePackages = [ config.boot.kernelPackages.tp_smapi ];
services.tlp.settings = {
START_CHARGE_THRESH_BAT0 = 75;
STOP_CHARGE_THRESH_BAT0 = 80;
};
# This T400 has the optional discrete GPU fitted: an ATI Mobility Radeon HD # This T400 has the optional discrete GPU fitted: an ATI Mobility Radeon HD
# 3470 (RV620), driven by the open `radeon` KMS driver. Load it in the initrd # 3470 (RV620), driven by the open `radeon` KMS driver. Load it in the initrd
# for early modesetting (clean Sway/Wayland start); firmware comes from # for early modesetting (clean Sway/Wayland start); firmware comes from
+39 -5
View File
@@ -13,6 +13,18 @@
nix.settings.auto-optimise-store = true; nix.settings.auto-optimise-store = true;
nix.settings.download-buffer-size = 134217728; # 128 MiB nix.settings.download-buffer-size = 134217728; # 128 MiB
# Extra binary cache for the nix-community toolchain (home-manager, nixvim,
# treefmt, ...). Merges with any host-specific caches (e.g. the Asahi cache on
# the MBP) rather than replacing them.
nix.settings.substituters = [ "https://nix-community.cachix.org" ];
nix.settings.trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
# Run dynamically-linked foreign binaries (VS Code remote server, prebuilt
# toolchains, language-server downloads) on every NixOS host, not just WSL.
programs.nix-ld.enable = true;
# Minimal system-level CLI available before the home-manager profile loads # Minimal system-level CLI available before the home-manager profile loads
# (e.g. early boot / rescue). User-level tooling lives in home-manager. # (e.g. early boot / rescue). User-level tooling lives in home-manager.
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
@@ -20,9 +32,31 @@
fastfetch fastfetch
]; ];
# Terminal font with powerline/Nerd glyphs. Installed on every host because # Fonts on every host. The Nerd Font carries the powerline/Nerd glyphs the
# the tmux statusline (which uses these glyphs) runs everywhere, not just on # tmux statusline uses (foot names it explicitly in home/sway.nix); Noto sans +
# the Sway/graphical hosts. foot names it explicitly (home/sway.nix); the Mac # colour emoji prevent tofu in terminals/TUIs/Firefox -- important on the WSL
# installs it via the Darwin config. # box, which does not pull the graphical hosts' default Noto stack. The Mac
fonts.packages = [ pkgs.nerd-fonts.jetbrains-mono ]; # installs the Nerd Font via the Darwin config.
fonts.packages = with pkgs; [
nerd-fonts.jetbrains-mono
noto-fonts
noto-fonts-color-emoji
];
# Map the generic fontconfig families so anything asking for "monospace" gets
# the Nerd Font (with emoji fallback), not DejaVu.
fonts.fontconfig.defaultFonts = {
monospace = [
"JetBrainsMono Nerd Font"
"Noto Color Emoji"
];
sansSerif = [
"Noto Sans"
"Noto Color Emoji"
];
serif = [
"Noto Serif"
"Noto Color Emoji"
];
emoji = [ "Noto Color Emoji" ];
};
} }
+16
View File
@@ -12,4 +12,20 @@
enable = true; enable = true;
settings.General.EnableNetworkConfiguration = true; settings.General.EnableNetworkConfiguration = true;
}; };
# Lid behaviour: suspend on battery, lock on external power (swayidle's
# before-sleep hook locks before the suspend completes either way).
services.logind.settings.Login = {
HandleLidSwitch = "suspend";
HandleLidSwitchExternalPower = "lock";
};
# Bluetooth. The Asahi MBP loads Apple's BT firmware (see its host config) and
# the T400 has an optional BT module; enable bluez on both, with blueman as the
# GUI/tray manager for the Sway session.
hardware.bluetooth = {
enable = true;
powerOnBoot = true;
};
services.blueman.enable = true;
} }
+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"
];
}
+13 -1
View File
@@ -5,12 +5,16 @@
# The bootloader is NOT set here -- it is firmware-specific, not form-factor: # The bootloader is NOT set here -- it is firmware-specific, not form-factor:
# UEFI hosts (MBP, Mac Pro 3,1) use systemd-boot, the BIOS-only T400 uses GRUB. # UEFI hosts (MBP, Mac Pro 3,1) use systemd-boot, the BIOS-only T400 uses GRUB.
# Each machine config declares its own. # Each machine config declares its own.
{ ... }: { lib, pkgs, ... }:
{ {
features.swayDesktop.enable = true; features.swayDesktop.enable = true;
console.keyMap = "dvorak"; console.keyMap = "dvorak";
# Intel thermal management. x86 only -- the Asahi MBP governs its own SoC
# thermals, and thermald is an Intel-platform daemon.
services.thermald.enable = lib.mkIf pkgs.stdenv.hostPlatform.isx86_64 true;
# Default-deny inbound. Hosts that run a listening service open their own # Default-deny inbound. Hosts that run a listening service open their own
# ports next to where the service is enabled (e.g. sshd -> 22 on X1). # ports next to where the service is enabled (e.g. sshd -> 22 on X1).
networking.firewall.enable = true; networking.firewall.enable = true;
@@ -20,6 +24,14 @@
services.fstrim.enable = true; services.fstrim.enable = true;
boot.tmp.cleanOnBoot = true; boot.tmp.cleanOnBoot = true;
# Userspace OOM killer: act on memory pressure early instead of letting the
# kernel OOM-thrash. Matters on the 4 GiB T400 and the elderly Mac Pro.
services.earlyoom.enable = true;
# Firmware updates via LVFS. No-op on the Asahi MBP (Apple-managed firmware),
# useful for UEFI/SSD updates on the x86 hosts.
services.fwupd.enable = true;
# Audio. PipeWire with the PulseAudio shim covers every graphical host; no # Audio. PipeWire with the PulseAudio shim covers every graphical host; no
# per-machine audio config is needed. # per-machine audio config is needed.
services.pipewire = { services.pipewire = {