Follow

Software setup

My machines, defined by dotfiles — the shell, the terminal and the tools I actually use

My working environment is versioned, like everything else. It is a family of repositories rather than one giant dotfiles repo: a root package that orchestrates, and one repository per concern.

The base is Arch Linux on Hyprland; the shell is zsh, with tmux, git, ssh, node and bash configuration each living in their own repository — installed onto a machine rather than curated by hand.


FactDetail
BaseArch Linux + Hyprland
Shell / terminalzsh, tmux, starship-era prompt tooling
Repositories8: dotfiles, -arch, -zsh, -tmux, -git, -ssh, -node, -bash
StartedMay 2019, still updated

What's in it

One repository per concern

A root package orchestrates seven focused repositories — you change only what you mean to change.

Reproducible machines

A new machine is a setup run, not an afternoon of remembering.

In detail

What is actually inside it

Order is explicit

order = 3 for tmux, = 30 for tpm, = 31 for tmux-resurrect, = 32 for tmux-continuum: the plugin chain checks out bottom-up so a checkout never depends on something not yet present.

Hooks, not instructions

pre_checkout = sudo apt install -y tmux sits next to the repository it is needed for — the setup knowledge lives in the manifest, not in a README nobody reads.

Frozen revisions

mrfreeze pins the exact revisions across all repositories, so "the setup that worked" is a commit, not a date.

DetailValue
Orchestratormyrepos (`mr`) + `mrfreeze`
Structure8 repositories, one concern each
StartedMay 2019, still updated

Technical notes

The decisions behind it

Why not one dotfiles repository?
Because the pieces change at different rates and belong to different tools. Split repositories mean a tmux tweak is a one-line diff in one place, while the root mr config keeps the composition in a single reviewable file.
Why homesick-era paths?
.homesick/repos/ is where this started; keeping that layout meant the switch to mr did not move anything on disk, and every machine kept working during the migration.

The manifest

What a fresh machine actually runs

32
Checkouts in the manifest
~/.mrconfig holds 32 repository entries: the ones you own, plus everything they configure.
19
Third-party checkouts
Fetched in the same run: oh-my-zsh, spaceship-prompt, three zsh plugins, eight vim plugins, the .tmux fork, four tmux plugins and polybar-themes.
13
`dotfiles-*` repositories
From dotfiles-bash at order 110 to dotfiles-i3 at order 200. Every one of them ends in homeshick link --force.
9
Are public on GitHub
The other four are private — dotfiles-history, and dotfiles-credentials, -notes and -ruhmesmeile, still fetched over SSH from a self-hosted Bitbucket Server on port 7999.

order is the only scheduling knob mr has: the default is 10, lower runs earlier, and the sequence is a total sort on (order, path). Here it is used as a map of the machine.

3, 6 and 7 clone the three checkouts everything else edits — the .tmux fork, oh-my-zsh and amix/vimrc. 30–33 the four tmux plugins, 50–53 the zsh plugins, 70–77 the eight vim plugins. 110–130 are the configuration repositories; every one of those hooks ends in homeshick link --force, which is what turns ~/.bashrc, ~/.gitconfig, ~/.ssh/config and ~/.zshrc into symlinks into ~/.homesick/repos/. 140, 150 and 160 are tmux, vim and zsh, and 200 is i3 — last, because the desktop is the only piece that has to exist for the rest to become visible.

The 16 post_checkout hooks do the machine-specific work; 13 of them call homeshick link and the other three symlink the tmux fork, oh-my-zsh and amix/vimrc into place. Nine pre_checkout hooks sit directly next to the repository they belong to: seven apt install lines (tmux, zsh, vim fonts-hack-ttf, tig, rbenv, dnsmasq, and one 8-package desktop line for i3), one curl that installs nvm at a pinned v0.35.3 — and one that installs nothing at all: dotfiles-tmux begins with rm -f $HOME/.tmux/.tmux.conf $HOME/.config/systemd/user/tmux.service, a deletion so that its own links have somewhere to land.

~/.mrconfig opens with include = cat ${HOME}/mrconfig. include is the one directive mr accepts outside a section, and it pulls in the 84-line freeze library that ships in the same repository as home/mrconfig. That library defines a [DEFAULT] lib block: it derives the top directory from where the config lives, points MR_FREEZE at ~/mrfreeze, and registers git_freeze and git_unfreeze. Before a revision is recorded, the repository path is rewritten relative to $HOME — which is why mrfreeze reads .homesick/repos/dotfiles-zsh ce1b6f2… and not an absolute path, and why the file survives being moved to another home directory.

The pinned set spans 2021 to 2024: dotfiles-git at 2021-10-02, dotfiles-node at 2022-08-16, dotfiles-ssh at 2023-07-28, the rest at 2024-03-16 — the last commit the root repository ever received. mr checkout gives you the current tips, mr unfreeze gives you those hashes, and nothing rewrites the file on its own.

