Skip to main content

max / alloy

Mount the install target under /run, not under /mnt /mnt is a symlink to var/mnt on the ostree live system, so `systemctl --root=/mnt/alloy-target/...` resolved the greetd drop-in to a path outside the root it was given, refused to read it, and reported the unit it could not finish loading as `bad`. The acceptance check read that as a machine with no way to log in and failed the install, which wipes the target: every client-profile install partitioned the disk, wrote three gigabytes, and then erased them. Found by booting the ISO in qemu, which is the only place it could have been found. Every unit file involved is correct, and the same check against the same deployment reads `enabled` once the mountpoint is somewhere that does not resolve through a symlink. /run rather than /var/mnt: a real directory on every Linux, never a symlink, and it does not hardcode ostree's layout into a path that has no other reason to know it.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-09 22:14 UTC
Signed with PGP, not checked
Commit: ca77276396be7f750971b26cb21b7d7c9df6a9c6
Parent: f1d9160
1 file changed, +38 insertions, -6 deletions
@@ -495,7 +495,23 @@
495 495 ///
496 496 /// So the sequence is mount, configure, finalize, unmount, and this is the
497 497 /// mountpoint used throughout.
498 - const TARGET_MOUNT: &str = "/mnt/alloy-target";
498 + ///
499 + /// **Under `/run` and not `/mnt`, and the difference is load-bearing.** On the
500 + /// live medium `/mnt` is a symlink to `var/mnt`, which is ostree's own layout.
501 + /// `systemctl --root=/mnt/alloy-target/...` then resolves a drop-in to a path
502 + /// under `/var/mnt/...`, decides it has escaped the root it was given, refuses
503 + /// to read it, and reports the unit it could not finish loading as `bad`. The
504 + /// acceptance check in [`acceptance_checks`] reads that as a greeter that would
505 + /// not start and fails an install that was fine, so every client-profile
506 + /// install died at the last step and wiped the disk it had just written.
507 + /// Measured in a VM 2026-08-09: the same check against the same deployment
508 + /// reads `enabled` when the mount is at `/run/alloy-target`.
509 + ///
510 + /// `/run` because it is a real directory on every Linux, tmpfs, never a
511 + /// symlink. `/var/mnt` would work on this image and is what `/mnt` points at,
512 + /// but it hardcodes ostree's layout into a path that has no other reason to
513 + /// know it.
514 + const TARGET_MOUNT: &str = "/run/alloy-target";
499 515
500 516 /// The activity light's lit and dim glyphs.
501 517 ///
@@ -5557,10 +5573,26 @@
5557 5573 ),
5558 5574 );
5559 5575 assert_eq!(shown[1], "udevadm settle");
5560 - assert_eq!(shown[2], "mkdir -p /mnt/alloy-target");
5576 + assert_eq!(shown[2], "mkdir -p /run/alloy-target");
5561 5577 assert!(shown[3].starts_with("lsblk"), "{}", shown[3]);
5562 5578 }
5563 5579
5580 + // Where the target is mounted decides whether the acceptance checks can
5581 + // read it. `systemctl --root=X` refuses any path whose resolution leaves X,
5582 + // and on this image `/mnt` is a symlink to `var/mnt`, so a mountpoint there
5583 + // made systemd report a unit it could not finish loading as `bad` and
5584 + // failed every client-profile install at the last step. The constant's doc
5585 + // comment carries the measurement; this is the part a future edit trips
5586 + // over.
5587 + #[test]
5588 + fn the_target_is_mounted_somewhere_that_is_not_a_symlink() {
5589 + assert!(
5590 + TARGET_MOUNT.starts_with("/run/"),
5591 + "{TARGET_MOUNT} is not under /run; a mountpoint under /mnt resolves \
5592 + through ostree's var/mnt symlink and breaks `systemctl --root`",
5593 + );
5594 + }
5595 +
5564 5596 // The summary can only show what is known before anything runs. Everything
5565 5597 // past the first discovery depends on values that do not exist yet, and
5566 5598 // inventing lines for them would be the summary claiming to know more than
@@ -5609,7 +5641,7 @@
5609 5641 }
5610 5642
5611 5643 /// A deployment directory shaped the way ostree names them.
5612 - const DEPLOYMENT: &str = "/mnt/alloy-target/ostree/deploy/default/deploy/abc123.0";
5644 + const DEPLOYMENT: &str = "/run/alloy-target/ostree/deploy/default/deploy/abc123.0";
5613 5645
5614 5646 /// The configure half, against a deployment directory as discovered.
5615 5647 ///
@@ -5803,7 +5835,7 @@
5803 5835 assert!(useradd.contains("--home-dir /var/home/max"), "{useradd}");
5804 5836 assert_eq!(
5805 5837 mkdir,
5806 - "mkdir -p /mnt/alloy-target/ostree/deploy/default/var/home/max"
5838 + "mkdir -p /run/alloy-target/ostree/deploy/default/var/home/max"
5807 5839 );
5808 5840 }
5809 5841
@@ -5887,7 +5919,7 @@
5887 5919 fn the_stateroot_var_is_two_levels_above_the_deployment() {
5888 5920 assert_eq!(
5889 5921 stateroot_var(DEPLOYMENT).unwrap(),
5890 - "/mnt/alloy-target/ostree/deploy/default/var"
5922 + "/run/alloy-target/ostree/deploy/default/var"
5891 5923 );
5892 5924 }
5893 5925
@@ -5896,7 +5928,7 @@
5896 5928 // var directory from.
5897 5929 #[test]
5898 5930 fn a_path_that_is_not_a_deployment_is_a_named_failure() {
5899 - let error = stateroot_var("/mnt/alloy-target").unwrap_err();
5931 + let error = stateroot_var("/run/alloy-target").unwrap_err();
5900 5932 assert!(error.contains("not an ostree deployment"), "{error}");
5901 5933 }
5902 5934