Skip to content

Next main push: the software images, the toolkit and the browser - #470

Draft
DevomB wants to merge 258 commits into
mainfrom
batch-images-browser
Draft

DevomB wants to merge 258 commits into
mainfrom
batch-images-browser

Conversation

@DevomB

@DevomB DevomB commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Next main push: the software images, the toolkit and the browser, over main as it is. Each PR below has its own non-author read; this PR exists for CI on the combined head and as the record of what the push carries.

Proof: the Distro runs on this branch. Main's tree will equal a proven head of this branch, or this branch merged with main's newer proven pushes.

DevomB added 30 commits October 12, 2026 23:18
…es partition on the USB, /images on the ISO

The USB image's third partition is an ext4 made as the root's is, holding the signed list, its signature and
each image; the ISO carries the same files in its own filesystem, which the installer already mounts. The
ISO's layout pass grafts a one-block placeholder for the signature, so the root does not move between the
passes. A release without images carries neither, and the development controls stay with the build.
…ps them in the store for the root it installs

Stage 06 puts the signed images.manifest, its signature and each image as
NAME.img.zst into payload-VERSION, listed in the release manifest, so the
channel fetches them with the rest. Before it writes the slot,
kryptik-update apply copies the list from the payload and checks it against
the manifest's hash, requires its base to be the root the payload carries,
and decompresses each image into /var/lib/kryptik/images/NAME/ROOT-HASH.img,
checking the copy against the size and hash the list signs before it takes
its name. Once the trial is armed, the store keeps the lists of the roots in
the two slots and the images they name. The update suite runs the probe from
B's image once B is committed, and again after A was applied to the other
slot and B rolled back to; tools/tests/update-images.sh holds the staging's
refusals and the pruning to it.
build/service-scripts/image-store.sh, installed as
/usr/libexec/kryptik/image-store.sh beside esp-records.sh, holds the store:
images_keep LIST SIG STORE BASE READER keeps the images a list names for its
root, each copy checked against the signed size and hash before it takes its
name, and images_prune STORE ROOT-HASH... keeps those roots' lists and the
images they name. kryptik-update's staging is what is the payload's own: the
list's copy checked against the manifest, every named image carried, and
zstd as the reader, which now dies nowhere inside a redirection. boot-check
looks for the library; the host test lifts it.
…tition, for the root it installs

The medium carries the signed list and its images in a partition of their own
(USB) or under /images (ISO). Before anything is written, kryptik-install
requires the list to be signed and built for the root the medium installs;
once the state partition is made, it keeps each image through image-store.sh,
the copy checked against the size and hash the list signs. The install suite
runs the probe from the kept image on the installed system. The design note
says what recovery cannot do: kryptik-recover does not open the state
partition, so a restored slot finds its images only if the store still holds
that release's.
…naming one must name them too

A requires line, one per image in image order between the image and needs
lines, names the images an image is built over. kryptikd refuses to start a
zone that names an image without the images it requires, naming the missing
one, and check --images refuses the same at build time, before a browser
named without its toolkit could fail at the loader.
Whoever can write the stick can craft that filesystem, and the installer mounts it. FAT is the parser the
ESP already needs; ext4's is far larger. An image of 4 GiB or more, past what FAT32 holds, fails the step.
…partway removes the copies already made

The payload's .zst files are read again after the manifest checked them, so a
file swapped in between could decompress into /var without end before the size
check refused it: images_keep now gives the reader the signed size, and
kryptik-update's reader stops one byte past it, so an overlong image fails the
size check instead of filling the state partition. A refusal partway through
images_keep removes the copies of the images before it rather than leaving
them until the next update prunes them.
…n image no further than its signed size

The medium's kryptik-images partition is now a FAT32 made like the ESP, a
filesystem the installer already mounts, not an ext4 whose crafted images its
maintainers do not treat as a boundary: kryptik-install mounts it vfat,
read-only, nosuid, nodev and noexec. Its reader stops one byte past the size
the list signs, as kryptik-update's does.
# Conflicts:
#	docs/design/software-delivery.md
…esystem and directory have names of their own
…rospection, over the base system

