9.2 KiB
9.2 KiB
dotfiles-nixos
One flake that builds every machine the user owns.
The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overlays) lives in ~/Documents/ai-artifacts/projects/dotfiles/003-dotfiles-context.md.
Conventions
- Comments posted to Gitea (pull requests, issues, reviews) go out under the operator's account, so sign every one to make clear the author is the agent, not the operator.
End the comment with a
— Claudesign-off. (A dedicated bot account may replace this later. Until then, the sign-off is the only marker.) - Commit messages follow Conventional Commits, specified in
docs/conventional-commits.md. Scope is the module or host the change belongs to (fish,nvim,neogaia), omitted for repo-wide changes. Keep messages free of Gitea-specific references: this repository is mirrored to GitHub, where issue and pull-request numbers resolve to unrelated things. - When a graphical application is added, give it a
window-rewriteicon mapping inmodules/desktop/waybar.nix. Without one its windows fall back to the generic default glyph on the workspace indicator instead of showing a recognisable per-application icon. Match on the window class, whichhyprctl clients -j | jq -r '.[].class' | sort -ulists for the running session.
Gotchas
- ADR bodies are immutable records of decisions as they were made, while frontmatter is mutable. When a decision changes or its premise proves wrong, preserve the original body, update its status, and add a new ADR that supersedes it. Filename migrations preserve references in immutable bodies through frontmatter aliases rather than rewriting those bodies.
- This repo pins no Nix formatter, and its committed
.nixfiles are not clean under currentnixfmt-rfc-style. Runningnixfmtacross a file reflows untouched code (for examplelib.nix'sderiveMaclist and multi-line assertion messages) and injects churn unrelated to the change. Format only the lines being written or changed, matching the surrounding style by hand. - This repo is developed on
neogaia, which now runs the NixOS it builds. Flakes and the chaotic substituter come from this flake's ownnix.settings, so noNIX_CONFIGexport or per-command--extra-experimental-featuresis needed, and building a toplevel withboot.kernelPackages = linuxPackages_cachyosfetches the kernel fromnyx-cacherather than compiling it. Both were true only while the machine still ran CachyOS against a distro Nix daemon. - Git identity is declared in the flake by
modules/git.nix, which writesalexion <contact@alexion.dev>— the identity all history uses — on any host enablingmodules.git. Every new host has to enable it, so that a host reads as a full checklist of what it carries. It is deployed onneogaiaand verified: a commit in a repository outside this checkout is authoredalexion <contact@alexion.dev>with no override. Verify it that way rather than from this checkout, whose.git/configcarries the same identity and would mask a broken module.~/.gitconfig(a second global file that outranks the flake-managed~/.config/git/config) currently holds only ateacredential helper and nouser.*, so it does not shadow the identity, but it is undeclared and will not survive a reimage. - The primary build/verify seam for any Host is
nix flake check, which buildschecks.x86_64-linux.<host>(the system toplevel). Cheap targeted checks usenix eval .#nixosConfigurations.<host>.config.... - chaotic-nyx must not follow our
nixpkgs, and its packages are built against chaotic's own pinned nixpkgs (its overlay defaults toonTopOf = "flake-nixpkgs", the cache-friendly path). That is what lets thenyx-cache.chaotic.cxbinary cache hit instead of compiling the CachyOS kernel from source. The tradeoff is that chaotic packages do not see ourunstable/stableoverlays. - The remote is self-hosted Gitea (
git.alexion.dev), and the forge CLI isgitea-axirather thantea.gitea-axiresolves the repository from theoriginremote and discovers credentials from atealogin whose host matches the remote, so both are implicit inside a checkout. It is installed onneogaiabymodules.agents.tools.gitea-axi, and verified:gitea-axirun from this checkout renders thealexion/dotfilesdashboard authenticated, so the claude-codeSessionStarthook that runs it now resolves to a real binary rather than a missing one. The package wraps the binary sogitandteaare reachable without being onPATH, while still preferring the operator's own where present. Credentials:~/.config/tea/config.ymlholds a token-bearing login namedalexion, whichgitea-axiuses and which also opens pull requests directly withnix run nixpkgs#tea -- pr create --login alexion --repo alexion/dotfiles --base main --head <branch> .... The--repoflag is required on that path, sincetearesolvesoriginonly for a login whose SSH host matches. The same token reads PR discussion, whichteaitself does poorly:tea pr <n> --commentsprints only the body, and-f commentsreturns no comments field at all. Use the API instead, taking the token from.logins[] | select(.name=="alexion") | .token. Review comments are not at/issues/<n>/comments— that endpoint holds only top-level discussion and is usually empty. Inline comments need two calls:/pulls/<n>/reviewsfor the review ids, then/pulls/<n>/reviews/<id>/commentsfor the bodies, whosepathanddiff_hunkfields say what each one is attached to. A review row with an emptybodyis the normal shape when the operator left only inline comments. ~/.claude/skillsand~/.pi/agent/skillsare home-manager-generated (recursive = true), so editing a skill in place fails and a new file created there silently escapes the repo. Shared global skills come from theskillsflake throughmodules/agents/skills.nix, applied by a rebuild. Claude-specific legacy skills, when kept, live undermodules/agents/claude-code/skills/<name>/.- Pi skill discovery honors
.gitignore,.ignore, and.fdignoreinside scanned skill directories. A generated.agents/skills/.gitignoreentry that ignores a symlinked skill also prevents Pi from loading that skill, even when.agents/skills/<name>/SKILL.mdexists and the symlink target is valid. - nixpkgs
vimPlugins.nord-nvimisshaunsingh/nord.nvim(norequire("nord").setup()). The config wantsgbprod/nord.nvim, which is packaged asvimPlugins.gbprod-nord. nixos-generate-config --show-hardware-configneeds root on this machine even just to print: unprivileged it dies atFailed to retrieve subvolume info for /, because the root filesystem is btrfs.- This repo's claude-code module sets sudo's credential cache to per-user (
timestamp_type=global, 60-minute window), so an authentication made in one real terminal counts for the agent's commands. APreToolUsehook refuses privileged commands while the cache is cold, so a cold cache announces itself instead of stalling. A privileged-command failure without that message is the sandbox, not the cache. - Host GPUs:
neogaiais Intel,zeus(the desktop) is AMD, andraichu(a headless server) is the only Nvidia machine. The corrected fact also lives in artifact006-dotfiles-hyprland-compositor-adr.md. - This repo's
programs.firefoxsearch(withforce = true) writessearch.json.mozlz4. Omission alone does not prune a built-in engine, since Firefox reconciles its app-provided engines back in, so remove one by listing it with<engine>.metaData.hidden = true. Engines are referenced by their current id, so the default isdefault = "ddg", not"DuckDuckGo". Decode the built file withmozlz4a -d <search.json.mozlz4>to check the result. - Any non-empty Home Manager Firefox
profiles.<name>.extensions.settings.<id>.settingscauses Home Manager to setextensions.webextensions.ExtensionStorageIDB.enabled = falseglobally for that profile. This repo's Stylix FirefoxcolorThemesettings trigger it, so every extension in the profile uses the legacy extension-storage backend regardless of how it is installed. home.sessionVariablesdo not reach the Hyprland session, since UWSM does not sourcehm-session-vars.sh. The cursor is therefore set through Hyprland's ownenv = KEY,VALUEinmodules/desktop/hyprland/hyprland.nix, sourced fromconfig.stylix.cursor. Bibata ships XCursor format only (nohyprcursor/dir), rendered through Hyprland's XCursor fallback, soXCURSOR_*andHYPRCURSOR_*naming the same theme are both safe.neogaia, the repo's only host, is a wifi laptop with a btrfs root and no ZFS pools, so it cannot honestly carrymodules.network,modules.zfs, or a networked/pool-mounted guest. Enabling networkd takes over its DNS, its CachyOSzfs-kernelbuild is marked broken, and it has no bridge or pool to attach to. Verify these against it ad hoc throughnixosConfigurations.neogaia.extendModules(forcing a ZFS-capableboot.kernelPackagesfor the zfs case) plusnix evalof the derived values, never by committing the enablement. A committed guest therefore leavesvlan,mounts, andsecretsunset, and the standing enablement waits for the first wired server host with real storage.