feat(installer): self-contained macOS SD-card setup CLI
A ratatui TUI that provisions the SD card for a pre-flashed Typoena: Preflight → Configure → SD card → Done. Clones the notes repo onto the card with system git (PAT in http.extraHeader, never in origin), seeds .typoena.toml, writes typoena.conf, strips AppleDouble, ejects. An already-provisioned card is handled by a y-confirmed wipe-and-reclone that only ever removes repo/ + the dirty journal. This is the target of typoena.dev's `curl … | sh`. Also records firmware auto-update options in docs/macroplan.md (v1.x).
This commit is contained in:
95
installer/DESIGN.md
Normal file
95
installer/DESIGN.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Typoena installer — design
|
||||
|
||||
A self-contained macOS CLI (ratatui TUI) that prepares an SD card so a
|
||||
**pre-flashed** Typoena is ready to use the moment the card goes in. The public
|
||||
entry point is the one-liner on typoena.dev:
|
||||
|
||||
curl -fsSL https://typoena.dev/install.sh | sh
|
||||
|
||||
`install.sh` downloads this prebuilt binary; the binary does the rest. The user
|
||||
needs **no repo checkout and no Rust toolchain** — just the card.
|
||||
|
||||
## Decisions (2026-07-14)
|
||||
|
||||
- **Self-contained end-user tool.** No `just`, no typewriter checkout. The
|
||||
binary bundles what it needs (config templates, snippet catalog). The proven
|
||||
`just` bash (`firmware/justfile`) is the *reference spec* for the safety
|
||||
behaviours, ported to Rust — not shelled out to.
|
||||
- **The installer never flashes.** Devices ship **pre-flashed from
|
||||
manufacturing**; setup is SD-card-only. Firmware field updates
|
||||
(auto-update) are a **device/roadmap** concern, not the installer's — see
|
||||
[`docs/macroplan.md`](../docs/macroplan.md) (v1.x note).
|
||||
- **The card's repo is a fresh `git clone` from the remote** (HTTPS + PAT),
|
||||
written straight onto the card. There is no local source clone to mirror, so
|
||||
none of the rsync machinery applies: **no `--ff-only` refresh, no `.gitignore`
|
||||
excludes, no repack** — a fresh clone already contains only tracked files and
|
||||
is a single pack, and its origin is already the HTTPS URL we cloned from.
|
||||
- **Lives in `typewriter/installer/`**, tracked in the firmware repo so it
|
||||
versions in lockstep with the config templates + snippet catalog it ships.
|
||||
|
||||
## Phase pipeline (the wizard)
|
||||
|
||||
1. **Preflight** — a mounted card + git present. Advisory; warnings don't block.
|
||||
2. **Configure** — collect Wi-Fi SSID/pass, git remote, GitHub user, PAT, and
|
||||
commit identity. Pre-fill via the derive ladder (below).
|
||||
3. **SD card** — pick the card (refuse on ambiguity), `git clone` the remote
|
||||
onto `/repo`, seed `.typoena.toml` + snippets if absent, write
|
||||
`typoena.conf`, strip `._*`, eject.
|
||||
4. **Done** — "put the card in your Typoena and power on."
|
||||
|
||||
## Safety behaviours to keep (from `firmware/justfile` — do NOT regress)
|
||||
|
||||
- **Card-ambiguity refusal** — never guess when >1 removable volume; a wrong
|
||||
guess lets a write hit the wrong disk. Refuse and ask.
|
||||
- **`.typoena-dirty` guard** — refuse to overwrite a card that carries
|
||||
unpublished device edits; offer backup-and-discard.
|
||||
- **AppleDouble `._*`** — `dot_clean` before eject; `._pack-*.idx` corrupts the
|
||||
pack scan (Mac git *and* device libgit2).
|
||||
- **PAT never derived** — always typed; fine-grained, `contents:write` on the
|
||||
one repo; plaintext on FAT means physical custody is the control. The clone
|
||||
uses the PAT so private notes repos work.
|
||||
|
||||
_Dropped vs. the old `just load` (now moot with clone-from-remote): rsync
|
||||
mirror, `--ff-only` source refresh, `.gitignore` exclude list, `git repack -ad`._
|
||||
|
||||
## Config derive ladder (Configure step)
|
||||
|
||||
Each value: explicit input → derived from this Mac → prompt.
|
||||
`author` ← `git config user.{name,email}` · `gh_user` ← `gh api user` ·
|
||||
`ssid` ← active Wi-Fi (`networksetup`) · `wifi_pass` ← Keychain (on ^K, may
|
||||
prompt macOS) · `remote`, `pat` ← typed (PAT never derived).
|
||||
|
||||
## Architecture / crates
|
||||
|
||||
- `ratatui` + its crossterm backend — TUI.
|
||||
- `git2` — clone the remote onto the card, confirm origin. _[SD slice]_
|
||||
- Config templates + snippet catalog embedded via `include_str!`
|
||||
(self-contained).
|
||||
|
||||
## Open items (not blocking the current slices)
|
||||
|
||||
- **Hosting** — where the CLI binary lives for the `curl | sh` download (Gitea
|
||||
release vs typoena.dev static asset). Dev runs via `cargo run` meanwhile.
|
||||
- **Non-macOS** — Linux/Windows later; slice work is macOS-first.
|
||||
- **Clone target** — cloning ~hundreds of MB directly onto FAT via a reader;
|
||||
measure, and fall back to clone-to-temp-then-copy if it's too slow.
|
||||
- **Re-provision** — DONE for the destructive case: an existing card is handled
|
||||
by an explicit **wipe-and-reclone** (`y`-confirmed screen showing origin +
|
||||
HEAD + unpublished-edit count; removes only `repo/` + the dirty journal, then
|
||||
clones fresh). Follow-ups: a config-only rewrite that rotates the PAT /
|
||||
switches Wi-Fi *without* recloning (like `just provision`), and backing up
|
||||
`.typoena-dirty` edits before wiping instead of only warning.
|
||||
|
||||
## Slice plan
|
||||
|
||||
1. **App shell + Preflight** — DONE 2026-07-14. Branded wizard; card + git
|
||||
detection; `--check` headless mode.
|
||||
2. **Configure** — DONE 2026-07-14. Form + derive ladder, masked secrets,
|
||||
Keychain fill, required-field validation.
|
||||
3. **SD card** — DONE 2026-07-14 (fresh-card path). Pick card (boot disk
|
||||
excluded) → `git clone` onto it (single pack, clean HTTPS origin) → seed
|
||||
`.typoena.toml` → write `typoena.conf` → strip `._*` → eject; the long clone
|
||||
runs on a worker thread streaming progress. Verified: card detection on real
|
||||
hardware and clone + seed + conf via `--list-cards` / `--dry-run-sd`. Full
|
||||
interactive run + real write/eject await a blank card + a TTY.
|
||||
4. **install.sh + release/hosting** — checksums, polish.
|
||||
Reference in New Issue
Block a user