| 1 |
# Mountaineer Profiles |
| 2 |
|
| 3 |
This document is the concrete manifest of what each Mountaineer build contains. `MANIFESTO.md` states the principles, `STACK.md` defends each pick. This file is the inventory. |
| 4 |
|
| 5 |
There are two builds, with a strict subset relationship per principle 13: |
| 6 |
|
| 7 |
- **Mountaineer Server** — the foundational system. Headless, service-oriented, everything an operator needs on a VPS, a homelab box, or any machine that does not need a graphical layer. |
| 8 |
- **Mountaineer Sysadmin** — Server plus the Wayland graphical layer and laptop integration. The build a sysadmin installs on the workstation they use every day. |
| 9 |
|
| 10 |
**Sysadmin = Server ∪ Graphical.** No package in Server is omitted from or replaced in Sysadmin. A Sysadmin install on a server is a Server install with idle graphical packages; a Server install on a workstation is a perfectly functional headless system. This is the constraint that keeps the two builds one mental model. |
| 11 |
|
| 12 |
Roles within a profile — `bastion`, `homelab`, `dev-workstation`, etc. — are planned for a later iteration (see the final section) and layer additional package sets and config on top of a chosen profile. They are not separate profiles. |
| 13 |
|
| 14 |
--- |
| 15 |
|
| 16 |
## Mountaineer Server |
| 17 |
|
| 18 |
The full set of packages and configuration overlays Server adds on top of a minimal Alpine installation. |
| 19 |
|
| 20 |
### Packages |
| 21 |
|
| 22 |
Grouped by role for readability. Versions follow Alpine's `linux-lts` release; any package not in Alpine's repositories is not in Mountaineer. |
| 23 |
|
| 24 |
**Foundation** |
| 25 |
|
| 26 |
- `alpine-base`, `alpine-conf` (Alpine baseline) |
| 27 |
- `apk-tools` (package manager — already present) |
| 28 |
- `busybox`, `busybox-suid` (already present; `/bin/sh` stays ash) |
| 29 |
- `openssh-server`, `openssh-client` |
| 30 |
- `chrony` |
| 31 |
- `unbound` |
| 32 |
|
| 33 |
**Init and supervision** |
| 34 |
|
| 35 |
- `s6`, `s6-rc`, `s6-linux-init`, `execline` |
| 36 |
- `mountaineer-s6-services` (Mountaineer-authored: the canonical s6-rc service definitions for every shipped daemon) |
| 37 |
|
| 38 |
**Interactive userland (defaults, not aliases)** |
| 39 |
|
| 40 |
- `nushell` |
| 41 |
- `helix` |
| 42 |
- `zellij` |
| 43 |
- `starship` |
| 44 |
- `ripgrep`, `fd`, `sd`, `bat`, `eza`, `dust`, `procs`, `bottom`, `zoxide`, `lnav` |
| 45 |
|
| 46 |
**Containers** |
| 47 |
|
| 48 |
- `podman`, `buildah`, `crun` |
| 49 |
- `mountaineer-podman-s6` (Mountaineer-authored: s6-rc templates for supervised containers — the quadlet analog) |
| 50 |
|
| 51 |
**Filesystem** |
| 52 |
|
| 53 |
- `zfs`, `zfs-lts`, `zfs-scripts` |
| 54 |
- `zfsbootmenu` |
| 55 |
|
| 56 |
**Networking** |
| 57 |
|
| 58 |
- `ifupdown-ng`, `ifupdown-ng-wireguard` (plugin for wireguard interfaces) |
| 59 |
- `dhcpcd` |
| 60 |
- `nftables` |
| 61 |
|
| 62 |
**Web ingress and TLS** |
| 63 |
|
| 64 |
- `caddy` |
| 65 |
|
| 66 |
**Secrets and backup** |
| 67 |
|
| 68 |
- `age` |
| 69 |
- `sops` |
| 70 |
- `restic` |
| 71 |
|
| 72 |
**Mail (send-only)** |
| 73 |
|
| 74 |
- `msmtp`, `msmtp-mta` (provides `/usr/sbin/sendmail`) |
| 75 |
|
| 76 |
**Log routing** |
| 77 |
|
| 78 |
- `vector` |
| 79 |
|
| 80 |
**Mountaineer tools** |
| 81 |
|
| 82 |
- `mountaineer-sysop` (the `sysop` CLI itself — ratatui frontends, swap-friendly backends, role-based vocabulary) |
| 83 |
- `mountaineer-base-config` (the configuration overlay — see next section) |
| 84 |
- `mountaineer-manifest` (ships `/etc/mountaineer/manifest.toml`) |
| 85 |
|
| 86 |
### Configuration overlay |
| 87 |
|
| 88 |
Mountaineer ships defaults in these files. Each one is heavily commented; reading the file teaches what it does (principle 2). Operators edit these files directly; nothing wraps them. |
| 89 |
|
| 90 |
``` |
| 91 |
/etc/mountaineer/ |
| 92 |
manifest.toml # role → package mapping, source of truth for `sysop info` |
| 93 |
profile # "server" or "sysadmin" |
| 94 |
|
| 95 |
/etc/ssh/sshd_config # hardened: no password auth, no root, modern KEX/cipher/MAC |
| 96 |
/etc/chrony/chrony.conf # iburst, public NTP pool, documented swap pattern |
| 97 |
/etc/unbound/unbound.conf # local recursive resolver, DoT upstream, DNSSEC |
| 98 |
/etc/caddy/Caddyfile # commented example: reverse proxy, static, podman backend |
| 99 |
/etc/nftables.d/ # default-deny, ICMP/ICMPv6 filtering, SSH rate-limited |
| 100 |
/etc/vector/vector.toml # routes container + system logs to /var/log/{containers,system} |
| 101 |
/etc/msmtprc # configured-but-failing default (operator must point at SMTP) |
| 102 |
/etc/network/interfaces.d/ # example stanzas for static, dhcp, bridge, wireguard |
| 103 |
/etc/restic/ # stub repository config; commented scheduled-job example |
| 104 |
|
| 105 |
/etc/s6/ # s6-rc compiled service database |
| 106 |
/etc/s6-rc.d/ # Mountaineer-canonical service definitions (source) |
| 107 |
|
| 108 |
/etc/nushell/ # global nushell environment + default aliases |
| 109 |
/etc/profile.d/ # interactive shell setup (PATH, zoxide init, starship init) |
| 110 |
/etc/helix/ # global helix config (themes, LSP defaults) |
| 111 |
|
| 112 |
/etc/zfs/ |
| 113 |
zpool.cache # generated at install |
| 114 |
mountaineer-arc.conf # tuned zfs_arc_max default (sized at install) |
| 115 |
``` |
| 116 |
|
| 117 |
### Default services (running on a fresh Server install) |
| 118 |
|
| 119 |
Supervised by s6-rc: |
| 120 |
|
| 121 |
- `sshd` |
| 122 |
- `chronyd` |
| 123 |
- `unbound` |
| 124 |
- `nftables-load` (one-shot at boot, loads ruleset) |
| 125 |
- `caddy` (off by default; enable via `sysop service enable caddy` once Caddyfile is configured) |
| 126 |
- `vector` |
| 127 |
|
| 128 |
Not running by default; available to enable: |
| 129 |
|
| 130 |
- `podman` (lazy — no daemon, but `sysop podman-s6` templates supervise individual containers as needed) |
| 131 |
- `restic-backup` (one-shot scheduled; off until `/etc/restic/` is configured) |
| 132 |
|
| 133 |
### Explicitly not in Server |
| 134 |
|
| 135 |
- No graphical anything (no Wayland, no X, no compositor, no fonts beyond terminal essentials) |
| 136 |
- No web browser |
| 137 |
- No NetworkManager, no systemd-anything, no Docker, no OpenRC |
| 138 |
- No full MTA (msmtp is send-only) |
| 139 |
- No bash as interactive shell (`apk add bash` if you insist) |
| 140 |
|
| 141 |
--- |
| 142 |
|
| 143 |
## Mountaineer Sysadmin |
| 144 |
|
| 145 |
Sysadmin = Server + the additions below. Everything in the Server section above applies; this section is purely the delta. |
| 146 |
|
| 147 |
### Additional packages |
| 148 |
|
| 149 |
**Wayland and compositor** |
| 150 |
|
| 151 |
- `river` |
| 152 |
- `wlroots` (pulled in by river) |
| 153 |
- `xdg-desktop-portal`, `xdg-desktop-portal-wlr` (for application portals — screen share, file picker) |
| 154 |
|
| 155 |
**Graphical userland** |
| 156 |
|
| 157 |
- `foot` (terminal) |
| 158 |
- `yambar` (status bar) |
| 159 |
- `fuzzel` (launcher) |
| 160 |
- `cliphist` (clipboard history) |
| 161 |
- `fnott` (notifications) |
| 162 |
- `grim`, `slurp`, `swappy` (screenshots and annotation) |
| 163 |
- `swaylock`, `swayidle`, `swaybg` (lock, idle, background) |
| 164 |
- `wl-clipboard` (clipboard CLI integration) |
| 165 |
|
| 166 |
**Audio** |
| 167 |
|
| 168 |
- `pipewire`, `pipewire-pulse`, `pipewire-jack` |
| 169 |
- `wireplumber` |
| 170 |
|
| 171 |
**Wireless and laptop integration** |
| 172 |
|
| 173 |
- `iwd` |
| 174 |
- `seatd` |
| 175 |
- `brightnessctl` |
| 176 |
- `tlp`, `tlp-rdw` |
| 177 |
|
| 178 |
**Browser fallback** |
| 179 |
|
| 180 |
- `w3m` (emergency text browser only; first-boot install flow prompts for a real browser) |
| 181 |
|
| 182 |
**Fonts** |
| 183 |
|
| 184 |
- `font-jetbrains-mono-nerd` (terminal, editor) |
| 185 |
- `font-inter` (UI text) |
| 186 |
- `font-noto`, `font-noto-emoji` (broad Unicode coverage) |
| 187 |
|
| 188 |
### Additional configuration overlay |
| 189 |
|
| 190 |
``` |
| 191 |
/etc/mountaineer/ |
| 192 |
profile # "sysadmin" |
| 193 |
|
| 194 |
~/.config/river/init # shipped default; tiling layout, keybinds documented inline |
| 195 |
~/.config/foot/foot.ini # font, colors, scrollback |
| 196 |
~/.config/yambar/config.yml # the ~30-line default status bar |
| 197 |
~/.config/fuzzel/fuzzel.ini # launcher styling |
| 198 |
~/.config/fnott/fnott.ini # notification styling |
| 199 |
~/.config/swaylock/config # lock screen styling |
| 200 |
~/.config/swayidle/config # dim, lock, suspend timeouts |
| 201 |
|
| 202 |
/etc/iwd/main.conf # iwd defaults |
| 203 |
/etc/seatd.conf |
| 204 |
/etc/tlp.conf # battery-favoring tuned defaults |
| 205 |
``` |
| 206 |
|
| 207 |
Per-user configs ship as `/etc/skel/.config/...` and are copied into new users' home directories at account creation. |
| 208 |
|
| 209 |
### Additional services (Sysadmin-only) |
| 210 |
|
| 211 |
Supervised by s6-rc (system) or river's session manager (user): |
| 212 |
|
| 213 |
- `seatd` |
| 214 |
- `iwd` |
| 215 |
- `pipewire`, `wireplumber` (user session) |
| 216 |
- `tlp` |
| 217 |
|
| 218 |
### First-boot Sysadmin flow |
| 219 |
|
| 220 |
On first login to a Sysadmin install, the Mountaineer setup script: |
| 221 |
|
| 222 |
1. Prompts for hostname, user account, SSH key import (if any). |
| 223 |
2. Asks if the user wants to import a wifi profile (or defers to `sysop wifi` later). |
| 224 |
3. Presents the `apk` browser packages and runs `apk add` on the user's choice (or "skip — use w3m for now"). |
| 225 |
4. Drops the user into a river session with the default layout. |
| 226 |
|
| 227 |
Subsequent runs of any of these steps are available as `sysop` subcommands. |
| 228 |
|
| 229 |
### Explicitly not in Sysadmin |
| 230 |
|
| 231 |
Sysadmin adds exactly the packages above and nothing else. Anything not in this delta is either in Server (and therefore in Sysadmin) or not in Mountaineer at all. |
| 232 |
|
| 233 |
In particular, Sysadmin does **not** add: |
| 234 |
|
| 235 |
- A display manager. River is launched from a TTY login via a small shell script (`/usr/local/bin/start-river`) — no greetd, no SDDM, no gdm. The login prompt is the kernel's, the way it has always been. |
| 236 |
- A file manager. The terminal is the file manager. `yazi` (or `ranger`, or any other TUI file manager) is one `apk add` away for operators who want one. |
| 237 |
- A mail client. `aerc` is the natural Mountaineer-flavored pick if we ship one in a later iteration; for now, operators install what they want. |
| 238 |
- An office suite, a media player, a chat client, a calendar, or any of the consumer-desktop expectations. Sysadmin is not a consumer desktop (manifesto: "What this isn't"). |
| 239 |
|
| 240 |
--- |
| 241 |
|
| 242 |
## Roles (planned, v0.2+) |
| 243 |
|
| 244 |
Roles are *additive overlays* on top of a chosen profile. They are not separate profiles, do not break the subset relationship, and exist to bundle common operator setups so a fresh Mountaineer machine can become "the bastion" or "the homelab box" with one command. |
| 245 |
|
| 246 |
Each role is a small `sysop` subcommand that installs a specific package set and drops in a specific configuration overlay. The mechanism is identical to a manual `apk add ... ; cp config files` sequence — the role just curates the list. |
| 247 |
|
| 248 |
Planned roles (none of these are built yet; this list anchors the design conversation): |
| 249 |
|
| 250 |
- **`bastion`** — hardened SSH jump host. Tightens sshd, installs fail2ban-equivalent (probably `sshguard`), configures audit logging, enables port-knocking via nftables sets. Implies Server. |
| 251 |
- **`homelab`** — single-machine self-hosted services host. Adds caddy as default-on, ships an example Caddyfile for `*.home.arpa` routing, pre-seeds a podman-s6 setup for common containers (gitea, nextcloud, etc., user picks at role-init time). |
| 252 |
- **`dev-workstation`** — Sysadmin plus language toolchains for the operator's stated languages and development tooling (`git`, `gh`, `direnv`, `mise` or equivalent). The `docker → podman` shell alias is already shipped system-wide; the role does not redo it. Implies Sysadmin. |
| 253 |
- **`vps`** — Server with a focus on minimal install size, default-deny aggressively tuned for an internet-exposed machine, wireguard pre-configured for inbound from a known endpoint. Implies Server. |
| 254 |
- **`relay-mail`** — Server with msmtp replaced by OpenSMTPD configured as an outbound relay (the documented upgrade from msmtp). Implies Server. |
| 255 |
|
| 256 |
The role mechanism deliberately mirrors the profile mechanism: additive, documented, no parallel forks, swappable underlying tools per principle 14. A role that adds Caddy and one that adds OpenSMTPD compose; a role that *replaces* an init system does not exist and never will. |
| 257 |
|
| 258 |
--- |
| 259 |
|
| 260 |
## How profiles are built |
| 261 |
|
| 262 |
(Implementation notes — separable from the inventory above.) |
| 263 |
|
| 264 |
Both profiles ship as `apkovl` overlays applied to Alpine's minimal install image, plus an ISO variant that runs the Mountaineer installer as its only first-boot action. The installer handles: |
| 265 |
|
| 266 |
1. Disk partitioning and zpool creation (canonical dataset layout). |
| 267 |
2. Base Alpine bootstrap via `apk add --root /mnt`. |
| 268 |
3. Mountaineer package set install (Server set always; Sysadmin set additionally if the install was started from the Sysadmin ISO). |
| 269 |
4. Configuration overlay deployment. |
| 270 |
5. s6-rc service database compilation and enablement. |
| 271 |
6. zfsbootmenu installation and the initial snapshot creation. |
| 272 |
7. First user account creation and the first-boot flow (Sysadmin only). |
| 273 |
|
| 274 |
The package sets are maintained as plain text files in the Mountaineer repository — `profiles/server.world`, `profiles/sysadmin.world` — and the strict-subset invariant is checked in CI: every package in `server.world` must appear in `sysadmin.world`, and CI fails otherwise. The invariant is enforced mechanically, not by discipline. |
| 275 |
|