Skip to main content

max / alloy

build: drop the sourcehut CI, and say what actually builds the image builds.disabled/alloy-image.yml is deleted rather than left parked. It had been outside the .builds/ path since 8d0bca6 so sourcehut never ran it, and it still pinned image: fedora/41 against a fedora-bootc:43 base, so re-enabling it would have failed on a two-release-old builder. A CI config nobody runs is worse than none: it reads as coverage. What replaces it, honestly, is nothing. The image is built by build/build-image.sh and build/build-iso.sh on fw13. IMAGE.md now says that, and names the real blocker: an always-on x86_64 builder does not exist here. astra is always-on and aarch64, fw13 is x86_64 and sleeps, and builds are native per architecture with no cross-compiling. Until one machine is both, CI would be a scheduler with nothing to schedule onto. Two things that were quietly resting on the sourcehut job and now need homes: the build-and-lint smoke test on every push, and the weekly rebuild that picks up upstream Fedora base updates. The second is the one a human never remembers. Both are named in IMAGE.md rather than dropped silently, along with the cosign private key, whose old answer was "an sr.ht secret". Docs follow the remote policy: source is makenot.work, srht is a backup. The alloy_tui and makeover links now point at pages that resolve, and IMAGE.md's sr.ht-account open question retires with the migration it was about.
Author: Max Johnson <me@maxj.phd> · 2026-07-25 14:25 UTC
Signed with PGP, not checked
Commit: f8465ccae88321848f1c07e7d6bb29e071463414
Parent: ca7ff2c
6 files changed, +28 insertions, -52 deletions
M CONTRIBUTING.md +1 -1
@@ -6,7 +6,7 @@
6 6
7 7 Alloy is licensed under the GNU General Public License v3.0 or later. See [LICENSE](LICENSE). Code contributed to this repository must be compatible with GPLv3+. Any future code outside this license (vendored dependencies, etc.) must carry compatible terms.
8 8
9 - The design system is deliberately not in this repo: [`alloy_tui`](https://git.sr.ht/~maxmj/alloy_tui) is a separate MIT crate, consumed here from crates.io. It is the reusable library rather than the application, so publishing it permissively lets other terminal UIs adopt Alloy's look without inheriting copyleft, and lets non-GPL siblings link it. Everything in this repo is GPLv3+.
9 + The design system is deliberately not in this repo: [`alloy_tui`](https://makenot.work/git/max/alloy_tui) is a separate MIT crate, consumed here from crates.io. It is the reusable library rather than the application, so publishing it permissively lets other terminal UIs adopt Alloy's look without inheriting copyleft, and lets non-GPL siblings link it. Everything in this repo is GPLv3+.
10 10
11 11 ## Project state
12 12
M README.md +2 -2
@@ -13,7 +13,7 @@
13 13 ## License
14 14
15 15 GPLv3-or-later. The design system it renders through,
16 - [`alloy_tui`](https://git.sr.ht/~maxmj/alloy_tui), lives in its own repo under
16 + [`alloy_tui`](https://makenot.work/git/max/alloy_tui), lives in its own repo under
17 17 MIT, so other projects can build terminal UIs on it without taking on copyleft.
18 18 See [`LICENSE`](LICENSE) and [`CONTRIBUTING.md`](CONTRIBUTING.md).
19 19
@@ -29,5 +29,5 @@
29 29 - [`docs/CONTINUITY.md`](docs/CONTINUITY.md) — a mesh VPN and file sync as base-image citizens: an Alloy install should be rejoinable, not just installable.
30 30 - [`docs/SHELL.md`](docs/SHELL.md) — Nushell as the login shell; bash unchanged as `/bin/sh` and `/bin/bash`. Architecture, interop rules, accepted costs.
31 31 - [`docs/HARDWARE-FW12.md`](docs/HARDWARE-FW12.md) — Framework Laptop 12 as the first hardware target: 2-in-1 stance, touch/tablet/rotation/OSK/fingerprint decisions.
32 - - [`docs/IMAGE.md`](docs/IMAGE.md) — Image composition: bootc + Containerfile, source on `git.sr.ht`, CI on `builds.sr.ht`, container image on `quay.io`.
32 + - [`docs/IMAGE.md`](docs/IMAGE.md) — Image composition: bootc + Containerfile, source on `makenot.work`, built locally and natively, container image on `quay.io` when distribution starts.
33 33 - [`docs/todo.md`](docs/todo.md) — open questions and next steps.
M docs/IMAGE.md +21 -12
@@ -1,6 +1,6 @@
1 1 # Image composition
2 2
3 - How Alloy is built and delivered. bootc + Containerfile, source on `git.sr.ht`, CI on `builds.sr.ht`, container image on `quay.io`. Companion to [STACK.md](STACK.md).
3 + How Alloy is built and delivered. bootc + Containerfile, source on `makenot.work`, built locally and natively, container image on `quay.io` when distribution starts. Companion to [STACK.md](STACK.md).
4 4
5 5 ## Landscape
6 6
@@ -38,27 +38,33 @@
38 38
39 39 ## Delivery stack
40 40
41 - ### Source: `git.sr.ht/~maxmj/alloy`
41 + ### Source: `makenot.work/git/max/alloy`
42 42
43 - Primary remote. Alloy is source-hosted on sr.ht for the same reasons the rest of Make Creative's public work is: independence from big-tech-monopoly infrastructure at the layer where users install software. `srht` is a canonical remote for Alloy, not a backup.
43 + Alloy publishes its source on Make Creative's own platform, for the reason the project holds elsewhere: independence from big-tech-monopoly infrastructure at the layer where users install software, and hosting our own source rather than renting someone's. `astra` is the private mirror and `srht` is a backup remote, not the public face. See `~/Code/CLAUDE.md`, "What each remote is for".
44 44
45 - ### CI: `builds.sr.ht` (disabled during local-iteration phase)
45 + **Current state:** the `mnw` remote is configured but not pushed, so that URL is not live yet. Alloy goes public when there is a manual for a stranger to read.
46 46
47 - sr.ht ships Fedora build VMs with `dnf`, `podman`, `buildah`, `skopeo` preinstalled. When ready, the build file at `.builds/alloy-image.yml` triggers on every push.
47 + ### CI: none. Builds are local and native.
48 48
49 - **Current state:** the build file lives at `builds.disabled/alloy-image.yml`, parked outside the `.builds/` path sr.ht auto-runs, so no CI resources are consumed while the Containerfile is being iterated locally. Rename the directory back to `.builds/` and push to re-enable.
49 + Alloy has no hosted CI, and the sourcehut build that used to stand in for one is gone (`builds.disabled/alloy-image.yml`, deleted; it had been parked outside `.builds/` since `8d0bca6` and pinned `image: fedora/41`, two releases behind the base).
50 +
51 + The image is built by `build/build-image.sh` (disk images, via bootc-image-builder) and `build/build-iso.sh` (the installer ISO) on fw13, against rootful podman. That is the whole build story today, and it is a deliberate consequence of two standing rules rather than an accident: builds are native per architecture and nothing is cross-compiled, and app binaries are never built on the production host.
52 +
53 + Which means an always-on x86_64 builder is the actual missing piece, not a CI config. astra is always-on but aarch64, and the image is x86_64. fw13 is x86_64 but sleeps aggressively. Until one machine is both, "CI" would be a scheduler with nothing to schedule onto. The Fedora build host meant to replace Pop!_OS on fw13 is where this resolves.
50 54
51 55 ### Registry: `quay.io` (deferred until distribution starts)
52 56
53 57 Red Hat's container registry is the intended destination once Alloy has an image worth distributing. Free for public images, ecosystem-coherent with the `quay.io/fedora/fedora-bootc:43` base.
54 58
55 - **Current state:** no registry provisioned. `.builds/alloy-image.yml` runs a **build + lint smoke test** only, validating the Containerfile on every push, without publishing anywhere. When the image is worth distributing, the CI file grows back `sign` and `push` tasks and `quay.io/alloy/alloy` gets provisioned with a robot account.
59 + **Current state:** no registry provisioned, and nothing validates the Containerfile except running `build/build-image.sh` by hand. The old sourcehut job did a build-and-lint smoke test on every push; deleting it means that check now happens when someone remembers to build, which is worth naming rather than glossing. When the image is worth distributing, `quay.io/alloy/alloy` gets provisioned with a robot account and whatever builder exists by then grows `sign` and `push` steps.
56 60
57 61 Rejected alternatives (for when distribution starts): ghcr.io (source-registry ecosystem mismatch), Docker Hub (pull rate limits, no advantage), self-hosted (real infra commitment, not worth v0 operational load; revisit v2+).
58 62
59 63 ### Signing: cosign (deferred alongside distribution)
60 64
61 - Once a registry is publishing images, cosign signs them as a CI task. Private key stored as an sr.ht secret; public key published in the Alloy repo. Users verify via `bootc switch --signature-source=<url>`. Nothing to set up until distribution starts.
65 + Once a registry is publishing images, cosign signs them as a build step. Public key published in the Alloy repo. Users verify via `bootc switch --signature-source=<url>`. Nothing to set up until distribution starts.
66 +
67 + The private key needs a home, and "an sr.ht secret" was the old answer, which went out with the sourcehut build. Whatever builds and pushes the image is what needs to hold it; a signing key on a machine that sleeps is a different problem from a signing key in a hosted secret store, and that is another thing the builder decision settles.
62 68
63 69 ## Layer structure
64 70
@@ -77,10 +83,12 @@
77 83
78 84 ## Update cadence
79 85
80 - The sr.ht build triggers on:
86 + There is no automated trigger today. The image is rebuilt when someone runs `build/build-image.sh`.
81 87
82 - - **Push to `main`**: Alloy commits.
83 - - **Weekly cron**: picks up upstream Fedora base image updates. sr.ht doesn't have webhook receivers for external triggers, so cron is the pragmatic answer.
88 + Two triggers are wanted whenever a builder exists, and they are worth writing down now because the second is the one a manual process silently drops:
89 +
90 + - **On a commit to `main`**, so a Containerfile that no longer builds is caught by the person who broke it.
91 + - **Weekly**, to pick up upstream Fedora base updates. Nothing in Alloy changes, and the image still does: this is the trigger a human never remembers, and its absence means the base drifts until the next hand build notices.
84 92
85 93 Users receive updates via `bootc upgrade`: no push notifications, no forced restart. Alloy's update UX is the standard bootc UX.
86 94
@@ -95,5 +103,6 @@
95 103 - [ ] Verify which Alloy packages are in Fedora main vs. need COPRs. Candidates that may need COPRs: `satty`, `wl-screenrec` (depending on Fedora version). Audit at v0 packaging time.
96 104 - [ ] `bootc-image-builder` for ISO generation. First-time-user path is `bootc install` from a live environment; the ISO is what makes that a smooth experience. Verify the ISO builder handles Alloy's specific package set.
97 105 - [x] **Containerfile stage policy: multi-stage only where a non-Fedora Rust binary needs to ship.** Currently that is one stage, `rust-build`, and it builds the Alloy console. Everything else comes from RPM in the runtime stage. Do not add stages preemptively. Corrected 2026-07-19: this line named wl-screenrec as the stage's occupant, which was stale — wl-screenrec was deferred rather than shipped, so for a while there was no stage at all. The console is the case the policy was written for and did not anticipate: it is the one binary Alloy authors, so no repo can ever carry it, and a distro whose headline surface is its console cannot ship without one. It is built on `fedora:43` rather than on the dev box so the toolchain and glibc match the runtime stage.
98 - - [x] sr.ht account: **`~maxmj`** for v0. LLC-owned `~makecreative` remains the long-term target; migration if/when the project grows.
106 + - [ ] **An always-on x86_64 builder.** The blocker under "CI" above, and the thing the signing key and the weekly base-update rebuild both wait on. astra is always-on and aarch64; fw13 is x86_64 and sleeps.
107 + - [x] Source hosting: **`makenot.work/git/max/alloy`**, our own platform. The old answer here was the sr.ht account `~maxmj` for v0 with LLC-owned `~makecreative` as the long-term target; sourcehut is now a backup remote and that migration question retires with it.
99 108 - [x] **Publish `:latest`, `:<fedora-version>`, and `:<fedora-version>-YYYYMMDD` tags.** Currently `:latest`, `:43`, `:43-YYYYMMDD`. `:latest` for casual users, the bare version for version-pinned users, the dated tag for reproducibility. Wired up when distribution starts.
M docs/STACK.md +1 -1
@@ -8,7 +8,7 @@
8 8
9 9 **ublue relationship: alongside, not downstream.** Preserves naming and architectural independence. The "Alloy" name was picked specifically to avoid the ublue "Blue\*" prefix. Tradeoff: no shared image-building infrastructure, so whatever Alloy ships for image composition is on Alloy to build/maintain. Upside: total architectural freedom and a clean brand.
10 10
11 - **Composition strategy:** bootc + Containerfile, `FROM quay.io/fedora/fedora-bootc:43`. Source on `git.sr.ht/~maxmj/alloy`, CI on `builds.sr.ht`, published container image on `quay.io/alloy/alloy`. This is the ublue *convention* (Containerfile, OCI registry, CI-driven build) without ublue's base image, which is what "alongside ublue" resolves to in practice. Full delivery strategy and open questions in [IMAGE.md](IMAGE.md). Containerfile at the repo root; sr.ht CI at `.builds/alloy-image.yml`.
11 + **Composition strategy:** bootc + Containerfile, `FROM quay.io/fedora/fedora-bootc:43`. Source on `makenot.work/git/max/alloy`, built locally and natively by `build/build-image.sh`, published container image on `quay.io/alloy/alloy` when distribution starts. This is the ublue *convention* (Containerfile, OCI registry) without ublue's base image, which is what "alongside ublue" resolves to in practice. There is no hosted CI: sourcehut's build was deleted rather than left to rot, and an always-on x86_64 builder is the open question. Full delivery strategy in [IMAGE.md](IMAGE.md). Containerfile at the repo root.
12 12
13 13 ## Compositor
14 14
M docs/TOKENS.md +3 -3
@@ -6,7 +6,7 @@
6 6
7 7 ## Storage format: makeover
8 8
9 - Alloy consumes themes in the [makeover](https://git.sr.ht/~maxmj/makeover) TOML format, the same schema GoingsOn, Balanced Breakfast, audiofiles, and makenot.work already consume. A theme file has seven sections:
9 + Alloy consumes themes in the [makeover](https://makenot.work/git/max/makeover) TOML format, the same schema GoingsOn, Balanced Breakfast, audiofiles, and makenot.work already consume. A theme file has seven sections:
10 10
11 11 ```toml
12 12 [meta]
@@ -45,7 +45,7 @@
45 45 six = "#305858"
46 46 ```
47 47
48 - Alloy's default light theme is **[Akari Dawn](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/akari-dawn.toml)**; the default dark is **[Akari Night](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/akari-night.toml)**. Both are based on Shu Kutsuzawa's [Akari](https://github.com/cappyzawa/akari-theme) (MIT). Users pick a mode at startup (`--theme` flag or `$COLORFGBG` / OSC background query); users can also drop any makeover `.toml` into `~/.config/alloy/themes/` and pass its name to swap.
48 + Alloy's default light theme is **[Akari Dawn](https://makenot.work/git/max/makeover/tree/main/themes/akari-dawn.toml)**; the default dark is **[Akari Night](https://makenot.work/git/max/makeover/tree/main/themes/akari-night.toml)**. Both are based on Shu Kutsuzawa's [Akari](https://github.com/cappyzawa/akari-theme) (MIT). Users pick a mode at startup (`--theme` flag or `$COLORFGBG` / OSC background query); users can also drop any makeover `.toml` into `~/.config/alloy/themes/` and pass its name to swap.
49 49
50 50 **Why makeover as the storage format:** the future user-facing theme catalog (a website of downloadable `.toml` files) is only useful if every make-family app renders any downloaded theme the same way. Sharing the format across apps buys that with zero adapter code.
51 51
@@ -133,7 +133,7 @@
133 133
134 134 ## How this file is consumed
135 135
136 - The themes [makeover ships](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/) are the ground truth. `alloy_tui` at load time:
136 + The themes [makeover ships](https://makenot.work/git/max/makeover/tree/main/themes/) are the ground truth. `alloy_tui` at load time:
137 137
138 138 1. Reads the selected theme file via the `makeover` crate.
139 139 2. Computes the two derived tokens (`border-subtle`, `border-strong`) from `line.border`.
@@ -1,33 +1,0 @@
1 - # Alloy image build — builds.sr.ht
2 - #
3 - # Build-and-lint smoke test. Validates the Containerfile on every push
4 - # without publishing anywhere. Sign + push tasks are added back when
5 - # Alloy is ready to distribute (see IMAGE.md open questions).
6 - #
7 - # Triggered by push to main + weekly cron for upstream base updates.
8 -
9 - image: fedora/41
10 -
11 - packages:
12 - - podman
13 - - buildah
14 -
15 - sources:
16 - - https://git.sr.ht/~maxmj/alloy
17 -
18 - environment:
19 - IMAGE_TAG: alloy:ci
20 -
21 - tasks:
22 - - build: |
23 - cd alloy
24 - podman build -t "$IMAGE_TAG" .
25 -
26 - - lint: |
27 - podman run --rm --entrypoint /usr/bin/bootc "$IMAGE_TAG" \
28 - container lint
29 -
30 - triggers:
31 - - action: email
32 - condition: failure
33 - to: info@makenot.work