Fourteen sources, pinned in build/images/versions.env and locked in build/images/sources.lock, apart from
the root's: they never reach the root, so they are no input of stage 04 and leave its cache standing.
fetch-sources.sh lists them per image (--list-image) and the root's alone (--list-root); stage 04's
licences are the root's sources only, and the image installs its own, which stage 06 checks. Every check of
the lock reads both. GNOME publishes an unsigned .sha256sum named for the stem, a new kind the provenance
check reads by file name.

gdk-pixbuf reads PNG alone, built in; pcre2 has no JIT; cairo keeps no tracer or script interpreter;
fontconfig's and GTK's configuration is under /usr/etc. at-spi2-core is patched to build ATK without
libxml2, which only the bridge's tests use. The zones suite runs fc-match and gtk-query-immodules-3.0 in a
zone that names the image.
… each set of sources against its own lock

The redirector sent the runner to a mirror that lacked 2.15.0. The test knew one lock and the old kinds.
… that ships to no one

A definition's image_over names the images whose trees lie under it, above the sysroot, while its rows
run; they are built first, and their fingerprints are its inputs. The tree check takes every lower
layer and refuses a file any of them has. image_ships=no makes a layer that only builds over it use,
which stage 06 neither packs nor lists; a shipped image built over shipped ones gets the list's
requires line, in the list's own order.
…names them, hicolor's through Debian's WKD

pcre2's SECURITY.md names its key; cairo's signer serves his under his GNOME account; FreeType publishes
only the long id, which the pinned key ends in and which has signed every release since 2.12.0.
…s and needs lines follow the image lines' order

"probe-x/record" sorts before "probe/record", as "-" is below "/", while the image lines put probe first;
kryptikd refuses a list whose requires or needs lines are out of image order.
DevomB added 24 commits October 14, 2026 22:54
… the work that bounds it

A page dwl reads before the zone writes it is charged to dwl, and a zone that keeps a pool's descriptor keeps it after the pool is gone, which the proxy's bounds on live pools do not hold. The item orders the measurement first, then punching each pool's range once gone and a memory.max on the session's cgroup.
…ped, sound, power, brightness, layouts, locked keys), the zone filter's soft refusals for thread scheduling and I/O priority, sources saved from main alone, F4d's group lines

# Conflicts:
#	.github/actions/prepare/action.yml
#	.github/workflows/ci.yml
#	build/config/image-programs.txt
#	compartments/kryptikd/src/rootfs.rs
#	compartments/kryptikd/src/spawn.rs
#	compartments/kryptikd/src/zone.rs
#	docs/roadmap.md
…a zone's open Firefox forward instead of starting another
ESR 153's configure takes --disable-nodejs, but its build runs Node.js
for the devtools debugger's modules all the same, so the browser's build
stopped there. The node layer unpacks the program alone from Node.js's
linux-x64 build, whose checksum list a releaser's pinned key signs, and
the browser's mozconfig names it.

