Enumerates the forty-seven prompts the two scripts actually ask and sorts them into branches, consents and values — the distinction that decides what a generated leaf can remove. Four real branches (OS, role, tailnet state, which half), fourteen consents that let a leaf omit a section entirely, and a set of values that must stay prompts because baking them in would mean publishing somebody's hostname. Names the two things that need deciding rather than deciding them: Whether a leaf strips dead code or sets constants and calls the base. They are different artifacts and the plan rests on which one is meant — the first is what makes it auditable by being short, the second is what keeps it maintainable. And the combinatorics: 4 OS x 3 roles x 3 tailnet states is 36 leaves before consents, so the tree cannot be the full product. Publishing a few opinionated leaves keeps the static-file-anyone-can-diff property; generating on demand does not, which is the property per-leaf scripts existed for. Also notes that --unattended and a generated leaf are the same mechanism seen twice, and that install_config's existing behaviour — keep the user's file when there is no tty — is the conservatism every unattended answer needs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
100 lines
4.7 KiB
Markdown
100 lines
4.7 KiB
Markdown
# 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.
|