refactor(installer): install-answers.json + initialUser option (#29) - #64
Merged
Conversation
install.sh generated hosts/<host>/local.nix by copying local.nix.example and splicing values in with a two-stage Nix/sed escape. That was fragile (passwords with $, ", or \ could corrupt the generated Nix) and mixed credentials into the user-editable override file. Instead, write the install-time answers to hosts/<host>/install-answers.json with jq and read them from Nix via builtins.fromJSON. The generated default.nix maps them onto NixOS options at mkDefault, so local.nix stays credential-free and can override any value (e.g. from agenix/sops). Fix #29 by replacing the "set CODER_ADMIN_* on coder-init-admin.service via local.nix" pattern with a first-class option: services.coder-nixos.initialUser = { username; email; password; }; configuration.nix wires those onto coder-init-admin.service (the service that does the work) as INITIAL_USER_*, renamed to reflect that it only sets the initial user/password. Users no longer hand-edit a systemd service's environment. Also move the installer bootstrap assets (local.nix.example, disko-standard.nix) under installer/bootstrap/, and update the docs.
Move the install-answers.json read out of the generated default.nix and
into local.nix (via local.nix.example). The generated default.nix goes
back to hardware/disk only; local.nix now reads the answers file and sets
services.coder-nixos.{lanIp,initialUser} and the OS login user, so the
file the user owns is the one that applies (and can override) the values.
phorcys420
marked this pull request as ready for review
August 24, 2026 20:21
📀 ISO build artifacts
↻ Updated for |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Refactor the installer so per-host values live in a structured JSON file instead of being spliced into Nix source with
sed, and fix #29 by turning the Coder admin bootstrap into a first-class NixOS option.Fixes #29.
Why
install.shcopiedlocal.nix.exampletohosts/<host>/local.nixand spliced in the admin/OS credentials + LAN IP with a two-stage Nix-string +sed-replacement escape. That was fragile (a password containing$,", or\could corrupt the generated Nix) and it baked secrets into the file users are supposed to hand-edit. Separately, #29 flagged that settingCODER_ADMIN_*oncoder-init-admin.servicevia a rawsystemd.services.*.environmentblock inlocal.nixis bad practice and the name should reflect that it only sets the initial password.Changes
install-answers.json—install.shnow writeshosts/<host>/install-answers.jsonwithjq(dropped thenix_string_escape/sed_replacement_escapehelpers and allsed -isplicing).local.nix(copied from the example) reads it viabuiltins.fromJSONand maps the values onto NixOS options atmkDefault, so the file the user owns is the one that applies the answers and can cleanly override any value (e.g. from agenix/sops). The generateddefault.nixstays hardware/disk only.jqis now a required tool; the file ischmod 600and force intent-added (it holds the initial password).services.coder-nixos.initialUser(fixes change structure of CODER_ADMIN_EMAIL and CODER_ADMIN_PASSWORD #29) — new option{ username; email; password; }.configuration.nixwires it ontocoder-init-admin.service(the service that does the work) asINITIAL_USER_*, renamed fromCODER_ADMIN_*to reflect it only sets the initial user/password. Users no longer hand-edit a systemd service'senvironment.box-turnkey.nixsets the option directly for image builds.installer/bootstrap/— movedlocal.nix.exampleandnixos/disko-standard.nixhere (the assetsinstall.shconsumes for a new host).install.shstays at the repo root. Import paths and docs updated.README.md,hosts/incus-vm/README.md,agents.mdupdated for the new option, file list, moved paths, and reset/password-change steps.Validation
nix fmt— 30 files, 0 changed (nixfmt/statix/deadnix/shfmt clean);shellcheck install.shclean;bash -nOK.default.nix+local.nixfrom the example +install-answers.json): a password containing$ " \round-trips JSON →fromJSON→INITIAL_USER_PASSWORDintact (the case the oldsedpath couldn't guarantee).nixosConfigurations._appliance-diskand.incus-vmstill evaluate to a full systemtopleveldrv;_appliance-diskpicks upadmin@coder.comfrombox-turnkey'sinitialUser.Note for reviewer
disko-standard.nixwas moved toinstaller/bootstrap/and is still imported by the generated hosts and_appliance-disk(not copied per-host), to avoid forking a shared infra file. Say the word if a per-host copy was intended instead.Implementation plan
box: installer/bootstrap refactor + fix #29
Goals
sedsplicing ininstall.sh; write installer answers as structured JSON (jq).systemd.services.coder-init-admin.environmentinlocal.nix. Expose a clean NixOS option instead; module wires it to the service that does the work.local.nix.example(+ bootstrap assets) underinstaller/bootstrap/.Design
#29 resolution
The Coder server binary can't create the first user from env (only
coder login/create-admin-userdo, and the box already does this incoder-init-admin.service). So "pass to the Coder service" literally isn't possible oncoder.service. Instead:services.coder-nixos.initialUser = { username; email; password; }.configuration.nixsetscoder-init-admin.service.environmentfrom this option (the service that does the work). Users never touch asystemd.services.*env block anymore.CODER_ADMIN_*->INITIAL_USER_*to reflect it's the initial user/password.coder-resetkeeps inheriting the same env.install-answers.json
Per host:
hosts/<host>/install-answers.json, written byjq, withlanIp,initialUser, andloginUser. Generateddefault.nixdoesbuiltins.fromJSON (builtins.readFile ./install-answers.json)and maps straight onto options atmkDefault.local.nixstays creds-free (optional overrides only) and remains the clean hand-edit / agenix-sops surface.OS login user
Declared by the generated
default.nixfrominstall-answers.json;box-turnkey.nixhardcodes its own.install.sh
nix_string_escape+sed_replacement_escape+ allsed -i.install-answers.jsonwithjq; generateddefault.nixreads it.cp installer/bootstrap/local.nix.exampleinstead of root path.jqto required tools; intent-to-addinstall-answers.json -f.Files moved
local.nix.exampleandnixos/disko-standard.nix->installer/bootstrap/.install.shstays at repo root (REPO_DIR derives repo root from its own path).Docs
README.md,hosts/incus-vm/README.md,agents.md: new option names, new file list, moved example path, reset/password-change instructions.Validation
nix fmt/nix flake check,nix evalon a host toplevel drvPath, shellcheck/shfmt on install.sh.Generated by Coder Agents on behalf of @phorcys420.