Files
Ryan Yin 4470b3d014 docs: centralize documentation under docs/
Move standalone repository docs from the root into docs/ and add a documentation index that links the moved docs and the code-local runbooks.

Update Markdown links, source comments, and the root entry points to the new locations. Keep README.md and AGENTS.md at the root as required entry points.

Assisted-by: DeepSeek Harness (deepseek-flash)
2026-11-02 17:48:57 +08:00

8.5 KiB

name, description
name description
nix-config-update Use when a version changes here: upgrading or updating a package or nixpkgs, bumping or pinning a flake input (a tag or commit, not a branch), or deploying the result to hosts, VMs, and MicroVM guests.

Updating this flake safely

Read this before running just up, just upp, just up-nix, or just override-pkgs, and before deploying the result to any host.

Core rules

  1. Update and deploy are two steps. Never fold "bump the lock file" and "switch every host" into one blind action.
  2. Validate before touching a host. An update is only a candidate until just test is green and the affected hosts build locally.
  3. One reviewable commit per update. The convention here is a flake.lock: Update commit for the bump; a config change gets its own commit or PR.
  4. Keep a way back. Anything that reaches a host needs a rollback path: the previous generation, boot mode, just darwin-rollback, or git revert.
  5. Security first. An update re-opens the supply chain. Re-audit every input whose revision moved before you deploy it.
  6. Pin third-party inputs. A third-party input — not one of the user's own ryan4yin/* repositories — MUST name a version tag or a fixed commit, not a branch. Official channel upstreams that publish no release (nixpkgs' nixos-unstable/master) are the exception. When only a branch carries a needed third-party change, keep the resolved revision in the lock, record why next to the declaration, and re-audit each update.

1. Pre-flight

  • git status is clean, and you are on main or a fresh branch for the work. If you are on an unrelated feature branch, stop and ask for the right branch before bumping.
  • just test is green before the update, so you have a baseline to compare against. It and every eval fetch the private mysecrets input over SSH; without that access they hang instead of failing.
  • Check WORKAROUNDS.md for the inputs and recipes you are about to use; an active row that names one of them replaces its command here.
  • Enough disk for the new closures: df -h /nix/store. Broad nixpkgs bumps pull a lot.
  • Know what will move and choose the narrowest recipe that does the job.

2. Run the update

  • Everything: just up — updates and commits the lock file automatically; audit before pushing
  • One input: just upp <input> — same, for a single input, e.g. just upp catppuccin; audit before pushing
  • The nixpkgs family: just up-nix — updates nixpkgs-stable/-master/-darwin/-patched; not nixpkgs
  • Pin nixpkgs: just override-pkgs <hash> — pin nixpkgs to a known-good Hydra commit
  • Update but keep it dirty: nix flake update <input> — leaves the change uncommitted so you can inspect it first
  • Nix itself (macOS): just nix-upgrade — determinate-nixd upgrade; macOS only

After an update, read git diff flake.lock: it lists exactly which inputs moved and by how many commits. Because just up and just upp commit automatically, either use nix flake update <input> while auditing or amend the generated lock commit after the audit so its message records the conclusion.

3. Audit the change

Treat an update as a supply-chain event.

  1. From git diff flake.lock, list every input whose locked.rev changed.

  2. Find the inputs that contribute modules this repo imports:

    grep -rn "homeModules\.\|nixosModules\.\|darwinModules\." --include='*.nix' .
    
  3. For each of those inputs, read what changed between the old and new revision in the module files we actually import. Look for:

    • restricted nix.settings (substituters, extra-substituters, trusted-public-keys, trusted-users) — at the Home Manager layer these are ignored for an untrusted user and only warn, but at the system layer they are a root-equivalent trust boundary;
    • environment.etc / home.file entries with a mode or user that widens access;
    • system.activationScripts, systemd.services running as root, sudo/polkit rules;
    • lib.mkForce overrides that silently replace existing settings;
    • eval-time network access or import-from-derivation.
  4. For nixpkgs-class inputs, skim the lock diff and expect broken packages and eval deprecation warnings.

  5. Write the audit conclusion into the update commit message. If an automatic recipe already made the commit, amend it only after reviewing the diff; do not push the unaudited commit first.

4. Validate before deploying

Run these before any host is touched:

  • just test — fails (non-zero) unless the suite returns true.
  • just eval-host <host> — fast, evaluation only.
  • just build-host <host> — builds the full system closure and catches broken packages or build failures that eval misses.
  • just build-microvm <guest> — same, for a MicroVM guest.
  • just test-vm — the runtime security-VM check (needs /dev/kvm); just test does not run it.
  • nix flake check — broader checks when the change touches shared code.

Cover every host you are about to deploy, and prefer building the closure over trusting a green eval.

Preview what the machine you are on will change before it is deployed:

nix store diff-closures /run/current-system '.#nixosConfigurations.<host>.config.system.build.toplevel'

It lists every package whose version or size moves; an empty result means nothing changes. Read it for unexpected removals, major-version jumps, and kernel or systemd changes that need a reboot.

5. Deploy in stages

Modes are switch (take effect now, the default) and boot (only the next boot). Add debug for verbose output. Anything else is rejected.

The user runs these for the machine you are on:

  • Current desktop: just niri [mode] [verbosity]
  • Other local NixOS host: just local [mode] [verbosity]
  • macOS: just local [debug] (build then switch; macOS has no switch/boot split)

The rest authenticate over SSH as root on the target, so they run non-interactively — but they still change remote state. Confirm the target instead of trusting the recipe's default, then state it back to the user and get authorization for that host:

hostname                  # which machine you are on
git branch --show-current
git remote -v
getent hosts <host>       # the address the tag will connect to
ssh root@<host> hostname  # the host that actually answers
  • Remote servers: just shoryu [mode], just shushou, just youko, just ruby, just kana
  • All VM hosts at once: just lab [mode]; any Colmena tag: just col <tag> [mode]
  • k3s test nodes: just k3s-test [mode]
  • MicroVM guest: just microvm-deploy <guest> <physical-host> installs the runner on the physical host and restarts the guest unit there (WA-026); switch that host afterwards, or the next activation reverts the guest to its own runner. Deploy guests serially and check each one before moving on.

Use boot plus a deliberate reboot for anything that can drop networking mid-flight: the VM hosts with the br0 bridge, and broad nixpkgs bumps. See hosts/README.md. Pause Flux sync on the k3s cluster before changing a node's networking, and resume it once the node is back.

6. Verify after deploying

  • systemctl --failed and just list-failed for failed units.
  • journalctl -b -p err for boot-time errors.
  • The user-visible surface: network, the desktop session, and the specific service you changed.
  • Re-run just test so a dirty tree cannot hide a regression.

7. Roll back

  • NixOS: just history lists system generations; pick the previous one in the bootloader menu, or re-deploy a reverted tree.
  • macOS: just darwin-rollback (darwin-rebuild --rollback).
  • Remote (Colmena): git revert <sha> and re-apply, or apply with boot and reboot.
  • Update failed but never deployed: drop the bump's own uncommitted lock change with git checkout -- flake.lock, then reproduce. If the tree holds other uncommitted work, ask before discarding anything.
  • Isolate a bad bump by moving one input at a time with nix flake update <input>, and pin a known-good nixpkgs with just override-pkgs <hash> while the breakage is fixed upstream.

8. Clean up only after it is stable

just gc (older than 7 days) and just clean (wipes profile history) delete the generations you would roll back to, so run them last, once the update has proven stable. just gcroot only lists GC roots. The full list of hazardous recipes is in the Command Hazards section of AGENTS.md.