Reference
Development
Collective configures the machines it is developed on, so a careless change can break the environment that would fix it. The working rule is to evaluate and test freely, and leave activation to the operator.
Ground rules
cltv switch, nixos-rebuild switch, darwin-rebuild switch, nix-on-droid switch or a raw nix flake update. Switches depend on SSH agents, private inputs, tokens and work steps that only the operator's session has. Say what changed and that a switch is needed.- Evaluation,
nix build --no-linkof a package or closure, andnix-on-droid buildwithoutswitchare fine. - Install nothing imperatively: no
nix profile install,nix-env -i,cargo installor copied binaries. Usecltv runto try a package. - Advance project pins with
cltv nix update-inputs --input NAME --no-publishand review the lock diff. - Never print, log or attach a secret value. Diagnose secrets by path and metadata (see Operations).
Evaluate instead of switching
# a system option on this machine or anothercltv nix eval system -e 'config.services.openssh.enable'cltv nix eval system -e 'config.environment.systemPackages' --system-name NAME --system-type nixos# a Home Manager optioncltv nix eval home -e 'config.programs.zsh.enable'# anything in the flake's outputscltv nix eval outputs -e 'registry.lists'# does the whole machine still evaluate?nix eval --raw .#nixosConfigurations.NAME.config.system.build.toplevel.drvPath# a library expressionnix eval --json .#lib.x86_64-linux --apply 'L: L.dispatch { int = x: x + 1; } 41'42
cltv repl --flake opens nix repl with the system's environment preloaded, and cltv repl -p opens one against the working copy of collective-public. cltv script run --name NAME runs an edited cltv command without a switch, and cltv run pkg#bin runs a package from the configured set.
Tests
Tests sit next to what they test and run without a switch.
| Suite | Where | Run |
|---|---|---|
| collective-lib | Inline _tests in 21 library files, plus nix-reflect's suites | COLLECTIVE_PUBLIC_DIR=$PWD/collective-public ./collective-public/scripts/run-tests |
| One library test | Any named test | ./collective-public/scripts/run-test module name, or debug-test for traces |
| Public modules | Module suites in collective-public | ./collective-public/scripts/run-module-tests |
| Flake checks | Runner sandbox contract (evaluated from a real devbox), supervisord exec canary, release and VM workflow contracts, browser VM receipts | nix flake check, or nix build .#checks.system.name |
| Rendered behaviour | 12 Nix tests in tests/ that render module output (displays, gestures, audio, Tailscale on Android, handheld power) | Per file, passing Collective's locked nixpkgs or its lib |
| Python | 23 test files in tests/, 13 in scripts/ | python3 -m unittest discover -s tests -p 'test_*.py' -v |
| Deployed helpers | About thirty shell, Python and PowerShell tests for runner, cache and devbox helpers | Each file directly, e.g. bash modules/tests/github-runner-min-lifetime-test.sh |
The deployed-helper pattern matters: when a shell script is embedded in a module, the module reads it from a file with readFile rather than inlining it, and a test sources the same file. The tested code is the deployed code.
What runs where
| Workflow | Trigger | Does |
|---|---|---|
| Build & Cache Collective | Version tags, weekly, manual | Builds machine closures on self-hosted runners and fills the caches |
| FlakeHub (parallel to Attic) | Push to main | Builds the prebuilt machines' closures and pushes them to FlakeHub Cache |
| Pages | Changes to site/ on main | Validates and publishes this site |
| MicroVM builds | Manual and on change | Build and promote the browser-bootable VM bundles |
The scheduled prebuilds on cache nodes are not CI: they run as services on the nodes (see Delivery). Nothing gates a pull request automatically, so run the relevant tests and evaluations before merging.
Conventions
- Formatting
nix fmtruns alejandra.- Modules
- Options under a clear prefix, declared with the library's helpers (
mkEnable,mkDefaultOption); configuration behindmkIf cfg.enable; platform differences throughsystemType.switch, not machine names. - Packages
- A flake input, an overlay entry and a bootstrap category; never a second delivery path (see Architecture).
- Machines
- Only machine-specific settings in
machines/name/; registry first, and explicit lists between their markers. - Live configuration
- Anything tools edit while running goes in
standalone/, linked out of the store. - Public code
- Reusable modules and the library go in
collective-public/, which is published to its own repository. - Incidents
- A fix to a production failure comes with a runbook in
docs/runbooks/: the evidence, the change and how to validate it. - Agents
- Repository instructions live in
AGENTS.md; reusable procedures are skills inplugins/andstandalone/pi/skills.
This site
The site's source is in site/: hand-written HTML whose shared parts are filled in by scripts/site/build.py. Drawings come from scripts/site/figures.py, and the CLI reference from scripts/site/cli-help.sh. scripts/check-site.py checks structure, links and budgets, and refuses any machine name from the registry, address, tailnet name or login before the site can be published.
python3 scripts/site/build.pypython3 scripts/check-site.py