Skip to content

refactor(installer): install-answers.json + initialUser option (#29) - #64

Merged
phorcys420 merged 3 commits into
mainfrom
phorcys/install-answers
Aug 24, 2026
Merged

refactor(installer): install-answers.json + initialUser option (#29)#64
phorcys420 merged 3 commits into
mainfrom
phorcys/install-answers

Conversation

@phorcys420

@phorcys420 phorcys420 commented Aug 24, 2026

Copy link
Copy Markdown
Member

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.sh copied local.nix.example to hosts/<host>/local.nix and 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 setting CODER_ADMIN_* on coder-init-admin.service via a raw systemd.services.*.environment block in local.nix is bad practice and the name should reflect that it only sets the initial password.

Changes

  • install-answers.jsoninstall.sh now writes hosts/<host>/install-answers.json with jq (dropped the nix_string_escape / sed_replacement_escape helpers and all sed -i splicing). local.nix (copied from the example) reads it via builtins.fromJSON and maps the values onto NixOS options at mkDefault, 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 generated default.nix stays hardware/disk only. jq is now a required tool; the file is chmod 600 and 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.nix wires it onto coder-init-admin.service (the service that does the work) as INITIAL_USER_*, renamed from CODER_ADMIN_* to reflect it only sets the initial user/password. Users no longer hand-edit a systemd service's environment. box-turnkey.nix sets the option directly for image builds.
  • installer/bootstrap/ — moved local.nix.example and nixos/disko-standard.nix here (the assets install.sh consumes for a new host). install.sh stays at the repo root. Import paths and docs updated.
  • DocsREADME.md, hosts/incus-vm/README.md, agents.md updated 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.sh clean; bash -n OK.
  • Evaluated a simulated generated host (minimal default.nix + local.nix from the example + install-answers.json): a password containing $ " \ round-trips JSON → fromJSONINITIAL_USER_PASSWORD intact (the case the old sed path couldn't guarantee).
  • nixosConfigurations._appliance-disk and .incus-vm still evaluate to a full system toplevel drv; _appliance-disk picks up admin@coder.com from box-turnkey's initialUser.

Note for reviewer

disko-standard.nix was moved to installer/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

  1. Kill sed splicing in install.sh; write installer answers as structured JSON (jq).
  2. Generated host reads that JSON from Nix.
  3. Fix change structure of CODER_ADMIN_EMAIL and CODER_ADMIN_PASSWORD #29: stop making users override systemd.services.coder-init-admin.environment in local.nix. Expose a clean NixOS option instead; module wires it to the service that does the work.
  4. Move local.nix.example (+ bootstrap assets) under installer/bootstrap/.
  5. Update docs + validation paths.

Design

#29 resolution

The Coder server binary can't create the first user from env (only coder login/create-admin-user do, and the box already does this in coder-init-admin.service). So "pass to the Coder service" literally isn't possible on coder.service. Instead:

  • New option services.coder-nixos.initialUser = { username; email; password; }.
  • configuration.nix sets coder-init-admin.service.environment from this option (the service that does the work). Users never touch a systemd.services.* env block anymore.
  • Rename internal script vars CODER_ADMIN_* -> INITIAL_USER_* to reflect it's the initial user/password. coder-reset keeps inheriting the same env.

install-answers.json

Per host: hosts/<host>/install-answers.json, written by jq, with lanIp, initialUser, and loginUser. Generated default.nix does builtins.fromJSON (builtins.readFile ./install-answers.json) and maps straight onto options at mkDefault. local.nix stays creds-free (optional overrides only) and remains the clean hand-edit / agenix-sops surface.

OS login user

Declared by the generated default.nix from install-answers.json; box-turnkey.nix hardcodes its own.

install.sh

  • Drop nix_string_escape + sed_replacement_escape + all sed -i.
  • Write install-answers.json with jq; generated default.nix reads it.
  • cp installer/bootstrap/local.nix.example instead of root path.
  • Add jq to required tools; intent-to-add install-answers.json -f.

Files moved

  • local.nix.example and nixos/disko-standard.nix -> installer/bootstrap/.
  • install.sh stays 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 eval on a host toplevel drvPath, shellcheck/shfmt on install.sh.


Generated by Coder Agents on behalf of @phorcys420.

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
phorcys420 marked this pull request as ready for review August 24, 2026 20:21
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

📀 ISO build artifacts

Kind Arch Size Download
Installer aarch64 3.49 GB ⬇️ coder-box-installer-aarch64-linux.iso
Installer x86_64 3.67 GB ⬇️ coder-box-installer-x86_64-linux.iso

↻ Updated for c2e883b · run #119 · artifacts expire in ~1 day · sign in to GitHub to download.

@phorcys420
phorcys420 merged commit 3a3bfeb into main Aug 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

change structure of CODER_ADMIN_EMAIL and CODER_ADMIN_PASSWORD

1 participant