max / alloy
3 files changed,
+41 insertions,
-14 deletions
| @@ -2941,16 +2941,25 @@ | |||
| 2941 | 2941 | # booted system talks to rpm-ostreed over D-Bus, and its --enablerepo is | |
| 2942 | 2942 | # "only supported in a container build" — so a repo shipped disabled is a | |
| 2943 | 2943 | # repo the first-boot unit cannot turn on for its own transaction, and the | |
| 2944 | - | # machine comes up with no console. The standing "repo enabled by default | |
| 2945 | - | # or opt-in" question (GO d866e125) is about the future NETWORK repo, where | |
| 2946 | - | # the answer is a real choice; this one reaches no network and answers to | |
| 2947 | - | # nothing but the local filesystem. | |
| 2944 | + | # machine comes up with no console. The "enabled by default or opt-in" | |
| 2945 | + | # question was about the future NETWORK repo, where the answer was a real | |
| 2946 | + | # choice, and it is answered: opt-in, `enabled=0`, ruled 2026-08-17. This one | |
| 2947 | + | # reaches no network and answers to nothing but the local filesystem, so the | |
| 2948 | + | # ruling does not reach it. | |
| 2948 | 2949 | # | |
| 2949 | - | # gpgcheck=0 is a marker for signing being unfinished (GO d866e125 subtask | |
| 2950 | - | # 4), not a decision. It is a weaker exposure than an unsigned network repo | |
| 2951 | - | # — these files came in with the image and are only as trustworthy as it is | |
| 2952 | - | # — but "the image is the trust boundary" is a claim signing should make | |
| 2953 | - | # explicitly rather than one this file should assume. | |
| 2950 | + | # gpgcheck=0 is unfinished work rather than a position, and the position is | |
| 2951 | + | # now decided: this repo gets signed too. Max ruled it 2026-08-17 over the | |
| 2952 | + | # recommendation, which was to leave the carried repo explicitly unsigned — | |
| 2953 | + | # these files came in with the image and are only as trustworthy as it is, so | |
| 2954 | + | # a signature defends nothing the image's own integrity does not already | |
| 2955 | + | # defend. What the ruling buys is one code path with the network repo, so | |
| 2956 | + | # there is no branch only that path exercises and the key exists before it is | |
| 2957 | + | # needed under pressure. "The image is the trust boundary" is a claim signing | |
| 2958 | + | # should make explicitly rather than one this file should assume. | |
| 2959 | + | # | |
| 2960 | + | # What is left: the key, then `gpgcheck=1` and a `gpgkey` here, the public key | |
| 2961 | + | # shipped in the image, and a test that fails the build if this repo does not | |
| 2962 | + | # verify. GoingsOn alloy d866e125. | |
| 2954 | 2963 | RUN printf '[alloy-local]\nname=Alloy components, carried with the image\nbaseurl=file:///usr/share/alloy/rpm\nenabled=1\ngpgcheck=0\n' \ | |
| 2955 | 2964 | > /etc/yum.repos.d/alloy-local.repo | |
| 2956 | 2965 |
| @@ -44,6 +44,12 @@ | |||
| 44 | 44 | ||
| 45 | 45 | That repo is enabled, and it is forced rather than chosen: `rpm-ostree install` on a booted system supports `--enablerepo` only in a container build, so a repo shipped disabled is one the first-boot unit could not turn on for its own transaction. It reaches no network, which is why this does not touch the position below. | |
| 46 | 46 | ||
| 47 | + | **The network repo ships disabled, and that is chosen rather than forced.** The carried `file://` repo above had to be enabled; this one did not, and it is `enabled=0` on every installed machine. A fresh Alloy has no line pointing at makenot.work that its owner did not add. Turning it on is one command and needs no extra package: | |
| 48 | + | ||
| 49 | + | sudo sed -i 's/^enabled=0$/enabled=1/' /etc/yum.repos.d/alloy-hotfix.repo | |
| 50 | + | ||
| 51 | + | The cost is real and is not papered over here: **a hotfix reaches only machines whose owner went looking for the channel.** Every other machine stays on what it installed until its owner rebuilds. That is the trade the opt-in default makes, and anyone announcing a fix has to assume most machines will not take it on their own. Decided by Max 2026-08-17. | |
| 52 | + | ||
| 47 | 53 | **Our layers are disposable.** A layered package whose version the base later overtakes wedges the machine: every subsequent update fails to depsolve, permanently, and that is the normal life of a hotfix rather than an edge case. The rule that avoids it is to carry no layer of ours across an upgrade. Drop them, upgrade, re-apply only if the base still lacks the fix. It costs one reboot rather than two, because the two transactions compose into one deployment, and it hands `bootc upgrade` back a deployment it will consent to work on. | |
| 48 | 54 | ||
| 49 | 55 | On the path Alloy takes today the rule costs nothing to apply, because `bootc switch` applies it for free. `switch` is not `upgrade`: only `upgrade` consults local modifications and refuses, and a switch discards the layer outright rather than carrying it. The machine comes up once with no console, `alloy-layer-components.service` sees no `/usr/bin/alloy` and lays the new image's copies down, and it reboots a second time into them. So a component tracks the image it shipped with, and nothing has to own the drop. The second boot is a blank screen on a machine that was working when its owner typed the command, so it says what it is doing: `alloy-layer-notice` writes to `/dev/console`, which `quiet` does not silence, and `alloy update` says the same thing before the command is typed. |
| @@ -29,10 +29,22 @@ | |||
| 29 | 29 | # fix. Measured, in build/layertest — read its README before changing where | |
| 30 | 30 | # this package lands. | |
| 31 | 31 | # | |
| 32 | - | # Unsigned so far. Signing is its own subtask and its own key story, and a repo | |
| 33 | - | # that can reach every installed machine has no business being unsigned for | |
| 34 | - | # long. `gpgcheck=0` in the generated .repo file is the marker for that being | |
| 35 | - | # unfinished, not a decision. | |
| 32 | + | # Unsigned so far, and the decision is in: **both repos get signed**, the | |
| 33 | + | # carried one included. Ruled by Max 2026-08-17 over the recommendation, which | |
| 34 | + | # was to leave the carried repo explicitly unsigned on the grounds that it | |
| 35 | + | # reaches no network and is only as trustworthy as the image around it. What | |
| 36 | + | # the ruling buys is one code path: the carried repo and the network repo are | |
| 37 | + | # built, configured and verified the same way, so no branch exists that only | |
| 38 | + | # the network path exercises, and the key and its handling exist before | |
| 39 | + | # anything is under pressure to ship. | |
| 40 | + | # | |
| 41 | + | # So `gpgcheck=0` in the generated .repo file is unfinished work, not a | |
| 42 | + | # position. What is left is the key: it is generated under the same rule as | |
| 43 | + | # the sops identities (never in the repo it signs, never synced by anything), | |
| 44 | + | # and where it lives is settled before it is generated. Then this script signs | |
| 45 | + | # what it builds, the public key ships in the image, the .repo snippets carry | |
| 46 | + | # `gpgcheck=1` and a `gpgkey`, and a test fails the build if the carried repo | |
| 47 | + | # does not verify. GoingsOn alloy d866e125. | |
| 36 | 48 | set -euo pipefail | |
| 37 | 49 | ||
| 38 | 50 | HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| @@ -156,4 +168,4 @@ | |||
| 156 | 168 | say " name=Alloy components" | |
| 157 | 169 | say " baseurl=https://<host>/rpm/" | |
| 158 | 170 | say " enabled=1" | |
| 159 | - | say " gpgcheck=0 # until signing lands" | |
| 171 | + | say " gpgcheck=0 # until signing lands; both repos are to be signed" |