max / alloy
3 files changed,
+76 insertions,
-38 deletions
| @@ -596,33 +596,48 @@ | |||
| 596 | 596 | # treated as a package list — the builder is not a package manager, and | |
| 597 | 597 | # the gate lives here so it holds even when the TUI is bypassed. | |
| 598 | 598 | # | |
| 599 | - | # THE DEFAULT IS WHAT COMPILES THE SHIPPED STACK, ruled by Max 2026-08-17: | |
| 600 | - | # someone who wants to rebuild a program Alloy ships should not have to | |
| 601 | - | # install a toolchain first. Measured against the client image rather than | |
| 602 | - | # assumed, by reading each shipped binary for a Go build id or a rustc | |
| 603 | - | # path: | |
| 599 | + | # THE DEFAULT IS WHAT THE IMAGE ITSELF REQUIRES, ruled by Max 2026-08-17. | |
| 600 | + | # So `rust`, alone. Rust is not a preference here: sandod shells out to | |
| 601 | + | # `cargo build --release` and refuses to compile anywhere but its | |
| 602 | + | # configured build host, so an image that quietly stopped carrying cargo | |
| 603 | + | # would break Sando rather than merely save disk. Nothing else in the | |
| 604 | + | # image makes that claim. | |
| 605 | + | # | |
| 606 | + | # GO WAS IN THIS DEFAULT AND CAME BACK OUT THE SAME DAY. Recording both | |
| 607 | + | # rulings, because the second reverses the first and a bare `rust` reads | |
| 608 | + | # like the question was never asked. | |
| 609 | + | # | |
| 610 | + | # The first rule was "the default is what compiles the shipped stack": | |
| 611 | + | # someone rebuilding a program Alloy ships should not have to install a | |
| 612 | + | # toolchain first. It was measured rather than assumed, by reading each | |
| 613 | + | # shipped binary for a Go build id or a rustc path: | |
| 604 | 614 | # | |
| 605 | 615 | # Rust hx yazi nu starship zoxide btm dua rg fd bat eza satty, plus | |
| 606 | 616 | # alloy and shop themselves. 14. | |
| 607 | 617 | # Go direnv fzf gopass syncthing restic tailscale cliphist. 7. | |
| 608 | 618 | # C sway mako swaylock, and the hardware-health group. | |
| 609 | 619 | # | |
| 610 | - | # So `rust,go`. Go is the cheaper half at 95 MiB inbound against rust's | |
| 611 | - | # 176, and it covers the whole continuity tier — syncthing, restic and | |
| 612 | - | # tailscale are the programs most likely to be wanted a version ahead of | |
| 613 | - | # what Fedora carries. | |
| 620 | + | # What killed it is that the rule was never applied evenly. It already | |
| 621 | + | # stopped short of the C tier — sway and mako want meson, ninja, | |
| 622 | + | # wayland-protocols and a spread of -devel packages that no language | |
| 623 | + | # toggle models — so "you can rebuild what we ship" was true for two | |
| 624 | + | # thirds of the stack and quietly false for the rest. A rule with a hole | |
| 625 | + | # that size is a preference. Then the exclusive-closure pass priced it: | |
| 626 | + | # Go's marginal cost is 356 MB, six percent of the image, against seven | |
| 627 | + | # programs nobody has rebuilt. Nothing in ~/Code is Go either — a full | |
| 628 | + | # find turns up four tree-sitter fixtures in an archived repo and no | |
| 629 | + | # go.mod — so the toolchain was not earning its place from the build-host | |
| 630 | + | # side either. | |
| 614 | 631 | # | |
| 615 | - | # The C tier is deliberately NOT covered and the rule stops short of it. | |
| 616 | - | # `gcc` arrives with rust, so a single-file C program builds, but sway and | |
| 617 | - | # mako want meson, ninja, wayland-protocols and a spread of -devel | |
| 618 | - | # packages that no language toggle models. Naming that here rather than | |
| 619 | - | # pretending the rule is uniform: the three C programs in the stack are | |
| 620 | - | # the ones a user rebuilds by layering a buildroot, not by flipping a | |
| 621 | - | # toggle in the builder. | |
| 632 | + | # THE COST, STATED SO IT IS NOT DISCOVERED: rebuilding syncthing, restic, | |
| 633 | + | # tailscale, gopass, direnv, fzf or cliphist from source on a stock image | |
| 634 | + | # now needs `LANGS=rust,go` at mint time, or a toolchain in $HOME. That is | |
| 635 | + | # the trade, and it is reversible at zero cost, which most things here are | |
| 636 | + | # not. | |
| 622 | 637 | # | |
| 623 | - | # python and zig stay off for the same reason go is now on: nothing | |
| 624 | - | # shipped is written in either. | |
| 625 | - | ARG LANGS=rust,go | |
| 638 | + | # python and zig stay off for the reason go now is: nothing in the image | |
| 639 | + | # requires them. | |
| 640 | + | ARG LANGS=rust | |
| 626 | 641 | ||
| 627 | 642 | # TRIM is the one builder choice about the *base* rather than about Alloy. | |
| 628 | 643 | # fedora-bootc is a general-purpose server base, and three of the things it | |
| @@ -1731,6 +1746,20 @@ | |||
| 1731 | 1746 | || { echo "$(basename "$theme") survived and skel does not name it; the prune in the package block stopped running and the image is carrying ~12-27 MB per unused theme" >&2; exit 1; }; \ | |
| 1732 | 1747 | done; \ | |
| 1733 | 1748 | echo "cursor: $(echo "$names" | tr '\n' ' ')kept, every other Bibata theme pruned"; \ | |
| 1749 | + | else \ | |
| 1750 | + | # The asserting else, same rule as the package block: a check that runs | |
| 1751 | + | # on one profile and silently passes on the other is not a check. The | |
| 1752 | + | # server claim is the stronger one — bibata-cursor-theme is named only | |
| 1753 | + | # inside the client branch, so a server image should carry no Bibata | |
| 1754 | + | # directory at all. If one is here, the graphical group leaked across | |
| 1755 | + | # the profile split and 179 MB of cursors came with it, on an image with | |
| 1756 | + | # no session to draw a cursor in. | |
| 1757 | + | for theme in /usr/share/icons/Bibata-*; do \ | |
| 1758 | + | [ -d "$theme" ] || continue; \ | |
| 1759 | + | echo "$(basename "$theme") is in the server image; bibata-cursor-theme is named only in the client branch, so the profile split leaked" >&2; \ | |
| 1760 | + | exit 1; \ | |
| 1761 | + | done; \ | |
| 1762 | + | echo "cursor: none, as the server profile draws none"; \ | |
| 1734 | 1763 | fi | |
| 1735 | 1764 | ||
| 1736 | 1765 | # ===================================================================== |
| @@ -116,6 +116,10 @@ | |||
| 116 | 116 | `BROWSER=helium LANGS=rust,go TRIM=unused`. It was 6.19 GB as an image and 5.26 GB | |
| 117 | 117 | deployed, and the 0.9 GB between those two numbers is the subject of half this section. | |
| 118 | 118 | ||
| 119 | + | Every figure below is from that build, so they describe an image built with Go in it. | |
| 120 | + | The default became `LANGS=rust` later the same day, which takes the language layer down | |
| 121 | + | with it; the numbers are left as measured rather than adjusted on paper. | |
| 122 | + | ||
| 119 | 123 | Two sizes, and they answer different questions. **Image size** is the sum of the layers: | |
| 120 | 124 | what a registry stores and what `bootc upgrade` moves. **Deployed size** is the final | |
| 121 | 125 | filesystem: what the disk holds and what the ISO squashes. A file written in one layer | |
| @@ -128,7 +132,7 @@ | |||
| 128 | 132 | | Size | Layer | Why it is that size | | |
| 129 | 133 | |------|-------|---------------------| | |
| 130 | 134 | | 1.44 GB | sway, `xdg-desktop-portal{,-gtk,-wlr}`, the session | The product. This is the desktop. | | |
| 131 | - | | 778 MB | `LANGS=rust,go` | Chosen, and argued at `ARG LANGS`. Go is 230 MB of it. | | |
| 135 | + | | 778 MB | `LANGS=rust,go` | As measured. Go is 230 MB of this layer, and 356 MB by exclusive closure once the packages only it pulls in are counted; that gap is the two questions the measurements answer, not a disagreement. Go left the default on 2026-08-17, so a stock image builds this layer at `LANGS=rust`. Argued at `ARG LANGS`. | | |
| 132 | 136 | | 674 MB | `helium-browser-bin` | The product, and the one stack pick Alloy declines to make. | | |
| 133 | 137 | | 663 MB | Base package list | The stack, per [STACK.md](STACK.md). | | |
| 134 | 138 | | 170 MB | initramfs + `rpmostree-unpackaged-content` | Bootable image. | |
| @@ -279,10 +279,15 @@ | |||
| 279 | 279 | /// Rust's figure is the Containerfile's own measurement, and it is the | |
| 280 | 280 | /// reason this row exists: 610 MiB is an order of magnitude above the | |
| 281 | 281 | /// hardware-health group and by a distance the largest thing in the image. | |
| 282 | + | /// | |
| 283 | + | /// Go's is its marginal cost from the 2026-08-17 exclusive-closure pass — | |
| 284 | + | /// what turning this row on actually adds, rather than what `golang` | |
| 285 | + | /// declares. It is priced here because it is off by default now, so the | |
| 286 | + | /// number is what someone is deciding to spend. | |
| 282 | 287 | const fn label(self) -> &'static str { | |
| 283 | 288 | match self { | |
| 284 | 289 | Lang::Rust => "rust (610 MiB; needed for the build-host role)", | |
| 285 | - | Lang::Go => "go", | |
| 290 | + | Lang::Go => "go (356 MB; rebuilds syncthing, restic, tailscale)", | |
| 286 | 291 | Lang::Python => "python", | |
| 287 | 292 | Lang::Zig => "zig", | |
| 288 | 293 | } | |
| @@ -410,19 +415,19 @@ | |||
| 410 | 415 | Self { | |
| 411 | 416 | profile: Profile::default(), | |
| 412 | 417 | browser: Browser::default(), | |
| 413 | - | // The default is whatever compiles the stack Alloy ships (Max, | |
| 414 | - | // 2026-08-17): someone rebuilding a program that came with the | |
| 415 | - | // image should not have to install a toolchain to do it. Measured | |
| 416 | - | // off the client image, 14 shipped binaries are Rust (helix, yazi, | |
| 417 | - | // nushell, starship, the whole CLI tier, plus alloy and shop) and 7 | |
| 418 | - | // are Go (syncthing, restic, tailscale, gopass, direnv, fzf, | |
| 419 | - | // cliphist), so both are on and python and zig are not. | |
| 418 | + | // The default is what the image itself requires (Max, | |
| 419 | + | // 2026-08-17), which is Rust and nothing else: sandod refuses to | |
| 420 | + | // compile anywhere but its configured host, so an image that | |
| 421 | + | // silently stopped being able to build Rust would break Sando | |
| 422 | + | // rather than merely save disk. | |
| 420 | 423 | // | |
| 421 | - | // Rust also carries the build-host role, which is a hard | |
| 422 | - | // requirement on its own: sandod refuses to compile anywhere but | |
| 423 | - | // its configured host, so an image that silently stopped being able | |
| 424 | - | // to build Rust would break Sando rather than merely save disk. | |
| 425 | - | langs: BTreeSet::from([Lang::Rust, Lang::Go]), | |
| 424 | + | // Go was in this default earlier the same day, under the rule "the | |
| 425 | + | // default is what compiles the shipped stack" — 7 of the shipped | |
| 426 | + | // binaries are Go (syncthing, restic, tailscale, gopass, direnv, | |
| 427 | + | // fzf, cliphist). That rule already stopped short of the C tier, so | |
| 428 | + | // it was two-thirds true, and Go's marginal cost measured 356 MB. | |
| 429 | + | // Reversed. `ARG LANGS` in the Containerfile carries both rulings. | |
| 430 | + | langs: BTreeSet::from([Lang::Rust]), | |
| 426 | 431 | hostname: String::new(), | |
| 427 | 432 | pubkey: String::new(), | |
| 428 | 433 | artifact: Artifact::default(), | |
| @@ -1392,13 +1397,13 @@ | |||
| 1392 | 1397 | use super::*; | |
| 1393 | 1398 | ||
| 1394 | 1399 | #[test] | |
| 1395 | - | fn the_default_is_the_desktop_with_what_compiles_the_stack() { | |
| 1400 | + | fn the_default_is_the_desktop_with_what_the_image_requires() { | |
| 1396 | 1401 | let choices = Choices::default(); | |
| 1397 | 1402 | assert_eq!(choices.profile, Profile::Client); | |
| 1398 | 1403 | assert_eq!(choices.browser, Browser::Helium); | |
| 1399 | - | // Both, and only both: the shipped stack is Rust and Go, and nothing | |
| 1400 | - | // in it is python or zig. See the comment on `Choices::default`. | |
| 1401 | - | assert_eq!(choices.langs, BTreeSet::from([Lang::Rust, Lang::Go])); | |
| 1404 | + | // Rust, and only Rust: it is the one toolchain the image itself needs, | |
| 1405 | + | // for the build-host role. See the comment on `Choices::default`. | |
| 1406 | + | assert_eq!(choices.langs, BTreeSet::from([Lang::Rust])); | |
| 1402 | 1407 | assert_eq!(choices.artifact, Artifact::Iso); | |
| 1403 | 1408 | // Trimmed by default. What it costs is foreign-architecture emulation, | |
| 1404 | 1409 | // which the house rules forbid using in the first place. | |
| @@ -1419,7 +1424,7 @@ | |||
| 1419 | 1424 | // the two defaults are one decision written in two files, and a build | |
| 1420 | 1425 | // that bypasses the TUI must get the same stack the TUI would have | |
| 1421 | 1426 | // asked for. | |
| 1422 | - | assert_eq!(args[2].1, "rust,go"); | |
| 1427 | + | assert_eq!(args[2].1, "rust"); | |
| 1423 | 1428 | assert_eq!(args[3].1, "unused"); | |
| 1424 | 1429 | } | |
| 1425 | 1430 |