Skip to content

Repository files navigation

dotfiles

NixOS flake for all my machines: Hyprland (two shell variants — waybar and Caelestia/Quickshell) plus Niri + Noctalia as a fallback, Ghostty, Neovim, tmux, and a dev-first setup with local LLMs. Built on flake-parts; home-manager runs as a NixOS module.

Machines

Host Hardware User Notes
atlas Surface Laptop Studio, i7, 32GB RAM carter nixos-hardware Surface module, CPU-only Ollama
kronos Ryzen 9 9950X, RTX 5070 12GB, 64GB RAM cjm LUKS + btrfs, GPU Ollama + llama.cpp heavy mode

Usage

git clone https://github.com/cartermccann/dotfiles ~/dotfiles

# Validate without sudo
nh os build ~/dotfiles

# Apply (or use the `nrs` alias)
sudo nixos-rebuild switch --flake ~/dotfiles#kronos

update (alias) rebuilds with updated flake inputs.

Layout

flake.nix          # inputs; outputs delegated to parts/
parts/             # flake-parts modules: hosts (mkHost), templates
hosts/             # per-machine configuration + hardware config
modules/           # NixOS system modules (desktop, nvidia, audio, ollama, oom-protection, ...)
home/              # home-manager modules (shell, tools, neovim, tmux, niri, hyprland, ...)
lib/               # overlays, the Ouranos palette (night/day), llm-models.nix
pkgs/              # custom package definitions (codex, qmd, playwright-cli, ...)
templates/         # dev-shell templates for `nix flake init -t ~/dotfiles#<lang>`
skills/            # Claude skills owned by this repo; installed by the module that uses them
config/            # generated blobs kept as real files: nvim, hyprland CSS, audio presets, hermes
scripts/           # shell scripts referenced by modules
docs/              # project specifications, architecture, and delivery plans
wallpaper/

config/nvim/ is symlinked out of the store rather than copied into it, so it stays live-editable. The other config/ subdirectories are inputs that are too large to keep inline in Nix strings.

Project documentation

Adding a machine

  1. Create hosts/<hostname>/ with a configuration.nix (import the modules you need) and the machine's hardware-configuration.nix.
  2. Register it in parts/hosts.nix:
    flake.nixosConfigurations.myhost = mkHost "myhost" { user = "myuser"; };
  3. sudo nixos-rebuild switch --flake ~/dotfiles#myhost

Desktop sessions

  • Hyprland — primary. Hyprland 0.55 Lua config in home/hyprland/ + lib/palette.nix. Two tiles from one compositor config: Hyprland (waybar + swaync + fuzzel + swayosd + hyprlock — the daily driver) and Hyprland (Caelestia) (Caelestia/Quickshell replaces all of those with one process). Both are generated by mkHyprlandLua in home/hyprland/compositor.nix, which takes a shellKind and diverges at only eight seams — layer rules, launcher/clipboard, notifications, lock/session menu, shell reload, volume/brightness, locked media keys, and autostart.
  • Niri (Noctalia) — kept as a working fallback; config in home/niri-noctalia.nix. The standalone plain-Niri session was retired and its login tile is filtered out in modules/desktop-wayland.nix.

All sessions share ghostty, tmux, the shared Wayland base in modules/desktop-wayland.nix, and the helper scripts in home/niri.nix.

Colours come from lib/palette.nixOuranos, cobalt on near-black (night) or near-white (day), named for the sky Atlas holds up and the father Kronos was born to. Both variants are always defined; active picks which one the session components get. Caelestia does its own theming (it derives a Material scheme) and is deliberately not wired to it.

Audio

The EQ is a PipeWire filter-chain sink declared in modules/audio.nix, not EasyEffects — the presets it was ported from are kept in config/audio/ as the record of the curve. Sinks are ranked by priority.session there, but wpctl set-default, pavucontrol, or hypr-audio-sink write a default.configured.audio.sink entry to ~/.local/state/wireplumber/default-nodes that overrides that ranking permanently. If the default sink is wrong, check that file before touching priorities.

Dictation

Local speech-to-text through sherpa-onnx Parakeet, typed into the focused window with ydotool. Scripts in home/dictation.nix; the ydotoold daemon (root, for /dev/uinput) in modules/dictation.nix.

Binding Mode Model
Super+Alt+L live — types words as you speak Parakeet streaming 1120 ms, int8
Super+Alt+Shift+L batch — types the whole utterance on stop Parakeet TDT 0.6B v2 offline, int8

Both models (~480 MB each) are fetched on first use into ~/.local/share/parakeet-dictation/, or up front with setup-dictation.sh. The live pipeline logs to ~/.local/state/parakeet-dictation/live.log.

Typing runs at 10 ms/character rather than ydotool's 40 ms default, and on its own thread. Both matter: typing and decoding share one real-time budget, and at stock timing the live pipeline loses that race under load and falls permanently behind rather than catching up.

Local LLMs

Ollama runs in a podman container (modules/ollama.nix); models are preloaded per host tier via local.ollamaTier:

Tier Hardware Models
high 12GB VRAM gemma4:12b-it-qat, qwen2.5-coder:3b-base
medium CPU, 32GB RAM llama3.2:3b
low smaller llama3.2:3b

Model tags live in lib/llm-models.nix. gemma4 is the only chat model (the ai alias); qwen2.5-coder is FIM tab-completion for minuet in nvim and is never used interactively.

heavy / heavy-stop (fish functions) swap the GPU between Ollama and a llama.cpp server running Qwen3.6-35B MoE with expert offload (modules/llama-heavy.nix).

Agents and scheduled loops

home/hermes.nix provides the runtime toolchain for the Hermes agent, deliberately not packaged as a derivation: Hermes git-clones into ~/.hermes/, builds its own uv venv, and writes new skills and memory to itself at runtime, so the flake supplies the dependencies and stays out of the way. home/hermes-events.nix installs the event/notification scripts from skills/hermes-ops/.

Maintenance loops run as systemd user timers. Each is a thin Nix wrapper — sandbox, credentials, schedule — around a Claude skill in ~/.claude/skills/ that holds the actual behavior. All of them are proposal-only or draft-PR-only; none merges anything.

Module Schedule What it does
self-improve-loop.nix every other day 07:07 Mines Claude Code transcripts for durable feedback under an anti-slop constitution
codex-self-improve-loop.nix every other day 07:27 Read-only Codex counterpart; writes inert proposal state only
ci-triage-loop.nix 08:00 and 15:00 Diagnoses failed Actions runs; opens draft ci-fix/* PRs only past a reproduce-before/pass-after oracle
docs-gardener-loop.nix Sundays 09:30 Audits project CLAUDE.md files against real repo state, files human-gated proposals

Dev shells

Per-language templates live in templates/ and auto-activate via direnv:

nix flake init -t ~/dotfiles#python   # node, python, go, rust, elixir, zig, java, c, ruby, deno
echo "use flake" > .envrc && direnv allow

Shell aliases and fish functions are defined in home/shell.nix.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages