diff options
| author | 2026-05-20 13:56:09 +0100 | |
|---|---|---|
| committer | 2026-05-20 13:56:09 +0100 | |
| commit | de5146c7976e1fb38e8d1f82c30544462d881100 (patch) | |
| tree | 2de6f2358d6b83b2f64b68fe105ed11d4ff0feea /nix/README.md | |
| parent | 52e53ad7956f637af3bb87de79934bfda4b74a2e (diff) | |
| download | dotfiles-de5146c7976e1fb38e8d1f82c30544462d881100.tar.gz dotfiles-de5146c7976e1fb38e8d1f82c30544462d881100.tar.bz2 dotfiles-de5146c7976e1fb38e8d1f82c30544462d881100.zip | |
refactor(nix): promote remote-dev/ to nix/ with common/vm/host split
Restructures the Home-Manager profile to support both the Arch host and
the Ubuntu remote-dev VM from the same flake.
- remote-dev/ → nix/ (hard rename; .chezmoiignore updated)
- home.nix split into common.nix (shared), vm.nix (Mason runtime
carve-outs + podman stack), host.nix (gpg scdaemon delegation to
system pcscd)
- flake.nix exposes homeConfigurations.{vm,host} via a mkProfile
helper
- rj alias in dot_zshrc updated to ~/.local/share/dotfiles/nix
- bootstrap.sh / justfile updated to use #vm against the new path
The split is behaviour-preserving for the VM: vm.nix + common.nix
together carry the same package set as the previous home.nix.
host.nix is provisioned but not yet wired into bootstrap (phase p8).
Phase 1 of the nix-on-host migration plan.
Diffstat (limited to 'nix/README.md')
| -rw-r--r-- | nix/README.md | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/nix/README.md b/nix/README.md new file mode 100644 index 0000000..2bf3383 --- /dev/null +++ b/nix/README.md @@ -0,0 +1,201 @@ +# nix + +Home-Manager profiles for the Arch host (`host.nix`) and the Ubuntu +remote-dev VM (`vm.nix`), both layered on top of `common.nix`. Shares +the same nvim, zellij, and zsh configs from the same repo — no +duplication across machines. + +## Bootstrap + +**Host (Arch)**: managed by the top-level `bootstrap.sh` in the repo +root (installs nix + runs `home-manager switch --flake .../nix#host` +as part of `just init`). + +**VM (Ubuntu)**: as the dev user (must have sudo): + +```sh +curl -fsSL https://raw.githubusercontent.com/sommerfelddev/dotfiles/master/nix/bootstrap.sh | sh +``` + +Then log out and back in. Run `nvim` once to let it fetch plugins from +GitHub on first launch. + +## What the VM bootstrap does + +1. Installs Nix (Determinate Systems multi-user installer). +2. Clones this repo to `~/.local/share/dotfiles`. +3. Runs `home-manager switch --flake .../nix#vm`, which: + - Installs the CLI tool subset (see `common.nix` + `vm.nix`). + - Symlinks `~/.config/{nvim,zellij,zsh,direnv,ghostty,git}` and + `~/.ssh/config` at the cloned working tree via + `mkOutOfStoreSymlink`, so `git pull` is enough to pick up config + edits — no rebuild needed for config-only changes. + - Sets `ZDOTDIR=$HOME/.config/zsh` so the shared zshrc/zprofile load. +4. Appends the nix-store zsh to `/etc/shells` and `chsh`'s to it. + +## Updating after a dotfiles change + +Run from `~/.local/share/dotfiles/nix` (host or VM): + +```sh +just update # pull + home-manager switch (handles everything) +``` + +Or piece-by-piece if you know which one you need: + +```sh +just pull # config-only changes (nvim/zellij/zsh/git/ssh): no rebuild needed +just switch # rebuild home-manager from the current checkout +``` + +> `just update` runs `pull` then `switch`. The home-manager invocation +> uses `--impure --flake '.#vm' -b backup`; the single-quotes around the +> flake ref matter because our zsh enables `extendedglob`, which would +> otherwise interpret `.#vm` as a glob pattern. On the host, swap +> `#vm` → `#host`. + +## Adding a tool + +Edit `common.nix` (shared) or the profile-specific file (`host.nix` / +`vm.nix`), add to `home.packages`, then `just switch` (or `just +update`). + +## Single-shell policy (leaf tools only) + +The nix profile carries **leaf CLI tools** plus **editor/AI-agent +runtimes**, and nothing else. Specifically forbidden in `home.packages` +because they would shadow the system toolchain via `PATH` and silently +break builds against the system sysroot/libc/CI: `cc`, `c++`, `gcc`, +`g++`, `clang`, `clang++`, `ld`, `make`, `cmake`, `ninja`, `meson`, +`pkg-config`, `autoconf`, `automake`, `python`, `python3`, `pip`, +`cargo`, `rustc`, `go`. The system `python3` stays the default +interpreter for project builds. + +Explicit carve-outs (used only by editor/AI agents, never by the +project build): + +- `nodejs` — `node`/`npm`/`npx` for npm-based LSPs and + copilot-language-server. +- `uv` — `uv`/`uvx` for ad-hoc Python tooling in isolated venvs. `uv` + does NOT install a `python3` in PATH; it manages its own + interpreters under `~/.local/share/uv/`. System `python3` is + untouched. +- `clang-tools` — `clang-format`, `clang-tidy`, `clangd` only (no + compiler driver). + +If a project needs a newer build toolchain, drop a `flake.nix` + +`.envrc` in that project tree (direnv + nix-direnv is already wired +up). Don't add it to `common.nix`/`host.nix`/`vm.nix`. + +## Commit signing on the VM (SSH-format, no GPG secrets) + +GPG private keys never leave the host. Commits on the VM are signed +with the **forwarded SSH agent** in SSH-signature format, using the +authentication subkey gpg-agent already exposes via `ssh-add -L`. + +One-time setup on the VM: + +```sh +mkdir -p ~/.config/git + +# allowed_signers: maps your committer email to the SSH pubkey of the +# auth subkey. Adjust the grep if you have multiple keys. +printf '%s %s\n' \ + "$(git config user.email)" \ + "$(ssh-add -L | head -n1)" \ + > ~/.config/git/allowed_signers + +# Machine-local git override (NOT tracked in dotfiles). +cat > ~/.config/git/config.local <<EOF +[gpg] + format = ssh +[gpg "ssh"] + allowedSignersFile = ~/.config/git/allowed_signers +[user] + signingkey = $(ssh-add -L | head -n1 | awk '{print $1" "$2}') +EOF +``` + +The tracked `dot_config/git/config` ends with `[include] path = +~/.config/git/config.local`, so the override is picked up +automatically (and silently ignored on machines that don't have it). + +Required on the **host's** `~/.ssh/config` for the VM `Host` block: + +``` +ForwardAgent yes +``` + +Verify on the VM after SSH-ing in: + +```sh +ssh-add -L # should list your auth pubkey(s) +git commit --allow-empty -m test +git log --show-signature -1 +``` + +## Caveats + +- **GPG / pass**: HM installs `gnupg` and `pass` but does _not_ import + any private key. On the VM, use SSH-format signing via the forwarded + agent instead (see above). On the host, smartcard access via + `pcscd` is configured in `host.nix` (`~/.gnupg/scdaemon.conf`). +- **Disk usage**: Nix store + nvim plugins consumes ~3-5 GB. Check + partition size first on the VM. +- **Network for first nvim launch**: `vim.pack.add` fetches plugins + from GitHub on first start. +- **Ubuntu apt collisions**: Nix-installed binaries appear first in + PATH. The leaf-tools policy above exists precisely to keep this + shadowing contained to harmless tools. + +## Podman (rootless, VM only) + +Nix can't manage setuid helpers, `/etc/subuid`/`/etc/subgid`, or kernel +cmdline. Do this once on the VM as root: + +```sh +sudo apt install -y uidmap +grep "^$USER:" /etc/subuid /etc/subgid || \ + sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 "$USER" +``` + +Then (optional, **only** if you need rootless CPU/memory limits) enable +cgroups v2. Ubuntu 20.04 still defaults to v1; flipping this requires a +reboot and affects every workload on the box, so skip unless you have a +concrete need: + +```sh +sudo sed -i 's|^GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"|GRUB_CMDLINE_LINUX_DEFAULT="\1 systemd.unified_cgroup_hierarchy=1"|' /etc/default/grub +sudo update-grub && sudo reboot +``` + +Verify: + +```sh +podman info | grep -E 'cgroupVersion|graphDriverName|networkBackend' +# expected: graphDriverName: overlay, networkBackend: netavark +# cgroupVersion: v1 is fine — only blocks --memory/--cpus flags. The +# podman v5 deprecation warning is silenced by PODMAN_IGNORE_CGROUPSV1_WARNING, +# set in vm.nix. +podman run --rm docker.io/library/alpine echo hi +``` + +The VM home-manager profile installs `podman`, `crun`, `conmon`, +`netavark`, `aardvark-dns`, `slirp4netns`, and `passt`, and writes +sensible `~/.config/containers/{registries,storage,policy}.conf` files. + +## How it's wired + +`common.nix` uses `config.lib.file.mkOutOfStoreSymlink` so the symlinks +point at the **live working tree** at `~/.local/share/dotfiles/...`, +not at copies in `/nix/store`. This means: + +- Editing `dot_config/nvim/init.lua` in the cloned repo takes effect + on the next `nvim` launch with no rebuild. +- `home-manager switch` only needs to re-run when adding/removing a + package or changing what's symlinked. + +The zsh plugins (`zsh-syntax-highlighting`, etc.) live in +`$HOME/.nix-profile/share/`. The shared `dot_zshrc` prefers the +nix-profile path on both host and VM, falling back to system paths for +un-bootstrapped states. |
