Skip to content

Making it yours

nebelhaus ships a complete opinion — system and shell, already wired. So “customizing” isn’t reassembling anything; it’s turning a handful of dials. This page is the map of those dials and the thinking behind the loudest defaults.

There are two halves to your setup, and they stay cleanly apart:

  • The rice (github:nebelhaus/nebelhaus) — the generic, no-identity system. You never edit this to use it. It updates upstream; you pull it when you like.
  • Your config (~/.config/nix) — a ~18-line flake that imports the rice, plus one host file where you live: hosts/<hostname>/default.nix.

That host file is where every tweak on this page goes. Open it instantly:

Terminal window
haus edit

It’s a plain nix-darwin module — whatever you write merges with what the rice already declares. Change something, then:

Terminal window
haus rebuild # builds first; a broken config never activates

The single biggest opinion in nebelhaus is how windows are arranged, so it’s worth saying out loud: each app you live in gets its own workspace, and one key takes you there.

Instead of alt-tabbing through a soup of windows, you build muscle memory: the terminal is always T, the browser is always B, Slack is always S. The app is launched if it isn’t running, focused if it is, and it lands on its workspace every time. Your hands stop negotiating with the window manager.

That whole scheme comes from one keyed mapnebelhaus.apps, the roster — so the launcher key, the workspace, the bar pill, and the auto-tiling rule can never drift out of sync. Keyed entries also compose cleanly across your host file and pounce-generated package modules. A few things worth internalizing:

  • Letters are apps, digits are scratch. Workspaces 14 are generic scratch space; letters (T, B, S…) belong to specific apps.
  • Focus vs. throw. Tap then a letter/digit to go there. Same leader, but with that letter/digit, to throw the current window there instead.
  • Launcher-only apps exist. Give a roster entry workspace = null and its key just launches it in place — no dedicated workspace, no pill. Handy for a password manager or a calculator.

Adding your own is a one-liner — see Adding apps & tools for the recipe and Window management for the full binding set.

Every knob below is a nebelhaus.* option (or a plain nix-darwin one) you set in your host file. This is the tour; the exhaustive list with types and defaults is the options reference.

Or don’t look any of it up. The rice installs a skill that teaches Claude Code this whole surface — generated from the revision you’re pinned to — so you can ask in plain words instead: Changing your Mac with an agent.

nebelhaus.git.name = "Ada Lovelace";
nebelhaus.git.email = "ada@example.com";
nebelhaus.git.signingKey = "6F7BD6F43A7C1420"; # "" to disable commit signing
nebelhaus.theme.accent = "sapphire"; # any of 14 Catppuccin accent names
nebelhaus.theme.wallpaper = "orbits"; # none | orbits | constellation | flow | bold

accent recolours lazygit, fzf, yazi, and the browser accent; bold generates a wallpaper from your accent. Full palette on Theming & accents.

nebelhaus.hearth.editor = "nvim"; # the ONE editor: $EDITOR + every "open in editor" action
nebelhaus.hearth.hijackFileAssociations = false; # make that editor the default file opener

More on each in The shell.

nebelhaus.apps.slack = { /* one keyed roster entry — see Adding apps */ };
nebelhaus.prowl.enable = true; # false: no tiling, no Caps-Lock remap
nebelhaus.pounce.enable = true; # false: no ⌘Space palette
nebelhaus.pounce.signingIdentity = ""; # keeps Pounce's Accessibility grant across rebuilds
nebelhaus.sill.enable = true; # false: keep the native macOS menu bar
nebelhaus.sill.plugins = [ "elgato" ]; # opt-in extras: agents | elgato | harvest

The bar’s optional plugins are personal — an Elgato Key Light toggle, a Harvest timer, an agents pill — off unless you ask. See The bar.

The built-in tour teaches nebelhaus’s four core moves. A shared rice can replace that lap with its own instructions while staying data-only:

nebelhaus.tour.steps = [
{
hint = "Press ⌘Space, type tour, then hit ↵";
detect = "palette";
}
{
hint = "Move to another workspace";
detect = "workspace";
}
];

Steps run in order and advance from signals the rice already emits — launch, workspace, navigate, resize, or palette — rather than by logging keys. The first four need prowl; palette needs Pounce. A left-click skips the current step, and the module warns during a rebuild if a step depends on a room you disabled. Leave tour.steps at its default null to keep the built-in tour.

