Skip to main content

max / alloy

Take the pinned bases from a mirror we own QUASI_TYPE_MIRROR is an ENV over the whole rust-build stage, defaulting to https://makenot.work/static/bases, where the MNW server holds the four pinned files under the sha256 each pin already carries. quasi-type tries it before the pinned url and verifies the bytes against that digest either way, so a mirror can serve the pinned file or nothing. An ENV rather than a flag on the cut, because the fetch this exists for is : shop's build script cuts a third face with the cache directory and the offline flag both hardcoded, so the seed the font stage carries has no path to aim at. The mirror is read inside quasi-type, which is the crate on both sides of that, and it is the only one of the available fixes that covers the fetch that took the build down in August without shop changing. The font stage says what the mirror holds, derived from the pinned checkout's own pins.toml rather than a list here that would go stale. It reports rather than asserts: the mirror is a second source, so one that is down is a slower build. Verified against the live default, which is committed but not yet deployed, by building the stage: it printed holds 0 of 4 and cut both faces from upstream. SHOP_REV and QUASI_TYPE_REV move with it. Both are needed: the variable is read from quasi-type 0.1.3, and shop's own pin has to be at least that.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_0153yKAYUmoC9bZz2eV6cA88
Author: Max Johnson <me@maxj.phd> · 2026-08-29 17:45 UTC
Signed with PGP, not checked
Commit: 91890a0eaab2323dac855cdef96f7544cee24593
Parent: 0a6c574
5 files changed, +228 insertions, -38 deletions
M Containerfile +78 -28
@@ -119,6 +119,35 @@
119 119 # that a `COPY` into /usr/bin is the worst of the shapes — an unowned file
120 120 # cannot even be layered over, it dies in checkout.
121 121 #
122 + # The mirror of quasi-type's pinned bases, named here rather than in the font
123 + # stage below because the fetch it exists for is `cargo install shop`, 180 lines
124 + # further down. shop's `shop-font/build.rs` cuts a third face from the same
125 + # Atkinson base with the cache directory and the offline flag both hardcoded, so
126 + # the seed cache the font stage carries has no path and no flag to aim at. A
127 + # mirror is the only one of the available fixes that covers that fetch without
128 + # shop changing, which is why it is an ENV over the whole stage rather than a
129 + # flag on the cut.
130 + #
131 + # A base URL. quasi-type addresses every pinned file under it by the sha256 the
132 + # pin already carries, tries it before upstream, and verifies the bytes against
133 + # that digest either way, so a mirror can serve the pinned file or nothing. It
134 + # cannot serve a different one, which is what makes this a second source rather
135 + # than a second thing to trust. Bytes that do not match fall through to the
136 + # pinned url rather than failing the build, so an out-of-date mirror is not
137 + # worse than no mirror.
138 + #
139 + # Set it empty to switch it off and fetch upstream only, which is what a build
140 + # with no access to our infrastructure wants. Nothing requires it to answer: the
141 + # font stage reports which pinned files it holds rather than asserting any, since
142 + # a mirror that is down is a slower build and not a broken one.
143 + #
144 + # Needs a QUASI_TYPE_REV at or after 43845e3, where the variable starts being
145 + # read, and a SHOP_REV whose own quasi-type pin is at or after it. All three move
146 + # together for that reason. The files are in the MNW server's `static/bases/`,
147 + # served by the `/static` ServeDir with no route of its own.
148 + ARG QUASI_TYPE_MIRROR=https://makenot.work/static/bases
149 + ENV QUASI_TYPE_MIRROR=${QUASI_TYPE_MIRROR}
150 +
122 151 # Pinned by revision, not by branch. An unpinned build is a different image
123 152 # every day for the same Containerfile, and the terminal is the one component
124 153 # where "it worked yesterday" has no recovery path from inside the session.
@@ -145,7 +174,7 @@
145 174 # alone, so console edits — the thing that changes most often — reuse it
146 175 # instead of rebuilding a terminal that did not change. Moving it below the
147 176 # `COPY crates/` would defeat the dependency-cache split that follows.
148 - ARG SHOP_REV=af8c79c22e79f95874097bddb661ec46d8c60cb5
177 + ARG SHOP_REV=144438245fa3b25b897f4c61ae88f49b3508f66a
149 178
150 179 # Does the remote have anything to build from, before anything expensive runs.
151 180 #
@@ -244,29 +273,33 @@
244 273 # cannot happen, and that makes this a property of the distribution rather than
245 274 # a flaky step.
246 275 #
247 - # WHAT THE SEED COVERS IS THIS BLOCK'S TWO CUTS, AND NOTHING ELSE. Say it here
248 - # rather than let a reader infer a whole-build guarantee from the word `sealed`,
249 - # because the fetch it does not cover is the one that failed. `cargo install
250 - # shop` is 85 lines above, and moving the COPY above it would change nothing:
251 - # shop's `shop-font/build.rs` calls `quasi_type::cut("quasi-mono",
252 - # &out.join("bases"), false)`, with the cache directory hardcoded to its own
253 - # OUT_DIR and `offline` hardcoded false, so there is no path and no flag for
254 - # this file to aim at a carried copy. Read from shop at rev ${SHOP_REV} on
255 - # 2026-08-19.
276 + # WHAT THE SEED COVERS IS STILL THIS BLOCK'S TWO CUTS, AND NOTHING ELSE. Say it
277 + # here rather than let a reader infer a whole-build guarantee from the word
278 + # `sealed`. `cargo install shop` is 140 lines above, and moving the COPY above it
279 + # would change nothing: shop's `shop-font/build.rs` calls
280 + # `quasi_type::cut_native(&out, &out.join("bases"), false, ...)`, with the cache
281 + # directory hardcoded to its own OUT_DIR and `offline` hardcoded false, so there
282 + # is no path and no flag for this file to aim at a carried copy. Read from shop
283 + # at rev ${SHOP_REV} on 2026-08-29.
256 284 #
257 - # THE CONTRACT SHOP WOULD HAVE TO IMPLEMENT, so that this is a state to fix and
258 - # not a shrug: read the cache directory from an environment variable, falling
259 - # back to `OUT_DIR/bases` when it is unset so nothing about a plain `cargo
260 - # build` changes, and take `offline` from a second one. Two lines in that build
261 - # script. When they exist, the COPY moves above `cargo install shop`, this
262 - # stage exports both variables, and `sealed` becomes a claim about the build
263 - # rather than about the cut. It is shop's change to make, and it is not made
264 - # here. Until it is, a build host that wants the third fetch covered warms
265 - # cargo's target directory instead, which is what shop's own build script says
266 - # it is for.
285 + # WHAT COVERS THE FETCH THE SEED DOES NOT COVER IS THE MIRROR, and that is why
286 + # the mirror is the fix this stage took. `QUASI_TYPE_MIRROR` is read inside
287 + # quasi-type, which is the crate on both sides of the hardcoding, so it reaches
288 + # a fetch this file cannot otherwise touch. The seed still does not cover that
289 + # fetch: a mirror is a better source and not a carried copy, so `sealed` means
290 + # exactly what it meant.
267 291 #
268 - # TWO THINGS STAND AGAINST THE OTHER TWO FETCHES, and neither is the durable
269 - # fix.
292 + # THE CONTRACT SHOP WOULD HAVE TO IMPLEMENT, if `sealed` is ever to be a claim
293 + # about the build and not only about the cut: read the cache directory from an
294 + # environment variable, falling back to `OUT_DIR/bases` when it is unset so
295 + # nothing about a plain `cargo build` changes, and take `offline` from a second
296 + # one. Two lines in that build script. When they exist, the COPY moves above
297 + # `cargo install shop` and this stage exports both variables. It is shop's change
298 + # to make and it is not made here; the mirror lowered its urgency rather than
299 + # removing it, since an offline build host still cannot seal that fetch.
300 + #
301 + # TWO MORE THINGS STAND AGAINST THE OTHER TWO FETCHES, and neither is the durable
302 + # fix the mirror is.
270 303 #
271 304 # 1. The retry is upstream, in quasi-type's own fetch: curl `--retry 5
272 305 # --retry-delay 2 --retry-all-errors`, which covers 429, 408, the 5xx
@@ -311,11 +344,16 @@
311 344 # pin, and it would go stale the first time QUASI_TYPE_REV moved to a base with
312 345 # a new version in its file names.
313 346 #
314 - # WHAT IS STILL MISSING is a copy of the bases on infrastructure we own,
315 - # addressed by the same sha256, so that an unseeded build has somewhere to reach
316 - # that is not somebody else's rate limiter. That is a hosting decision (astra or
317 - # makenot.work) and it is not made here. It is also the only one of these that
318 - # would cover shop's fetch without shop changing at all.
347 + # THE COPY ON INFRASTRUCTURE WE OWN EXISTS NOW, and it is the piece this comment
348 + # used to say was missing. The four pinned files live in the MNW server's
349 + # `static/bases/` named by their sha256, and `QUASI_TYPE_MIRROR` near the top of
350 + # this stage points quasi-type at them. It is an ENV over the whole stage rather
351 + # than a flag here because the fetch that took the build down was shop's, not
352 + # these two, and shop reads the same variable through the same crate.
353 + #
354 + # It adds a source and does not remove one: every base still carries its
355 + # upstream url and falls back to it, so a build with no access to makenot.work
356 + # is slower on a cold cache and nothing more.
319 357 #
320 358 # THE CUT IS UNCONDITIONAL, and that is a decision rather than an omission. Only
321 359 # the client installs these faces (see the profile block near the end of this
@@ -326,7 +364,7 @@
326 364 # What it would cost is the property that makes this stage cheap. Nothing in it
327 365 # reads `$PROFILE`, so both profiles share every layer of it, including the
328 366 # console build below and the RPM packaging after that.
329 - ARG QUASI_TYPE_REV=e40330602e68fbdd2a9d2917bdec33a7ce573a1f
367 + ARG QUASI_TYPE_REV=52c5bc069fa0b0f3f74bad6957e2d5ca12c0b57d
330 368 ARG QUASI_BASES=fetch
331 369 COPY build/base-cache/ /base-cache/
332 370 RUN set -eu; \
@@ -381,6 +419,18 @@
381 419 test -f /quasi-type/bases/cache/"$name" || missing="$missing $name"; \
382 420 done; \
383 421 echo "quasi-type bases: seeded $seeded file(s) from /base-cache, mode $QUASI_BASES, missing:${missing:- none}"; \
422 + mirror="${QUASI_TYPE_MIRROR:-}"; \
423 + if [ -n "$mirror" ] && [ "$QUASI_BASES" = fetch ]; then \
424 + have=0; total=0; \
425 + for digest in $(sed -n 's/^\(license_\)\{0,1\}sha256 = "\([0-9a-f]\{64\}\)"$/\2/p' /quasi-type/bases/pins.toml); do \
426 + total=$((total + 1)); \
427 + curl --fail --silent --head --location --max-time 20 "$mirror/$digest" >/dev/null 2>&1 \
428 + && have=$((have + 1)); \
429 + done; \
430 + echo "quasi-type mirror: $mirror holds $have of $total pinned file(s); the rest fall through to their upstream urls"; \
431 + else \
432 + echo "quasi-type mirror: not consulted (mirror '$mirror', mode $QUASI_BASES)"; \
433 + fi; \
384 434 if [ "$QUASI_BASES" = sealed ]; then \
385 435 [ -z "$missing" ] \
386 436 || { echo "QUASI_BASES=sealed and the seed is short of:$missing. The cut would fail on the first of them after building the tool. Copy them into build/base-cache/ (its README says where from) or build with QUASI_BASES=fetch" >&2; exit 1; }; \
@@ -1,3 +1,3 @@
1 1 # The SHOP_REV that build/check-rust-stage.sh last built clean.
2 2 # Written by that script on success. Do not edit by hand.
3 - af8c79c22e79f95874097bddb661ec46d8c60cb5
3 + 144438245fa3b25b897f4c61ae88f49b3508f66a
@@ -18,9 +18,17 @@
18 18 terminal's bundled face while `cargo install shop` runs. That third one is the
19 19 one that broke the build on 2026-08-17, and this directory does not cover it:
20 20 shop's build script passes its own `OUT_DIR` as the cache directory, so there is
21 - no path for the image build to point at a carried copy. Covering it needs a
22 - change in shop (read the cache directory and the offline flag from the
23 - environment); until then a warm cargo target directory is what covers it.
21 + no path for the image build to point at a carried copy. Covering it with a seed
22 + needs a change in shop (read the cache directory and the offline flag from the
23 + environment).
24 +
25 + What covers all three without that change is the mirror. `QUASI_TYPE_MIRROR`,
26 + set in the Containerfile to `https://makenot.work/static/bases`, is read inside
27 + `quasi-type` itself, so it reaches the fetch this directory cannot. It is a
28 + second source and not a carried copy: files are addressed by the sha256 the pin
29 + already carries and verified against it, upstream stays the fallback, and
30 + `sealed` still means the two cuts. A warm cargo target directory is what covers
31 + the terminal's fetch on a host that has built once.
24 32
25 33 The rest of the build is not offline either, in any mode: it clones two
26 34 repositories, resolves crates.io, and installs packages.
@@ -141,8 +141,20 @@
141 141 that the terminal's own build script cuts while `cargo install shop` runs. The
142 142 seed below covers the first two. It does not cover the terminal's, which is the
143 143 fetch that failed in August, because the terminal's build script names its own
144 - cache directory and nothing in the image build can point it elsewhere. Warming
145 - the cargo target directory is what covers that one today.
144 + cache directory and nothing in the image build can point it elsewhere.
145 +
146 + What covers all three is a mirror, and the build takes one by default:
147 + `QUASI_TYPE_MIRROR` is set to `https://makenot.work/static/bases`, where the
148 + pinned files are served under the sha256 each pin already carries. The tool
149 + tries the mirror first and falls back to the pinned upstream url, and it checks
150 + the bytes against that digest either way, so a mirror can serve the pinned file
151 + or nothing. A build with no access to makenot.work is a slower first build and
152 + nothing more; to switch it off,
153 +
154 + build/build-iso.sh --build-arg QUASI_TYPE_MIRROR=
155 +
156 + The build log says which way it went. Warming the cargo target directory still
157 + covers the terminal's fetch on a host that has built once.
146 158
147 159 To seed the two the image cuts, put the four files in `build/base-cache/` (its
148 160 README names them and says where to copy them from). They are carried into the
@@ -206,7 +206,7 @@
206 206 let bin = scratch.join("bin");
207 207 std::fs::create_dir_all(&bin).expect("bin");
208 208 for tool in [
209 - "rm", "cp", "mkdir", "ls", "wc", "grep", "dirname", "cat", "chmod", "awk",
209 + "rm", "cp", "mkdir", "ls", "wc", "grep", "dirname", "cat", "chmod", "awk", "sed",
210 210 ] {
211 211 let Some(real) = ["/usr/bin", "/bin", "/usr/local/bin"]
212 212 .iter()
@@ -758,13 +758,14 @@
758 758 version = \"2.001\"\n\
759 759 license = \"OFL-1.1\"\n\
760 760 license_url = \"https://example.invalid/mono/OFL.txt\"\n\
761 + license_sha256 = \"1111111111111111111111111111111111111111111111111111111111111111\"\n\
761 762 copyright = \"Braille Institute\"\n\
762 763 designer = \"Braille Institute\"\n\
763 764 \n\
764 765 [[base.face]]\n\
765 766 style = \"ExtraLight\"\n\
766 767 url = \"https://example.invalid/mono/AtkinsonHyperlegibleMono%5Bwght%5D.ttf\"\n\
767 - sha256 = \"00\"\n\
768 + sha256 = \"2222222222222222222222222222222222222222222222222222222222222222\"\n\
768 769 variable = true\n\
769 770 \n\
770 771 [[base]]\n\
@@ -773,13 +774,14 @@
773 774 version = \"2.001\"\n\
774 775 license = \"OFL-1.1\"\n\
775 776 license_url = \"https://example.invalid/next/OFL.txt\"\n\
777 + license_sha256 = \"3333333333333333333333333333333333333333333333333333333333333333\"\n\
776 778 copyright = \"Braille Institute\"\n\
777 779 designer = \"Braille Institute\"\n\
778 780 \n\
779 781 [[base.face]]\n\
780 782 style = \"ExtraLight\"\n\
781 783 url = \"https://example.invalid/next/AtkinsonHyperlegibleNext%5Bwght%5D.ttf\"\n\
782 - sha256 = \"00\"\n\
784 + sha256 = \"4444444444444444444444444444444444444444444444444444444444444444\"\n\
783 785 variable = true\n\
784 786 \n\
785 787 [[slot]]\n\
@@ -810,6 +812,14 @@
810 812 // them, and a checkout with no pins is a build that cannot check a seed.
811 813 write_executable(&bin.join("git"), "#!/bin/sh\nexit 0\n");
812 814 write_executable(&bin.join("cargo"), "#!/bin/sh\nexit 0\n");
815 + // Records every url the mirror probe asks for and answers 404 to all of
816 + // them, which is the state of a mirror that has not been deployed yet. A
817 + // test that wants the other answer overwrites this stub.
818 + write_executable(
819 + &bin.join("curl"),
820 + "#!/bin/sh\nfor arg; do case \"$arg\" in http*) printf '%s\\n' \"$arg\" \
821 + >> \"$(dirname \"$0\")/../curls\" ;; esac; done\nexit 22\n",
822 + );
813 823 write_executable(
814 824 &root.join("quasi-type/target/release/quasi-type"),
815 825 "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$(dirname \"$0\")/../../calls\"\n",
@@ -825,13 +835,24 @@
825 835 scratch
826 836 }
827 837
828 - /// Run the cut block against a fake root, in one of the two modes.
838 + /// Run the cut block against a fake root, in one of the two modes, with no
839 + /// mirror named.
829 840 fn run_cut(scratch: &Scratch, mode: &str) -> std::process::Output {
841 + run_cut_with_mirror(scratch, mode, "")
842 + }
843 +
844 + /// [`run_cut`] against a named mirror.
845 + ///
846 + /// Separate rather than a parameter on every call site, because a mirror is not
847 + /// what any of the seed tests are about and an empty one is what a build with
848 + /// `--build-arg QUASI_TYPE_MIRROR=` gets.
849 + fn run_cut_with_mirror(scratch: &Scratch, mode: &str, mirror: &str) -> std::process::Output {
830 850 Command::new(shell())
831 851 .arg("-c")
832 852 .arg(cut_script(scratch.path()))
833 853 .env("QUASI_BASES", mode)
834 854 .env("QUASI_TYPE_REV", "deadbeef")
855 + .env("QUASI_TYPE_MIRROR", mirror)
835 856 .env("PATH", scratch.join("bin").to_string_lossy().to_string())
836 857 .output()
837 858 .expect("running the cut block")
@@ -890,6 +911,105 @@
890 911 );
891 912 }
892 913
914 + /// The mirror is probed for every digest the pins carry, and for nothing else.
915 + ///
916 + /// The probe derives the digests from the pinned checkout's own `pins.toml`
917 + /// rather than naming them here, which is the same rule the seed's file names
918 + /// follow: a list in this repo would be a second copy of the pin and would go
919 + /// stale on the first base version bump, reporting a live mirror as a dead one.
920 + /// The fixture's four digests are the two faces and the two licence texts.
921 + #[test]
922 + fn the_mirror_is_asked_for_every_pinned_digest() {
923 + let scratch = cut_root("mirror-probe", &[]);
924 + let output = run_cut_with_mirror(&scratch, "fetch", "https://mirror.invalid/bases");
925 + assert!(
926 + output.status.success(),
927 + "the mirror probe stopped the cut:\n{}",
928 + String::from_utf8_lossy(&output.stderr),
929 + );
930 +
931 + let asked = std::fs::read_to_string(scratch.join("curls")).unwrap_or_default();
932 + let mut urls: Vec<&str> = asked.lines().collect();
933 + urls.sort_unstable();
934 + assert_eq!(
935 + urls,
936 + [
937 + "https://mirror.invalid/bases/1111111111111111111111111111111111111111111111111111111111111111",
938 + "https://mirror.invalid/bases/2222222222222222222222222222222222222222222222222222222222222222",
939 + "https://mirror.invalid/bases/3333333333333333333333333333333333333333333333333333333333333333",
940 + "https://mirror.invalid/bases/4444444444444444444444444444444444444444444444444444444444444444",
941 + ],
942 + "the probe asked for something other than the four digests the pins name",
943 + );
944 + }
945 +
946 + /// A mirror that answers nothing is reported, not fatal.
947 + ///
948 + /// The whole argument for defaulting `QUASI_TYPE_MIRROR` to makenot.work is
949 + /// that it adds a source: every base still carries its upstream url and falls
950 + /// back to it, so a mirror that is down or not yet deployed is a slower first
951 + /// build. A build that failed here would turn a second source into a second
952 + /// dependency, which is the opposite of the point.
953 + #[test]
954 + fn a_mirror_that_holds_nothing_is_reported_rather_than_fatal() {
955 + let scratch = cut_root("mirror-empty", &PINNED);
956 + let output = run_cut_with_mirror(&scratch, "fetch", "https://mirror.invalid/bases");
957 + assert!(output.status.success(), "an empty mirror failed the build");
958 +
959 + let said = String::from_utf8_lossy(&output.stdout);
960 + assert!(
961 + said.contains("holds 0 of 4 pinned file(s)"),
962 + "the build did not say what the mirror holds, so a mirror nobody deployed \
963 + reads the same as a live one:\n{said}",
964 + );
965 + assert_eq!(
966 + cut_calls(scratch.path()).lines().count(),
967 + 2,
968 + "the cut did not run after the probe",
969 + );
970 + }
971 +
972 + /// No mirror named, no request made, and the log says which it was.
973 + ///
974 + /// `--build-arg QUASI_TYPE_MIRROR=` is the documented way to build against
975 + /// upstream alone, and a probe that ran anyway would be a request a person
976 + /// asked not to make.
977 + #[test]
978 + fn an_empty_mirror_is_not_probed() {
979 + let scratch = cut_root("mirror-off", &PINNED);
980 + let output = run_cut(&scratch, "fetch");
981 + assert!(output.status.success(), "the unmirrored cut failed");
982 +
983 + assert!(
984 + !scratch.join("curls").exists(),
985 + "a build with no mirror still made a request",
986 + );
987 + assert!(
988 + String::from_utf8_lossy(&output.stdout).contains("mirror: not consulted"),
989 + "the build did not say the mirror was skipped",
990 + );
991 + }
992 +
993 + /// `sealed` names no mirror either, because a sealed cut fetches nothing.
994 + ///
995 + /// The probe would be a request in the one mode whose point is that there are
996 + /// none, and its answer could not change anything: `--offline` means a file the
997 + /// seed is short of fails rather than being fetched from anywhere.
998 + #[test]
999 + fn a_sealed_cut_does_not_probe_the_mirror() {
1000 + let scratch = cut_root("mirror-sealed", &PINNED);
1001 + let output = run_cut_with_mirror(&scratch, "sealed", "https://mirror.invalid/bases");
1002 + assert!(
1003 + output.status.success(),
1004 + "the sealed cut failed:\n{}",
1005 + String::from_utf8_lossy(&output.stderr),
1006 + );
1007 + assert!(
1008 + !scratch.join("curls").exists(),
1009 + "a sealed build probed a mirror it may not fetch from",
1010 + );
1011 + }
1012 +
893 1013 /// `sealed` is the mode with a guarantee: the cut may not reach the network.
894 1014 /// With every pinned file seeded it runs, and both slots are cut offline.
895 1015 #[test]