verify_sums fetches a cached checksum list again when it does not name
the file: Node.js names every release's list SHASUMS256.txt, so the one
cached for a release would refuse the next.
./mach install ran without MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=none,
which the build step sets. Left unset, mach makes virtualenvs of its own
and runs pip for its optional packages, in a chroot with no network: it
carries on when they fail, but the step's environment was not the
build's.
make toolchain runs make sources, which fetches every software image's
sources from upstream as well. The Stages 01-02 job restores only the
root's set, so it downloaded the images' every run, and failed when one
of their hosts was down (icon-theme.freedesktop.org timing out, with no
copy of hicolor-icon-theme 0.18 at Gentoo's mirror). No stage there reads
them; the images' job restores their cache and fetches what it lacks.
…mages before it writes anything

The installer's comment called the medium's image list signed, but it
only checked that a .sig was there and that the list's base line named
the root it installs. images_keep then copied the sizes the unverified
list named onto the new state partition, after the slots and ESP were
written, and a malformed line ended the install halfway.

kryptikd check-images DIR BASE now checks a medium's list as a kept one
is checked: its signature as nobody on a copy (kryptik-update
check-images, so in the image namespace, by the release key), then
images::parse, the root it names, and every image it names present in
DIR at the size it signs. medium-root.sh's medium_images_ok runs it for
the root the signed command line names, before the partition table is
written, and kryptikd joins the tools checked up front. Each image's
hash is still checked as it is copied.

verified_list and the new check share the signature step. The design
says a corrupted mapping is refused until the machine restarts, as the
code does.
…er through a link or a FIFO

An ISO's Rock Ridge can carry links, FIFOs and device nodes. read_bounded
now opens with O_NONBLOCK and O_NOFOLLOW and requires a regular file, so
a FIFO named images.manifest is refused at once instead of blocking the
installer; check_medium looks at an image with symlink_metadata, as the
design's "a regular file" and open()'s own rule say.
…on, and a mapping is used again only if it is the image's

The rest of the non-author review of image delivery:
- kryptik-update pins each NAME.img.zst the manifest lists, as the root's
  four files are, and payload_path names its descriptor, so stage_images
  decompresses the bytes verify_payload hashed even if the payload
  directory changes in between.
- kryptikd uses /dev/mapper/kryptik-image-NAME-HASH again only if
  veritysetup status shows it is dm-verity with the root hash the list
  signs, besides not corrupted.
- An image's usr must be its own directory (symlink_metadata), not a link
  that would stack something else over /usr.
- apply reads the running root's hash from the kroot table alone.
- The ISO is mounted nosuid,nodev,noexec, as the stick's images partition
  is, and read_bounded looks at a path before it opens it, so no device
  node there is ever opened.
…the image list kryptikd checked

Pinning each listed image from descriptor 7 put the third on 9, the
descriptor apply and rollback hold the update lock on; reopening it
released the flock for the rest of the apply. The pins now start at 10,
and the snapshot test pins three images under a lock on 9 and finds the
lock still there.

The installer read the medium's list and signature again for
images_keep after kryptikd had checked them, and a medium can answer a
second read with other bytes. kryptikd check-images DIR BASE OUT now
writes the bytes it checked into OUT, the installer's private directory,
and images_keep and the count read them from there.
…ot leave one unreadable

While a call's output is redirected, bash keeps its own copy of the output on
descriptor 10, close-on-exec. A pin opened on a fixed 10 then took that place
and stayed close-on-exec, so stat, sha256sum and zstd could not read it, as the
snapshot test's redirected verify_payload showed. exec {fd}< takes the lowest
free descriptor from 10 up, past any bash holds.
…he medium's signed image list and images before it writes anything
…gh the descriptors they were hashed on, and a mapping is used again only if it is the image's
…thout a canary, each with its reason

Stage 06 refused the image on these seven alone: the Rust in libxul, NSPR's, freebl's and the codecs' assembly carry no CET note, and three small libraries have no function -fstack-protector-strong guards.
…without a CET note and three without a canary, each with its reason
…ne, the camera granted to one zone at a time, MIC and CAM marks on the zone bar, exFAT, display scaling, the sound decision), mirrors for sources only their own host serves, run-ovmf refusing a second head with --s3, and each proxy traced alone

# Conflicts:
#	build/guest-tests/gui-check.sh
#	compartments/kryptikd/src/rootfs.rs
#	compartments/kryptikd/src/spawn.rs
#	compartments/kryptikd/src/zone/tests.rs
#	docs/roadmap.md
#	docs/status.md
#	tools/image/gui-test.sh
#	tools/image/zones-test.sh
…t answer: Debian's pool with upstream's signature and Alpine's v3.22 distfiles for the one, the BLFS mirror for the other
…eads it in

mke2fs.conf gives a file system under 512 MiB blocks of 1024, and ext4 cannot mount a block smaller than the dm-verity device's own, so a zone could not start on the probe image (mount: EINVAL) after an install or an update. make_ext4 asks for 4096, and pack_image refuses an image of any other block size.
…t, as it does the broker's socket

The set is stacked under /run/kryptik, root's alone, and pid 1 binds it after taking the zone's ids, so looking up the bind's source failed with EACCES and no zone that names images started. intermediate_main opens the set O_PATH beside the broker's and the Wayland socket's descriptors, after the mount namespace is unshared and before the id switch, and pivot_into binds /proc/self/fd/N.
…ough a descriptor opened as host root, as it does the broker's socket

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant