My personal configuration files.
  • Shell 53.5%
  • Lua 24.5%
  • GLSL 9.7%
  • SCSS 6.7%
  • Python 5.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-03 17:41:46 +01:00
linux fix(linux): wait for the eww daemon before opening windows 2026-07-22 16:34:25 +01:00
macos Update various things 2026-08-03 17:41:46 +01:00
shared Update various things 2026-08-03 17:41:46 +01:00
system/etc fix(system): run the session under a dbus session bus 2026-07-22 16:13:37 +01:00
vps fix(vps): hybrid BIOS/UEFI boot layout 2026-07-22 15:45:37 +01:00
README.md fix: build AUR packages with makepkg directly 2026-07-22 15:56:56 +01:00
setup_artix.sh fix(linux): eww card geometry and label allocations 2026-07-22 16:23:20 +01:00
setup_machine.sh Add Netbird 2026-07-21 02:10:54 +01:00

Dotfiles

My personal dotfiles, managed with GNU Stow. The repo is split into three stow packages so macOS and Linux machines share what they can:

Package Contents
shared/ bash, git, nvim, ghostty, ansible, Claude, portable scripts
macos/ Hammerspoon, LaunchAgents, gnupg (PCSC), brew packages, scripts
linux/ the Arcane Ascension Wayfire rice, Linux shell setup

Files are stowed with per-file symlinks (--no-folding) so app state written next to stowed files never leaks into the repo. README.md, setup_machine.sh, setup_artix.sh and the system/ and vps/ trees live outside the packages and are never stowed.

The shared .bashrc sources ~/.config/bash/os.sh, which the macos and linux packages each provide their own version of — OS-specific shell setup goes in there, not in the shared file.

New machine setup — macOS

  1. Clone the repo to the home folder:

    git clone ssh://git@forge.quinlan.cloud:2222/joshquinlan/dotfiles.git ~/.dotfiles
    
  2. Run ~/.dotfiles/setup_machine.sh. It is idempotent and handles:

    • Homebrew (installs if missing, works on Apple Silicon and Intel)
    • Stowing shared + macos into $HOME (--adopt means the repo always wins over any pre-existing files)
    • All packages, via get_packages.sh
    • macOS defaults (Hammerspoon config location)
    • Homebrew bash as the login shell
    • TouchID for sudo (/etc/pam.d/sudo_local)
  3. Open Neovim once — lazy.nvim installs itself and all plugins on first launch. Versions are pinned in lazy-lock.json.

New machine setup — Linux (Artix, dinit)

  1. Clone the repo to the home folder:

    git clone ssh://git@forge.quinlan.cloud:2222/joshquinlan/dotfiles.git ~/.dotfiles
    
  2. Run ~/.dotfiles/setup_artix.sh as your user (system steps escalate via doas, or sudo on first run if that is all the box has). It is idempotent and handles:

    • Arch repositories (artix-archlinux-support + [extra])
    • doas as the escalation tool (system/etc/doas.conf; an existing policy is never overwritten, only diffed)
    • every rice package: wayfire, kitty, starship, fuzzel, mako, fastfetch, grim, slurp, swaybg, wlsunset, playerctl, greetd with tuigreet, fonts and icons; the AUR bits (wayfire-plugins-extra, eww, swaylock-effects) build directly with makepkg, no AUR helper needed
    • un-stowable system files (system//etc), user groups and dinit services (dbus, seatd, greetd)
    • stowing shared + linux and rendering the theme
  3. Install the general CLI tools the shared configs expect: nvim, lsd, most, gpg, gpg-tui, fzf, fd, termscp, libsecret (secret-tool, for weremutt's keyring unlock), and anything else the shell complains about. For ble.sh, either use the distro package or extract the prebuilt nightly tarball to ~/.local/share/blesh (see setup_machine.sh for the exact command; the bashrc also checks /usr/share/blesh).

On non-Artix distros there is no setup script; stow manually:

~/.dotfiles/shared/.local/bin/restow --adopt && git -C ~/.dotfiles checkout .

The Arcane Ascension rice (Linux)

A Wayfire desktop themed on the Arcane Ascension books: twilight purples, spire-fire ambers, serif display type. Pieces: eww (bar and cards), fuzzel, mako, kitty with a fastfetch "system augury" greeting, swaylock-effects, greetd + tuigreet.

Keys: Super is the modifier. hjkl focus, Shift+hjkl snap, 1..6 workspaces (Shift carries the window), Enter terminal, Space launcher, E overview, F fullscreen, M minimise, Q close, Esc lock, Shift+S region screenshot. The workspace cube rides on Super+Alt+drag.

Accessibility is non-negotiable: large type everywhere (scale lives in one file, shared/.config/themes/typeface.sh) and full-desktop zoom on Super+scroll.

  • theme-switch <scheme> swaps the whole rice between palettes (arcane is the committed default, kanagawa the alternative) and live-reloads what can be reloaded.
  • animation-profile <grimoire|swift|toggle> flips between the full effect set (fire open, vortex close, magic-lamp minimise, cube, blur, wobbly) and a fast low-motion profile; --persist makes it survive a compositor restart.

The wallpaper lives at linux/.local/share/wallpapers/arcane-serpent-spire.png and is referenced by the palette (THEME_WALLPAPER), so a scheme can bring its own image.

Day-to-day

  • After adding or removing files in the repo, run restow — it stows shared plus the right OS package automatically. restow -n for a dry run; extra flags pass through to stow.

  • New files go in the most specific package that fits: shared unless it genuinely only makes sense on one OS.

Colour schemes

One palette rules every themed tool. The palettes live in shared/.config/themes/ (hex values plus curated 256-colour approximations): kanagawa.sh everywhere, arcane.sh as the Linux rice default. Fonts and the type scale have their own contract in typeface.sh.

  • Runtime consumers (fzf, ble.sh) read the THEME_* variables directly; .bashrc sources the palette named by DOTFILES_THEME (set from ~/.config/themes/rice-theme on Linux, Kanagawa elsewhere).
  • File-based configs are rendered from shared/.config/themes/templates/ by gen-theme (shared set: lsd, termscp, urlscan, the Ghostty cursor shader) and gen-theme --rice (rice set under templates/rice/: wayfire, kitty, starship, eww, mako, fuzzel, swaylock, GTK). Generated files are committed; edit the template or the palette, never the output, then rerun (needs envsubst, from the gettext package).

A new scheme is a new palette file defining the same variable set (including the semantic role variables); switch the rice to it with theme-switch <name>. Switching away from arcane leaves the rendered files locally dirty by design; git checkout . restores the committed defaults.

Git identity

  • Default identity is Josh Quinlan <josh@quinlan.cloud>.
  • Repos with GitHub, GitLab, or sr.ht remotes automatically switch to the donquinleone identity via includeIfpriv.inc.

Gotcha: the global git config is itself a stowed file in this repo. Rebasing here checks out old commits, which silently reverts the global config mid-rebase (identity, signing key, everything). When rewriting history in this repo, set the identity explicitly:

GIT_COMMITTER_NAME="Josh Quinlan" GIT_COMMITTER_EMAIL="josh@quinlan.cloud" \
  git commit --amend ...

macOS Gatekeeper

Some apps refuse to open, claiming to be "damaged". That's Gatekeeper. Fix per app with:

xattr -cr /Applications/AppName.app