Skip to main content

max / mountaineer

7.7 KB · 196 lines History Blame Raw
1 # mountaineer-build
2
3 The implementation side of Mountaineer. Spec lives in `../mountaineer/`;
4 this repo builds artifacts (apkovl, ISO) from that spec.
5
6 **Status: MVP, installer end-to-end in QEMU.** 660 MB ISO at
7 `out/mountaineer-server-v3.23-x86_64.iso` boots in UEFI QEMU. The
8 installer runs all 17 steps against a clean disk — ZFS pool, dataset
9 layout, base packages, apkovl unpack, ZBM (zfsbootmenu) install,
10 per-host identity, finalize/reboot — and the installed disk boots
11 standalone via ZBM. Ready for Framework 13 hardware verification.
12
13 Build/install history and rediscovered traps live in
14 [`build_notes.md`]build_notes.md. Open work tracked in
15 `../mountaineer/todo.md`.
16
17 Anything marked `TODO-verify` or `TODO` in the source needs real
18 hardware testing or upstream-vendoring to commit to a final shape.
19
20 ## Scope (MVP)
21
22 What the MVP aims for, in plain terms: a Mountaineer Server ISO that boots
23 in a UEFI VM, runs an installer, lays down ZFS-on-root, unpacks the
24 Mountaineer apkovl, reboots, and passes every step of the first-boot route
25 from the spec.
26
27 What is deliberately **not** in MVP scope:
28
29 - **Sysadmin profile / graphical layer.** Only Server is built. Sysadmin
30 comes when Server is stable.
31 - **The `sysop` CLI.** Routes use canonical tools directly (`nft -f`,
32 `s6-rc -u change`, etc.) until `sysop` lands.
33 - **Reproducible builds, bit-for-bit.** The apkovl builder is
34 reproducible-best-effort (same source + same builder → same bytes).
35 Bit-identical across builder versions is a v0.2 goal.
36 - **Caddy / Nebula / podman / Vector running by default.** They're
37 installed but not started; the operator configures and starts them per
38 the routes in `../mountaineer/ROUTES.md`.
39 - **Mountaineer overlay apk repository.** Until we have one, everything
40 comes from Alpine repos. Mountaineer-authored packages (`sysop` when it
41 ships, the installer eventually) are TBD.
42
43 ## Layout
44
45 ```
46 mountaineer-build/
47 ├── apkovl-src/ # declarative source for the apkovl
48 │ ├── common/ # files shipped in every Mountaineer build
49 │ ├── manifest.toml # per-profile metadata (sources, version)
50 │ └── README.md # what lives where, how to verify reproducibility
51 ├── tools/
52 │ └── build-apkovl.sh # apkovl-src/ → reproducible tarball
53 ├── mkimg/
54 │ └── mkimg.mountaineer-server.sh # mkimage profile (drop into aports/scripts/)
55 ├── installer/
56 │ └── install.sh # 17-step shell+dialog installer
57 ├── build.sh # top-level: apkovl + ISO
58 └── README.md # this file
59 ```
60
61 ## Building
62
63 ### Quickest path: Alpine host
64
65 On any host running Alpine (physical, VM, container), in this directory:
66
67 ```sh
68 apk add alpine-sdk abuild xorriso squashfs-tools mkinitfs grub-efi git tar
69 ./build.sh
70 ```
71
72 Output:
73
74 - `out/mountaineer-server-<version>.apkovl.tar.gz` — the apkovl
75 - `out/mountaineer-server-*.iso` — the bootable installer ISO
76
77 The first run clones a shallow copy of Alpine's `aports` repo into
78 `.aports/` (≈100 MiB). Subsequent runs reuse it.
79
80 ### From a Mac (the dev loop)
81
82 The canonical Mac iteration loop uses qemu to run a long-lived Alpine
83 builder VM. See `tools/qemu/README.md` for the full walkthrough; the
84 short version, once `brew install qemu` is done:
85
86 ```sh
87 ./tools/qemu/vm-up.sh --provision # first time only: download ISO + setup-alpine
88 ./tools/qemu/vm-seed-builder.sh # install build deps in the builder VM
89 ./tools/qemu/vm-build.sh # rsync repo + run ./build.sh inside builder
90 ./tools/qemu/vm-test-install.sh # boot the new ISO in a disposable target VM
91 ```
92
93 Apple Silicon caveat: x86_64 guests run via TCG emulation (no HVF for
94 cross-arch), so a full build cycle takes ~10-20 minutes. On Intel Macs
95 HVF makes it ~2-3 minutes.
96
97 ### From a non-Alpine Linux workstation
98
99 Run the build inside an Alpine container via podman:
100
101 ```sh
102 podman run --rm -it --privileged \
103 -v "$PWD":/build \
104 -w /build \
105 docker.io/alpine:edge \
106 sh -c 'apk add alpine-sdk abuild xorriso squashfs-tools mkinitfs grub-efi git tar && ./build.sh'
107 ```
108
109 `--privileged` is required for `mkfs`/`mksquashfs`/loop-device work inside
110 the container. Outputs land on the host via the bind mount.
111
112 We do not use Nix, devbox, or any other ecosystem-replacing tooling.
113 Mountaineer's build environment is apk-native (see
114 `../mountaineer/BUILD-TOOLS.md`).
115
116 ### Just the apkovl
117
118 `./tools/build-apkovl.sh server` produces only the apkovl tarball (no ISO).
119 Useful for iterating on `apkovl-src/` content without rebuilding the ISO
120 every time. Requires GNU tar; the script auto-detects and prints the fix
121 if it's missing.
122
123 ## Testing the installer
124
125 The installer's `--dry-run` flag walks every prompt and prints every
126 destructive command instead of executing it. Run it inside a VM that has
127 `dialog` installed to exercise the prompt flow without losing data:
128
129 ```sh
130 ./installer/install.sh --dry-run
131 ```
132
133 End-to-end testing requires a fresh UEFI VM with a blank disk. The
134 canonical loop:
135
136 1. `./build.sh` to produce the ISO.
137 2. Boot the ISO in a UEFI VM (qemu, UTM, VMware, etc.) with at least
138 2 GiB RAM and a 20 GiB blank disk.
139 3. Log in to the live environment, run `mountaineer-install` (or the path
140 to `install.sh` if not yet installed in PATH on the live image).
141 4. Walk through the prompts; complete the install; reboot.
142 5. Verify the installed system passes the first-boot route in
143 `../mountaineer/ROUTES.md`.
144
145 The v0.1 dogfood gate is eight criteria documented in
146 `../mountaineer/INSTALL.md`.
147
148 ## Known gaps (recorded here so we don't lose them)
149
150 These are the load-bearing things still TODO:
151
152 - **zfsbootmenu install path.** Installer step 12 has a placeholder. Need
153 to decide: apk-package ZBM if Alpine ships it, or fetch upstream
154 release signed by the ZBM project. Affects build reproducibility and
155 the signing-trust story.
156 - **mkimg profile bootloader.** The profile script declares
157 `boot_efi="grub-efi"` for the live ISO; need to verify mkimage's
158 expected token names against the current aports tree.
159 - **OpenRC vs s6-rc on the live ISO.** The installed system uses s6-rc.
160 The live ISO inherits Alpine's default (OpenRC) for now; the installer
161 itself doesn't care which init it ran under. Migrating the live ISO to
162 s6-rc is a v0.2 cleanup.
163 - **Vector packaging on Alpine.** The world file lists `vector`;
164 TODO-verify the package name and repo placement before the first build
165 is attempted.
166 - **Non-interactive install config schema.** `--config <file>` is in the
167 spec but the schema doesn't exist yet.
168 - **Installer log routing.** Right now the installer logs to stderr;
169 decide whether to also tee to tmpfs and copy into the installed root
170 before reboot for post-mortem debugging.
171 - **Signing.** ISO and apkovl signing keys, lifecycle, where they live.
172 Open in `../mountaineer/INSTALL.md` and `../mountaineer/todo.md`.
173
174 When any of these get resolved, the corresponding `TODO-verify` or
175 `TODO` comment in the source code gets removed in the same commit that
176 resolves it.
177
178 ## Relationship to the spec
179
180 This repo implements `../mountaineer/`. When the two disagree:
181
182 - If the spec is right and the implementation is wrong, fix the code.
183 - If the implementation is right and the spec is wrong, fix the spec —
184 but only after the implementation has actually been tested. A spec
185 changed to match unverified code is worse than a spec at odds with it.
186
187 The spec docs that matter most for implementation work:
188
189 - `STACK.md` — what packages, what configs, what defaults.
190 - `INSTALL.md` — disk layout, dataset hierarchy, ARC sizing, installer
191 flow, dogfood gate.
192 - `ROUTES.md` — the end-state UX the installed system must support.
193 - `BUILD-TOOLS.md` — the toolchain orientation.
194 - `MANIFESTO.md` — the principles to consult when a question doesn't
195 have an obvious answer.
196