aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/nix/README.md
diff options
context:
space:
mode:
authorLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-05-20 13:56:09 +0100
committerLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-05-20 13:56:09 +0100
commitde5146c7976e1fb38e8d1f82c30544462d881100 (patch)
tree2de6f2358d6b83b2f64b68fe105ed11d4ff0feea /nix/README.md
parent52e53ad7956f637af3bb87de79934bfda4b74a2e (diff)
downloaddotfiles-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.md201
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.