nebelhaus.homebrew.cleanup = "none"; # none | uninstall | zap — how rebuild treats undeclared apps
nebelhaus.homebrew.autoUpdate = false; # brew update before each rebuild
nebelhaus.homebrew.upgrade = false; # upgrade outdated packages each rebuild
homebrew.casks = [ "discord" ]; # plain installs, no launcher key

cleanup defaults to "none" so nothing you installed by hand is ever removed. Set it to "zap" if you want a fully declarative machine where undeclared casks get uninstalled on rebuild.

Three rooms are opt-out; the rest are load-bearing:

RoomOptionOff means
prowl (tiling)nebelhaus.prowl.enable = falseNo AeroSpace, no leader key
sill (bar)nebelhaus.sill.enable = falseThe native menu bar stays
pounce (launcher)nebelhaus.pounce.enable = false⌘Space stays Spotlight

If what you actually want is the tiler without its keyboard claims, don’t turn the room off — set nebelhaus.keys.leader = "none" and nebelhaus.keys.windowNav = "none" instead. prowl keeps tiling and Caps-Lock stays Caps-Lock. See rebinding.

den (system + Homebrew), hearth (shell), and collar (Touch ID sudo) are always on — they’re the foundation the rest sits on. Turn any of the three optional rooms off and the others keep working; the rice degrades gracefully.

A preset is a data-only rice — a file whose only top-level key is nebelhaus. No pkgs, no lib, no activation scripts, so it can’t reach outside the option surface and you can read one in a minute and know the worst it can do. Import one alongside your host file:

darwinConfigurations.myhost = nebelhaus.mkNebelhaus {
username = "ada";
hostname = "myhost";
host = ./hosts/myhost;
extraModules = [ nebelhaus.presets.everyday ];
};

Your host file is imported after, so anything you set there still wins.

PresetFor
fullthe whole rice: bar, tiling, palette, developer pack. The default.
minimaljust the themed shell — no bar, no tiling, no palette. Still a developer machine.
everydaythe inverse: a Mac for someone who doesn’t write code. No developer pack, no Caps-Lock remap.
large-printbigger and sharper — the rice UI and whole display, plus the high-contrast palette and macOS’s own contrast lift.

The first three are whole rices; pick one. large-print is a layer — it describes seeing rather than the person, so it stacks:

extraModules = [ nebelhaus.presets.everyday nebelhaus.presets.large-print ];

It uses two complementary size controls. ui.scale = 1.4 moves the terminal font, the Dock icons and the tiling gaps together — pin any single one afterwards and yours wins. displays.main.uiScale = "larger-text" moves the current main display one step toward larger text, which makes everything bigger, including third-party apps the rice knows nothing about. The trade-off is less desktop space.

For per-display control, add a UUID-specific setting in your host file; it wins over the preset’s broad main selector. hausdisp list prints the persistent UUIDs of the displays currently attached.

The remaining size limits are the menu bar and the Pounce window: both use geometry tuned to their layouts, so a multiplier would break alignment rather than enlarge them. The preset also doesn’t change an app’s own font preference or macOS’s separate Dynamic Type text-size setting; display scaling is the system-wide lever that works.

Want to make one for other people? Sharing a rice covers the data-only boundary, checkRice, composition testing, and the smallest useful publishing shape.

The rice sets Dock, Finder, trackpad, and keyboard defaults — but every one of them is a soft default you can beat by just setting the same key with a plain value in your host file:

# Rice hides the Dock and puts it on the bottom; put it on the left, always shown:
system.defaults.dock.autohide = false;
system.defaults.dock.orientation = "left";

Anything under system.defaults is fair game. Your plain value wins over the rice’s default.

Anything sensitive — API keys, tokens, your app list if you’d rather it stay private — belongs in your host file (which is your repo), and secrets themselves belong in files the Nix store never sees:

home-manager.users.${username} = {
# Read secrets at shell startup, not from the (world-readable) Nix store:
programs.zsh.initContent = lib.mkAfter ''
export SOME_API_KEY="$(cat ~/.secrets/some-key 2>/dev/null)"
'';
home.packages = with pkgs; [ /* your private tools */ ];
};

Keep ~/.secrets/* out of git. Your host repo can be private; the rice never needs to know who you are.