Nothing is copied into $HOME. homeshick link creates symlinks, so ~/.zshrc, ~/.tmux.conf and ~/.gitconfig sit one hop away from ~/.homesick/repos/<repo>/home/…. The links inside the repositories are the interesting part, because they are written relative to that layout: dotfiles-tmux ships home/.tmux/plugins/tpm → ../../../../tpm, and four levels up from ~/.homesick/repos/dotfiles-tmux/home/.tmux/plugins is exactly ~/.homesick/repos — where order = 30 puts the tpm checkout.
dotfiles-zsh does the same one directory deeper (../../../../../zsh-autosuggestions for the oh-my-zsh plugin directories), and dotfiles-arch leaves its own repository entirely: .Xresources, .Xmodmap and .xbindkeysrc point into dotfiles-x, while .config/picom.conf points into dotfiles-i3. The path prefix ~/.homesick/repos/ is written into the repositories themselves, which is the price of splitting them: check one out anywhere else, or nest it one level deeper, and those links dangle.
homeshick links the leaves instead of replacing a directory, which is what makes the overlay work. link --force dotfiles-zsh walks into the existing oh-my-zsh checkout and drops custom/plugins/zsh-autosuggestions and custom/themes/spaceship.zsh-theme in there — and since oh-my-zsh's own .gitignore lists custom/, that checkout stays clean.
dotfiles-tmux is grafted into the .tmux fork the same way, and there the outcome is not clean: the fork's tracked .tmux.conf is replaced by a symlink, and plugins/ and resurrect/ appear as untracked directories in a third-party working tree.
tpm is cloned, linked and pinned in mrfreeze — but never invoked. The @tpm_plugins block in .tmux.conf.local is commented out, with a note that the plugin manager is not immediately compatible with the gpakosz base config, so the plugin set is four sibling checkouts wired in by symlink instead of an install step.
InSymlinkResolves under `~/.homesick/repos/`
dotfiles-tmux`.tmux/plugins/tpm``tpm` (order 30)
dotfiles-tmux`.tmux/plugins/tmux-resurrect``tmux-resurrect` (31)
dotfiles-tmux`.tmux.conf`the vendored 58 KB `dotfiles-tmux/home/.tmux/.tmux.conf`
dotfiles-zsh`.oh-my-zsh/custom/plugins/zsh-syntax-highlighting``zsh-syntax-highlighting` (53)
dotfiles-zsh`.oh-my-zsh/custom/themes/spaceship.zsh-theme``spaceship-prompt` (50)
dotfiles-arch`.Xresources`, `.Xmodmap`, `.xbindkeysrc``dotfiles-x` — not in the manifest
dotfiles-arch`.config/picom.conf``dotfiles-i3/home/.config/picom.conf` (200)

The terminal layer

The three most recent decisions

Three seconds of history

The 2026-09-29 commits reach into the running terminal. @continuum-save-interval drops from 120 to 3 seconds (with @continuum-restore and @continuum-boot on), so the resurrect snapshot is at most three seconds stale; default-terminal becomes tmux-256color, annotated in the file as # test proposed by omp. The local config also lifts the vendored history-limit 5000 to 10000 — the tmux server survives everything except a deliberate kill-server.

Marking the LLM panes

Panes running omp get a darker background so they are identifiable at a glance. .zshrc wraps the command itself — omp() calls ~/.config/tmux/omp-pane-bg.sh --force "$TMUX_PANE", runs command omp, then resets — and .tmux.conf.local adds set-hook -g pane-focus-in / pane-focus-out handlers that run the same script with run-shell -b, so panes that never went through the wrapper (a restored session, for instance) are coloured too. The colour is OMP_PANE_BG, defaulting to #04141a.

The server is a user unit

home/.config/systemd/user/tmux.service is Type=forking, claims DISPLAY=:0 and XDG_SESSION_TYPE=wayland, and runs ExecStart=/home/julrich/tmux_attach.sh. The manifest's post_checkout links it into the user manager, enables and starts it; the X session files in dotfiles-x start the same unit before launching urxvtd. It keeps KillMode=none, RemainAfterExit=yes and WantedBy=multiplexer.target, and both ExecStop lines — the resurrect save and tmux kill-server — are commented out: the sessions are not meant to disappear when the unit stops.

Where the split leaks

True on the machine, absent from every repository

Is the frozen revision set still current?
No. dotfiles-git is pinned to a 2021-10-02 commit, dotfiles-node to 2022-08-16, dotfiles-ssh to 2023-07-28, and the whole file was last rewritten on 2024-03-16. Every dotfiles-* repository was pushed again on 2026-09-29. mr checkout installs the tips, mr unfreeze checks out those hashes, and nothing runs mr freeze on a schedule — so the frozen state is exactly as old as the last time someone ran the command by hand.
Which repositories are not in the manifest?
dotfiles-arch and dotfiles-x. The first is the current Arch/Hyprland base and the repository that changed most recently: three commits over six years, the newest from 2026-09-29 replacing a single xrandr line to add --rate 30 to the second DisplayPort output in .screenlayout/oberkassel.sh. dotfiles-x holds the X resources that dotfiles-arch symlinks to. Neither appears in ~/.mrconfig or mrfreeze, and neither does dotfiles-osx (2017, macOS). Running mr checkout on a new machine therefore installs everything except the operating-system layer of the machine most likely to be rebuilt.
Does the bootstrap script still hold up?
It is a Debian script first and a dotfiles script second. util/bootstrap.sh runs sudo apt-get update, installs git mr jq, gates Docker CE behind an ID=neon check on /etc/os-release, sets the login shell with chsh --shell /bin/bash, clones homeshick over git:// — the unencrypted protocol GitHub permanently disabled on 2022-03-15 — then clones the dotfiles repository itself over SSH, before any key is guaranteed to exist on the machine. The package list beside it, util/package-list, is 231 Arch package names and is read by nothing in the repository; the manifest's install hooks are all apt except the one that curls nvm. The Arch side of the setup is the part that is still maintained by hand.
What do the shells reference that is not here?
Four absolute paths under /home/julrich/, written into three published repositories: tmux_attach.sh (the systemd ExecStart), .config/tmux/omp-pane-bg.sh (called from both .zshrc and .tmux.conf.local), .config/hypr/import_env (used by a refresh() left in .zshrc after its preexec caller was commented out with a TODO), and .zsh_tmux_env (behind the lte alias). None of them exists in a published repository, and none of the published ones is the Hyprland configuration — dotfiles-arch still carries the lightdm/picom/X11 layer it was created for. The files were split; the scripts they call were not.