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 (anarlog, 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.

The same module defines a speech bus: a virtual capture node carrying only applications whose output is human speech. anarlog records the mic on one channel and system audio on the other, and "system audio" would otherwise mean the default sink's monitor — post-mix, so whatever music is playing lands in every meeting transcript.

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, and anarlog's summaries); 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
pr-babysit-loop.nix weekdays 05:30 Reviews open PRs across the comcreate-io org, one marker-gated comment per PR, Telegram digest
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

home/whisperlivekit.nix runs a local Deepgram-compatible STT server so anarlog can transcribe meetings without shipping audio to a cloud vendor. It is a uv-tool install, not a flake package — upgrade it with uv tool upgrade whisperlivekit.

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