diff --git a/docs/install-variants.md b/docs/install-variants.md new file mode 100644 index 00000000..3e740d63 --- /dev/null +++ b/docs/install-variants.md @@ -0,0 +1,99 @@ +# The install page, and the scripts behind it + +**Status: for discussion, 2026-08-13.** Nothing here is built. It exists so tomorrow's conversation +is about real branches rather than sketched ones — every question below is one the scripts already +ask today. + +## The shape agreed + +- One **source** — the interactive scripts as they are. +- A **build script** that compiles them into single files, because `curl | bash` cannot fetch libs. +- The build emits **one script per leaf** of the question tree, not one script with pre-seeded + answers. A person auditing before running reads only their own path. +- Verification is of the **generator**, once: anyone regenerates the leaves from source and diffs + them against what is published. One thing to trust rather than N. + +--- + +## The questions that actually exist + +Forty-seven prompts across the two scripts. Almost none of them should become a branch — the +distinction that matters is: + +**A branch** changes which *code* runs. Removing it makes a script genuinely shorter. + +**A value** changes a *string*. Removing it makes a script no shorter — it just moves the answer +from a prompt to a constant. + +**A consent** is a yes/no about doing a step at all. These are the interesting middle: pre-answering +one lets the build delete the section entirely. + +### Branches — these change what code exists + +| question | answers | what it eliminates | +| --- | --- | --- | +| operating system | macOS · Debian/Ubuntu · Arch · Fedora | 17 of 26 machine-setup sections on macOS; the whole `case $PM` ladder collapses to one arm | +| machine role | homelab · vps · dev | swap, ballast, earlyoom, sleep/suspend, boot-hang, static addressing — each is role-gated today | +| tailnet | already connected · set one up · none | the entire Tailscale section, its four sub-options and the offscale explanation | +| which half | machine + officer · officer only · machine only | one of the two scripts disappears | + +### Consents — pre-answering deletes a section + +Docker · fail2ban · unattended-upgrades · Neovim · agent CLIs · shell config · firewall · SSH +hardening · DNS · swap · ballast · earlyoom · inotify · boot-on-start. + +Fourteen sections that a leaf script can simply not contain. + +### Values — never a branch + +Username · install path · git name and email · port · public URL · Postgres connection · timezone · +locale · LAN CIDR · swap size · swappiness. + +These stay as prompts even in a generated script, or arrive as environment variables. Baking them +into a published file would mean publishing somebody's hostname. + +--- + +## Where this collides with `--unattended` + +`--unattended` and a generated leaf are the *same mechanism seen twice*: both are "answer these in +advance". The difference is only whether the answer is baked in at build time or supplied at run +time. + +Worth deciding tomorrow whether a leaf script is literally `base.sh --unattended` with a header of +constants, or whether the build truly strips the dead branches. The second is what makes it +auditable-by-being-short; the first is what makes it maintainable. **They are not the same artifact, +and the whole plan rests on which one we mean.** + +One thing that already exists and should be preserved either way: with no tty, `install_config` +keeps the user's file rather than replacing it. Every unattended answer needs to be conservative in +that same way, and that is a property of each prompt, not of the flag. + +--- + +## The combinatorics + +4 OS × 3 roles × 3 tailnet states = **36 leaves** before any consent is considered, and consents +multiply it past anything anyone would publish. + +So the tree the install page walks cannot be the full product. Two ways out, to choose between: + +1. **Publish a few opinionated leaves** — "Ubuntu VPS, new tailnet", "macOS dev machine", "Ubuntu + homelab, existing tailnet" — and send everything else to the full interactive script. +2. **Generate on demand** — the page composes the leaf when the questions are answered. Stronger, but + the artifact is no longer a static file anyone can diff against the repo, which costs the + verification property the whole design was for. + +My inclination is (1), because (2) quietly trades away the thing that made per-leaf scripts worth +building. But it is a real trade and it is yours. + +--- + +## Open, for tomorrow + +- Does a leaf strip dead code, or set constants and call the base? +- How many leaves get published, and what happens to the rest? +- Does the install page show the script before running it? It should — that is the moment auditing + is cheap and nobody will do it afterwards. +- The report from `install-report.md` names a script commit. A generated leaf needs to name the + source commit it was generated from, or the report cannot be checked against anything.