Repository navigation
Conversation
… pages dwl faulted for a zone go with it A page dwl reads before the zone writes it is charged to dwl, and a zone that kept its descriptor kept that page after the pool was gone, which no bound on live pools held. The proxy now keeps its own copy of each pool's descriptor and punches the pool's range (fallocate, FALLOC_FL_PUNCH_HOLE) once the pool and its buffers are deleted and no surface shows one. It refuses a pool it could not punch (not open for writing, sealed against writes, not shared memory) and seals each one it takes against a later write seal. The pools of a connection that ended are punched a second later, once dwl has read the hang-up, and count against the zone until then. The proxy's call list gains fstatfs and fallocate. The pool tests use memfds, as clients do.
…y hold, so 32 clients still fit The proxy keeps a copy of each wl_shm pool's descriptor to punch it, up to MAX_SHM_POOLS_PER_ZONE (256), and reserves them: under kryptikd's limit as it was, 32 clients' worth plus a few, it would have taken 30 clients. The limit grows by 256, and the descriptor test holds it to 32.
… two sessions still fit The proxy now reserves 256 descriptors for the copies of its zone's pools; under the test's old limit of 300 one session fit, not two.
…le are punched by the last pool_fd read the seals and only then added F_SEAL_SEAL, so a client thread could add F_SEAL_FUTURE_WRITE between the two. Its mapping keeps working under that seal, and the punch then fails with EPERM, leaving the pages dwl faulted charged to dwl. The seal now comes first, which makes the read final, and a failed read refuses the pool. wl_shm.create_pool takes any descriptor, so two pools may share one file, and punching the first to go cleared pages the other still shows. Each pool now keeps its file's device and inode. One that goes while another on its file remains hands that one its range and its charge, and the last punches the widest. So a large pool released before a small one on the same file stays counted until its pages are freed. A punch that fails is logged through the main loop's rate-limited log. The proxy's filter allows fstat, and fallocate only for a punch that keeps the size.
…xt sharp at 1.5 At a fractional output scale a zone's client could only draw at the next whole scale and have the compositor scale it down, which blurs text and costs memory. The proxy now lets wp_fractional_scale_manager_v1 through, and dwl's zone sockets offer it too: the compositor tells each window the scale of its output in 120ths, and Firefox, which uses the protocol by default, draws at that scale through wp_viewporter. It tells a zone only its output's scale, which wl_output already gives it rounded up. The proxy's tables are generated again with fractional-scale-v1.xml from wayland-protocols 1.49, the others unchanged byte for byte. A proxy test passes the request and the scale; wlprobe scale asks for it, and the desktop suite's zone window hears it through its proxy.
…nstead of starting a second With no remote, a second firefox in a zone whose browser is open only says that Firefox is already running. dwl now has an action for the browser key, focusorlaunch: it takes the zone's window whose app_id is the proxy's kryptik.<zone>.firefox-default, matched with the zone of the client's socket, views its first tag on its monitor when it is not shown and gives it the keyboard, and starts the zone's Firefox only when there is no such window. The claimed app_id is named once in dwl-config.h. The menu keeps every launch, as a persistent zone's passphrase is asked on its terminal. dwl's status names the zones whose browser has a window, which the chrome records in browsers; when it names the zone, b writes the zone's name to browser-front in the session's runtime directory instead. dwl takes that request only from a regular file of the session user's that no one else may write, in a directory of the user's that no one else may write, removes it before acting, and only ever puts a window in front. The focus record gains the window's app_id and the tags viewed. The desktop suite presses the key from another tag: tag 3 is viewed, Firefox has the keyboard, and no second Firefox starts. A request root wrote is removed unheard, the user's puts Firefox in front, and with the zone stopped the key starts Firefox.
… stamped it The browser key matches that app_id; printed where Firefox has the keyboard, it shows the value dwl-config.h should name whatever the later checks find.
…nd the design names the case left As the chrome writes it, so the check does not depend on the suite's own umask. A press of the key or b while the zone's Firefox is starting, before its window shows, still starts a second one. The app_id is the program name GTK takes, which Firefox sets to its remoting name.
…s shown and has the keyboard A zone file may say [desktop] hold_awake = true. It is off by default; personal and work set it, untrusted sets it false beside its reason, and the nic zone is refused it. kryptikd starts the zone's proxy with --hold-awake from it, and only then does the proxy let zwp_idle_inhibit_manager_v1 through. The protocol tables gain idle-inhibit-unstable-v1.xml from wayland-protocols 1.49, beside the six hashes they already name. zone-colours.h carries the same setting for dwl, from the same files. dwl offers the inhibitor on such a zone's socket alone, and counts one only from such a zone, on a mapped window shown on a lit output, while a window of that zone has the keyboard and the session is unlocked. A hold lasts four hours from when it began or a key or button was last pressed. dwl's status names the zone holding the screen awake, for the chrome. A keyboard focus change, an output's power and the cap's timer each check the hold again. Upstream dwl 4847f97 comes as patch 0003: a zone can now keep an inhibitor on a surface whose window it unmaps, and the check reads that surface's pointer. wlprobe gains an inhibit mode and an idle mode, and the desktop suite checks that personal's window holds zone 0's idle timer off, that the hold ends when a zone 0 window or the lock takes the keyboard, and that untrusted is offered no inhibitor.
…own, and the design says when a hold begins again Both were joined to the line before by a line continuation the edit did not mean. A hold that a lid close, a suspend or the lock ended counts again, with its four hours anew, once its window has the keyboard back after the unlock.
…unts, and the chrome names the holder kryptik-lock --idle waited on ext-idle-notify-v1's input-only notification, so no hold could keep the session unlocked. dwl now counts a hold only from a zone whose file allows it, on a shown window with the keyboard, for four hours from the last key or button press. So all three of the watcher's timers wait on the notification that heeds inhibitors, and bind version 1, which has it. The chrome records dwl's holdawake line in the session's runtime directory, as it records browsers, and the menu names the zone holding off the lock and the screens going dark. The desktop suite checks the record during the hold and after it ends, and that a 2 s idle lock started during the hold is still waiting 6 s later.
…l counts It still said the idle lock counted input alone, the protocol's version 2, which this branch changed.
…ry zone window in the suite does On tag 1 the chrome's zone 0 window has the keyboard, so a zone window maps behind it and gets none until the user picks it. The hold's block waits for the window to draw and has the host press Alt+j, which reaches it whether zone 0's window is there or not.
…er-focus-or-launch # Conflicts: # build/guest-tests/gui-check.sh # tools/desktop/kryptik-chrome # tools/image/gui-test.sh
…ld-awake # Conflicts: # compartments/kryptikd/src/zone.rs # compartments/kryptikd/src/zone/tests.rs # tools/desktop/wlprobe.c
…ake, beside the microphone's and the camera's holders A hold's usual window is a video in front, which covers the chrome that would name the holder. The bar takes it from dwl's own count; the marks' look each second draws it again when the holder changes.
A zone name is at most 12 bytes, so the name, the microphone's, the camera's and the awake marks take 71 with the terminator; 64 cut the last.
dwl ran in the root cgroup with no limit, and a zone's pool pages that dwl reads before the zone writes them are charged to dwl: a zone could make the compositor hold memory outside every limit. The session's last step, kryptik-launch --desktop, asks the launch daemon to move it into kryptik-desktop, then becomes dwl, so everything the desktop starts is inside. The group sits beside kryptik/ rather than in it, under a memory.max of a quarter of the memory and 1 GiB at least. The daemon moves only the asker, and refuses root, a pid another uid holds and any process in a zone's or a proxy's cgroup. A desktop it cannot bound starts anyway, and the session log says so. Checked by cgroup.rs's unit test, launch-desktop.py, serve.sh's S3h and gui-check's desktop-memory-bound. launch-desktop.py checks that the launcher becomes the desktop in its own process whatever the daemon answers.
wlprobe charge keep commits one unwritten 4 MiB buffer in untrusted, then, 4 s after it is drawn, drops the buffer and its pool and unmaps the window while it keeps the pool's descriptor, as a zone may. gui-check reads the desktop cgroup's memory.current before, while dwl shows the buffer, and once the pool is gone, and prints the rises on a desktop-charge line: the measurement the design asked for. It is what the proxy's punch will change: until the proxy punches the range, the pages dwl faulted stay the desktop's. tools/tests/desktop-tools.sh compiles wlprobe.c and kryptik-launch.c with the stage's hardening flags and checks their usage, so a change to either is caught in CI before a Distro build.
A zone that outlives the desktop and keeps its pools' descriptors keeps their pages charged to the desktop's group: they are shmem, which nothing reclaims without swap. The next login's dwl joined that same group and could start at its bound. join_desktop now removes the group first. rmdir succeeds on an unpopulated group whatever it still has charged, and its charges then count at the root. EBUSY keeps a group an earlier desktop's processes still hold.
dwl-zone-borders.py makes about seventy exact-string edits to dwl 0.8, and CI checked only that they apply as text: a C error in them showed hours into a Distro build, in stage 04. tools/tests/dwl-compile.sh fetches the pinned wlroots, wayland-protocols and dwl, trying each source as fetch-sources.sh does, and keeps each only with sources.lock's hash. It writes the two headers meson would generate for wlroots and every protocol's server header, then applies build/patches/dwl-0.8 and the zone-border edits in stage 04's order with its config. It compiles dwl.c and util.c with dwl's own warning flags, -fsyntax-only, against the pinned headers. A negative control, a member that does not exist, must fail. The compartment job installs the system headers and runs it; without them it exits 77.
…dwl's filter refuses exec The first part of the compositor's own confinement (docs/design/compositor-separation.md, step three). Once the status command runs, dwl forks a spawner. The spawner closes everything but one end of a socket, names itself dwl-spawner, is not dumpable and ends with dwl. It runs only the commands in dwl-config.h's spawncmds, each as dwl's spawn did, sent to it by their index. dwl then sets no-new-privileges and a seccomp filter that refuses execve, execveat, ptrace and process_vm_writev, and kills calls of another architecture or ABI. A dwl made to run code reaches no program but the table's, and cannot steer the spawner. gui-check's dwl-execs-nothing reads Seccomp: 2 and NoNewPrivs: 1 in dwl's status and finds its spawner child; every key the suite presses goes through the spawner.
wlroots 0.19.3's version.h.in names WLR_VERSION_STR, _MAJOR, _MINOR and _MICRO with #mesondefine, not @var@, which the placeholder check caught on its first run in CI.
Its second run in CI found the check working end to end, negative control included, and two errors that were the host's: Kryptik's edits call wl_client_get_user_data and wl_client_set_user_data, which libwayland 1.23 added, and Ubuntu 24.04's headers are 1.22. The script now fetches the pinned wayland (1.26.0) as it does wlroots, writes its wayland-version.h and its core protocol's server header, and puts its src/ before the host's include path.
…ock sleeps when the dock goes
DevomB
added a commit
that referenced
this pull request
Oct 16, 2026
… run sound-tools.sh #570's tools/tests/sound-tools.sh compiles the sound's C programs against alsa-lib, and exits 77 without its headers, which CI's lint job installs. Acceptance runs the host suites with --strict, where a suite that did not run fails the item: the desktop proof 38909683518 failed build/host-suites on that alone (92 suites passed, sound-tools did not run). HOST_PACKAGES feeds the apt install only, no cache key.
…ibasound2-dev with them
dwl-compile.sh and dwl-headless.sh build wlroots and dwl from pinned sources with headers only CI's Compositor build job installs. Elsewhere they exit 77, which run-tests.sh --strict, as acceptance runs it, counts as a failure. They are left to that job, as the compositor and desktop-identity suites are left to theirs.
With the levels kept in /run/kryptik-sound/control, control holds more than the socket. The same change goes into the stack's own merge of main.
kryptik-launch returns once the zone runs, so waiting on the launch did not wait for the probe, which keeps its window 10 s. The socket checks after it then found untrusted still running and could not start their own, and failed with no list to read.
…rd back Killing the background job ended the shell around su, not the probe under it, so the zone 0 window stayed in front with the keyboard and hold-ends-at-lock saw none come back. The window is ended by its command, as the suite's other probes are.
… any pages the proxy left memory.current counts dwl's own allocations too, and #565's proof showed it rising after the pool went (+2024 KiB shown, +2748 KiB gone), which says nothing either way about the punch. The pool is shmem, so the desktop cgroup's shmem in memory.stat shows its pages alone; the proxy's log says if a punch failed.
… zone ends first, and its INFO reads shmem
… the charge probe's fixes
…e the dwl suites to CI
DevomB
added a commit
that referenced
this pull request
Oct 16, 2026
It replaces main as this branch's source of #566 and #572. - kryptik-sound.c: marks/'s HOLDER_PATH beside #572's LEVELS_PATH and KEPT_PATH, as rehearsed. - wlprobe.c: the resolution recorded before, #546's charge probe beside the stack's. - dwl-zone-borders.py: #565's desktop set laid over this branch the way #585's merge of the two (82ea9b4) laid it over the stack's top. The result differs from that merge by exactly the lines #562 to #584 change above this branch. kryptik-sound.c and wlprobe.c equal #585's 700731c.
DevomB
added a commit
that referenced
this pull request
Oct 16, 2026
…says control keeps the levels #566's watcher reads MICREC each second. This branch moved the mixer's record of the microphone's holder to /run/kryptik-sound/marks, so after the merge the watcher read a file nothing wrote any more, and the microphone window never opened. MICREC now names marks/microphone, which anyone on the host reads. compositor-separation.md says control keeps the socket and the levels, as #572 keeps them there. Checked: MICREC's lines and the design's paragraph equal #585's 700731c byte for byte, as does kryptik-sound.c.
Back from tty2, dwl answers clients as soon as its seat is enabled again, and libinput reopens the keyboards through the seat a moment later. The single Alt+p the host sends on GT KEY-MENU-AFTER-VT could arrive in between and be lost, as in the compositor proof's run (menu windows: 1 before Alt+p, 1 after). The check now waits until dwl holds as many /dev/input/event descriptors as before the switch, says so when they do not come back, and names both counts if no menu opens.
…k waits for dwl's input devices
DevomB
added a commit
that referenced
this pull request
Oct 16, 2026
…ces counted by their links
DevomB
added a commit
that referenced
this pull request
Oct 16, 2026
… a keyboard added after it confines itself takes keys wlroots 0.19.3 gives each keyboard its keymap in a file made by shm_open under /dev/shm. dwl's Landlock rules refused that create, so a keyboard libinput added after dwl confined itself had no keymap. wlroots then left it out of dwl's keyboard group, and its keys reached nothing. This covers the switch back from the administration console, a keyboard plugged in after login, and one found again after a sleep. The rule beneath /dev/shm grants what that path does: create, open for reading and writing, open again read-only, truncate and unlink.
…lroots writes under /dev/shm
DevomB
added a commit
that referenced
this pull request
Oct 16, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Not for merging. A desktop proof of the compositor stack over the first desktop proof (#565).
What it holds.
The merge's three conflicts.
tools/desktop/dwl-zone-borders.py: both sides appended edits at the end of EDITS, with disjoint anchors. Both are kept, the proof's (hold-awake, browser in front) first, then the stack's (spawner, filter, Landlock,-S/-X, the call list).tools/desktop/wlprobe.c: the stack asks for the keyboard from the seat's capabilities, so the oldget_keyboardgoes. A zone's window hears the scale it is shown at, so the browser draws text sharp at 1.5 #520's scale block and A zone whose file allows it holds the screen awake while its window is shown and has the keyboard #527's inhibit block stay.tools/sound/kryptik-sound.c:HOLDER_PATHis the stack'smarks/microphone. The mixer's volumes and mutes outlast its restart and a reboot, never the microphone's grant #572'sLEVELS_PATHandKEPT_PATHstay, andholder()'s temporary file is already inmarks/.One change no conflict showed. #566's chrome watcher reads
MICREC=/run/kryptik-sound/control/microphoneand asks the mixer only when that record changes. With the stack the mixer writesmarks/microphone, so the watcher never saw a change and the microphone's window never opened. Here MICREC ismarks/microphone. The stack's own merge of main carries the same change in #561.Checked before pushing. Every required name in gui-test.sh's list (148) is reported by gui-check.sh, none twice. The dwl edits parse, and no conflict markers are left.
What the run should show. The stack's checks: dwl-execs-nothing, dwl-without-group, seat-is-dwls, dwl-confined-to-its-files (with the signal scope), desktop-memory-bound. The audit INFO of dwl's calls outside its list decides whether the filter refuses them next. Everything #565 shows should still pass.
Distro is dispatched on this branch only after #565's proof, and on the integrator's word.