Skip to content

Latest commit

 

History

History
392 lines (346 loc) · 21.9 KB

File metadata and controls

392 lines (346 loc) · 21.9 KB

SOS compositor gates

Date: 2026-08-26 (updated 2026-08-28)

SOS now has a minimal Smithay compositor with a nested development backend and a direct DRM/GBM/libinput backend. Both keep Wayland beneath the generated presentation layer while SOS owns client identity, surface ordering, focus, input quiescing, and revision evidence. The direct backend and boot-owned logind/tty session pass in the reference Debian VM. The selectable GDM path has also passed physical Intel DRM, built-in keyboard, touchpad, and touchscreen diagnostics on a Framework Laptop 12 development-live boot. That physical run is not an installed-product or release-promotion verdict.

Boundary

revision supervisor
    | boot / prepare / present / confirm
    v
permanent sos-experience-host PID
    | authenticated bounded control protocol
    | register / quiesce / arm / configure bounded shell surfaces
    |
    | Luau -> Experience API v4 graph -> retained GPUI
    v
authenticated GPUI surfaces
    | shell + shell overlay + independently hosted native applications
sos-compositor (Smithay 0.7.0)
    | one shell + one overlay + bounded native/compatibility toplevels
    | shared focus/input/activation policy
    +-- nested winit submit -> outer development compositor
    `-- libseat + udev + DRM/GBM + libinput -> KMS output

Luau still sees only the versioned Scene and provider capabilities. Shell-role window_space and shell_overlay nodes describe bounded composition intent; ordinary-role v4 graph roots open independently supervised GPUI/XDG surfaces. Only the trusted hosts convert measured bounds into compositor geometry or authenticate the corresponding surface. Luau never receives a Wayland object, socket, file descriptor, surface identity, PID, or arbitrary placement operation. compositor-control-protocol is a separate bounded newline-JSON ABI between the trusted permanent host and compositor.

The compositor creates a mode-0600 control socket in a caller-owned mode-0700 runtime directory. A client must present the launch token and a PID equal to the socket's SO_PEERCRED PID before opening its GPUI Wayland connection. The compositor records the Shell host PID as trusted shell authority. Each ordinary v4 host separately registers its own peer-credential PID as NativeApplication but receives no shell-control methods. Other Wayland client PIDs are compatibility clients. The listener serves at most 16 registered or registering control connections concurrently; every mutation still crosses the single compositor event-loop channel. A long-lived shell registration therefore cannot block an independently presented application from registering. This is a development-session authenticator. A production session must also isolate service users/credentials so another same-UID process cannot inspect launch credentials.

The policy admits one fullscreen shell, one bounded overlay, and at most eight application windows across SOS-native Wayland, ordinary Wayland, and the explicitly enabled rootless XWayland path. Application surfaces stay above the shell and within the active window-space rectangle. They are not embedded into GPUI or exposed as generated nodes. Floating mode uses bounded cascade placement and click-to-raise focus. A normal xdg_toplevel.move from a compatibility client, or a trusted source-native chrome gesture, moves the selected window and clamps it to the declared window-space. Repeating a Floating configuration preserves those positions; switching layout resets placement. Tiling recursively bisects the longest edge, allocates each branch in proportion to its window count, and produces a balanced quad for four windows instead of a fixed master and unbounded stack. Focus changes only stacking order and never reassign managed geometry or clipping rectangles. The initial scrolling mode uses overlapping horizontal cards while focus/scroll-position control remains a later protocol addition. Configure requests are constrained to the declared region, and each application's rendered surface tree and pointer hit test are clipped to its assigned rectangle. Hit testing uses the same buffer render origin as drawing, the managed window location minus the client's xdg_window_geometry inset. It refreshes a stationary pointer target at an ungrabbed press boundary. This remains true when an XDG client advertises a minimum size larger than a tile. Popups are clipped with their owning application; layer shell and arbitrary client placement remain outside this gate. A null XDG buffer unmaps a role without destroying it; compositor application counts, focus candidates, and layout are recomputed immediately. Attaching a later buffer maps the same still-live role again. Destroy and unmap therefore cannot double-decrement policy state, and an unrelated shell action cannot resurrect an unmapped client.

Client maximize, unmaximize, and fullscreen requests are currently denied for application roles by recomputing the complete selected layout. This prevents a client title-bar double-click from applying the one-window size to one tile in place and overflowing its assigned rectangle. Distinct maximize and fullscreen semantics remain future shell policy.

Ordinary shell content cannot overlap an application surface. Stock therefore reserves its top bar and command or agent rail outside window_space; opening a rail reduces the application rectangle. The dedicated shell overlay is the narrow exception: it stays above all applications, is clamped to the output, and can be moved only through the compositor-owned XDG move path. Hover is hit-tested by the compositor. An expanded overlay collapses to its stable action rectangle for the duration of a move, so the action can reach every output edge without stale expanded geometry constraining it. A stationary trusted move gesture is reported as activation; a geometry-changing gesture reports only its final bounded anchor. The host suppresses stale scene geometry until that anchor commit returns, preventing a dragged overlay from flashing back to its previous position. This lets Luau define the bubble and composer without gaining surface handles or global placement authority.

Activation fence

Input quiescing begins before the provider/state authority changes. After the candidate VM is prepared, the supervisor asks the permanent host to quiesce the exact candidate revision and waits for the compositor acknowledgement. The compositor removes keyboard and pointer focus, sends releases for keys/buttons that the old scene saw pressed, drops subsequent backend events, and suppresses the eventual release of any device held across the boundary. The host clears its bounded pending-event and gesture queues. Only then may the coordinator promote provider/state authority.

At present, the host asks the Luau worker to commit the prepared VM. When the worker confirms that commit, it performs this handoff on the GPUI event thread:

  1. Ask the already-quiesced compositor to arm the exact request/revision pair and wait for its current shell-commit sequence.
  2. Install the worker-confirmed retained scene and assets in the same host.
  3. Request the new GPUI frame.
  4. Let the compositor tag the first later root shell commit.
  5. Keep compositor input quiesced until that shell commit participates in a successful nested submit, or until the exact queued direct frame produces a DRM VBlank/page-flip event.
  6. Return the request ID, revision ID, shell-commit sequence, backend-submit sequence, and typed evidence to the host.
  7. Only then emit the supervisor's presented; confirm still proves the host loop responds before current advances.

If authority promotion aborts before presentation, discard resumes the exact revision-bound compositor fence before discarding the candidate VM. A control disconnect clears the fence without restoring focus to the unauthenticated stale shell. Successful presentation restores focus only to the still-live surface that owned it before quiescing.

Arming at the actual retained-scene handoff matters. Arming in the earlier supervisor request handler would permit an animated frame from the old scene to commit while the Luau worker was still switching. Waiting for the worker's commit result also prevents a visible revision from being certified while its active VM is unavailable. If arming then fails, the host exits so the supervisor recovers the durable committed revision instead of continuing with divergent runtime and visible state.

The compositor also advertises wp_presentation. In nested mode it completes client feedback and the SOS event after a successful winit-backend submit. That is stronger than GPUI's next-frame callback but the outer compositor may still discard or delay the buffer. In direct mode, queueing a DRM frame only records the candidate; frame_submitted on the matching VBlank completes Wayland feedback and emits drm_page_flip with the kernel sequence, timestamp, and clock domain. Only that event releases input and lets the host tell the supervisor presented.

If the permanent host dies, its control disconnect drops an armed fence and releases input. The supervisor starts the committed revision in a new PID; the compositor authenticates it, replaces any stale shell surface, and fences the recovery frame without restarting itself.

Reproduce

On a Linux development machine with Xvfb, Weston, and weston-simple-shm installed:

./tools/linux-compositor/verify-nested

The verifier builds the locked graph and creates only disposable state. It runs Weston's X11 backend in Xvfb, runs sos-compositor through its Smithay winit backend, boots the coordinated Linux session inside that compositor, activates the test-only stateful fixture, kills the exact host PID, waits for supervisor recovery, and maps weston-simple-shm as the compatibility client. It requires:

  • unchanged PID across the normal Luau activation;
  • exact authority/current-pointer agreement;
  • no gpui_next_frame fallback;
  • three compositor submit fences: boot, activation, and recovered boot;
  • native XTest -> Weston -> Smithay -> wl_keyboard delivery, exact persisted wayland text replacement, and Enter submission;
  • restricted input-method-v2 attach, pinyin preedit/candidates/selection/CJK commit, dead key composition, keyboard grab, popup, and cursor rectangle;
  • Wayland clipboard copy/cut/paste ownership through the native editor;
  • live Linux-provider note and video-frame mutation, system/resource model refresh, and worker rerender without another revision presentation;
  • semantic snapshot, next/previous traversal, focus, activation, scrolling, selection, copy/cut/paste, status waits, and service recovery after host kill;
  • at least one compositor-owned backend event suppressed while activation is quiesced;
  • a new authenticated PID after forced host failure;
  • shell/native-compatibility role classification, authenticated window-space configuration and bounded placement;
  • an opt-in real Xwayland process and bounded rootless xmessage window.

Expected leading output:

linux_nested_compositor_passed activation_pid=... restarted_pid=... revision_id=... evidence=nested_backend_submit

The composition-specific companion gate is:

./tools/linux-compositor/verify-composition-nested

It installs the Agenda, Media, Dashboard, and Remix reference packages into a disposable graph store, activates the resolved Dashboard graph through the durable graph supervisor, and inspects the composed semantic tree. It requires separate parent/child ownership, namespaced editable state, child-event routing, appearance propagation without forcing the custom Media visual system, the same host PID across graph activation, exact graph recovery after host death, and three nested_backend_submit fences. Its raw logs and stores can be retained with SOS_COMPOSITION_EVIDENCE_DIR.

The host creates the trusted shell-overlay GPUI window only when the active Scene contains its corresponding content. It reconciles that window after the current GPUI entity update finishes, so a revision can add or remove the surface without a reentrant entity update or leaving a transparent input surface behind. Set SOS_NESTED_OVERLAY_ONLY=1 on the broader nested verifier for the focused open/close check. Independent application Experiences use registry lifecycle and separate host processes.

The gate passes both on the ARM64 Ubuntu 24.04 development host and inside the reference Debian 13.6 ARM64 KVM guest. The Debian run activated revision 552f06968bbc5c69de3db581454f60d4303289f304eaaf47a6e9dc3200297cdb in unchanged PID 11310, then recovered the same committed revision in PID 11514. Its compositor evidence was:

boot:      commit_sequence=1  submit_sequence=928
activate:  commit_sequence=9  submit_sequence=936
recovery:  commit_sequence=14 submit_sequence=1009

The compatibility client mapped at (280, 140). Mesa software rendering is acceptable for this functional gate and makes no GPU, latency, or physical display claim.

Direct Debian VM gate

Run this only inside the disposable reference VM:

./tools/linux-vm/verify-direct-session

The verifier refuses a non-virtualized or non-Debian-13 environment. It builds the direct feature, records whether GDM was active, stops GDM, acquires seat0 through libseat's seatd backend, and restores GDM on every exit path. It requires the compositor's dark recovery view to page flip before starting the SOS shell. It then repeats the nested gate's activation, exact host kill and recovery, authority agreement, and compatibility-client checks, while rejecting both gpui_next_frame and nested_backend_submit evidence.

The ARM64 Debian 13.6/KVM run passed with revision 552f06968bbc5c69de3db581454f60d4303289f304eaaf47a6e9dc3200297cdb in unchanged PID 59723 and recovered the committed revision in PID 59849. The three shell fences were:

boot:      commit_sequence=1  submit_sequence=3
activate:  commit_sequence=14 submit_sequence=11
recovery:  commit_sequence=20 submit_sequence=17

All were emitted from DRM VBlank callbacks with monotonic kernel timestamps. The VirtIO driver reported output sequence 0; this remains exact driver metadata, not a fabricated counter. The compositor presented its recovery view both before boot and between the killed/restarted hosts, and the separate compatibility client mapped at (280, 140).

The 2026-08-27 x86_64 Debian 13 v4 graph campaign passed at source revision 294fe67 on kernel 6.12.101+deb13-amd64 in 15.887747472 monotonic seconds. It booted Stock graph b8d0745f…, activated candidate graph 27ddfcc9… in unchanged host PID 16868, physically presented a fault-injected rejected Stock candidate, physically restored 27ddfcc9… before completing discard, and restarted that accepted graph in PID 17301. The exact DRM-frame graph sequence was old, new, rejected old, restored new, restarted new. Input stayed quiesced from each candidate boundary until authority finalization or the restored frame; both held-input lifecycles contained one keyboard key, one pointer button and two touch contacts. Registry and authority agreed, no singleton pointer was created, and GDM and seatd were active after cleanup. The retained evidence is indexed from docs/progress.md; it is VM, not physical-panel or integrated-input evidence.

The direct backend remains intentionally one seat, but accepts multiple DRM devices and simultaneous connected outputs. Its default mirror policy prefers the internal eDP connector as the canonical logical canvas, falling back to the connected output with the largest pixel area. It renders that full canvas unchanged on the canonical output and fits it uniformly, without crop or distortion, on every other output. On Framework 12 this gives the laptop its native 1920x1200 canvas with no top/bottom bands; the 1920x1080 PiKVM capture receives the complete 16:10 scene at 0.9 scale with 96-pixel side pillars. "layout": "extend" retains the connector-sorted horizontal desktop when independent output space is wanted. Both policies survive connector and whole-device removal/addition. SOS_OUTPUT_MODE, SOS_OUTPUT_SCALE, and SOS_OUTPUT_ROTATION set boot configuration. A bounded JSON file selected by SOS_OUTPUT_CONFIG_FILE can change those values and layout on a DRM udev event; the backend recreates outputs without restarting the compositor. That file can also associate an exact libinput device name with a connector in input_outputs. Absolute mice, touchscreens, and tablets use the configured connector's logical geometry regardless of connector discovery order. They remain automatic on a single output, but fail closed when multiple outputs make an unconfigured route ambiguous or when the configured connector is absent. In mirror mode, absolute input on a fitted secondary output is transformed through the inverse scale and offset into canonical coordinates; an internal touchscreen remains identity-mapped to the panel. Relative pointers stay inside the shared mirror canvas. In extended mode they traverse the complete connected-output layout and clamp only to the nearest valid output rectangle, including across gaps between outputs. The direct VM gate changes Virtual-1 to 1024x768, 1.25 scale, and 180-degree rotation, then hot-adds Virtual-2 and requires a nonempty first frame and page flip on that second CRTC. The render graph composites either a live client cursor surface with its hotspot or a deterministic compositor-owned fallback above the shell.

The direct verifier creates kernel uinput devices named SOS Gate Keyboard, SOS Gate Relative Pointer, and SOS Gate Multitouch. Direct libinput reports their add/remove events and the compositor records keyboard, relative motion, button, and two-slot touch classes. It holds one modifier, one button, and two contacts through a successful activation and through an injected before-promotion authority failure. Both paths require keys=1 buttons=1 touches=2 at quiesce and suppressed releases after presentation or abort. Revision 250b1573… activated in PID 8641 and recovered after the exact host kill in PID 8888 with DRM page-flip evidence.

Seat revocation may make a KMS commit return EACCES just before libseat delivers PauseSession. The direct renderer treats only Smithay's explicit inactive-device or permission-denied DRM errors as that transition: it stops submitting, waits for the seat event, and resumes on ActivateSession. Other render failures remain fatal. The boot verifier selects s2idle explicitly, restores the prior /sys/power/mem_sleep selection on every exit, and proves that the same compositor owner survives VT pause/activation, the kernel freezer suspend test, and connector remove/reconnect. This avoids both a false failure on machines whose default is deep and a real crash in the DRM-master revocation race.

Activation quiescing includes touch. Existing contacts receive one wl_touch.cancel, their physical motion/release is suppressed across either a successful presentation or an aborted candidate, and contacts that begin and end while quiesced never enter the client epoch. A fresh down may reuse a released slot. Smithay keeps each slot focused on its down target, which is the Wayland-level capture behavior. The pinned GPUI Wayland client binds wl_touch and forwards down/move/up/cancel into the shared bounded Scene raw-pointer router, including multi-touch, surface capture, cancellation, gesture arbitration, and pointer-count updates. The first direct run exposed a mutable-client borrow across the touch callback; dispatching callbacks/frames after releasing that borrow fixed the crash. Core wl_touch has no pressure field, so finger samples explicitly report pressure unavailable. A separate pressure stylus is carried through tablet-v2; the direct VM gate observes normalized nonzero Scene pressure. The VM's software GBM/EGL stack lacks EGL_WL_bind_wayland_display; the cited run therefore used the compositor's advertised wl_shm path. The direct backend now also advertises explicit Linux dmabuf feedback from the formats importable by every renderer with a connected output. It validates each client dmabuf against those active renderers before completing buffer creation and updates the feedback across connector/device changes. This is independent of the optional EGL Wayland binding and leaves wl_shm as the safe fallback. The cited VM result predates that protocol path and remains a software-rendering result; none of it is a physical display, latency, touch, GPU-performance, suspend/resume, or thermal claim.

Source pin and remaining boundary

The crate pins smithay = 0.7.0. Its state, input, and nested-winit setup were reduced from the upstream MIT-licensed smallvil and anvil examples at tag v0.7.0, commit a166cf4c94b5aedc332a65aa1dd753e8148829c3; the required notice is preserved in crates/sos-compositor/SMITHAY-LICENSE.txt. See the Smithay repository and backend documentation.

Boot-to-SOS packaging now passes in the same VM: a systemd/PAM service owns the active logind tty1 session, waits for the recovery page flip before provider and host startup, receives its token through systemd credentials, and recovers both host and whole-session failures without seatd. The nested campaign additionally proves the restricted input-method-v2 client, non-Latin candidate/preedit/ commit, dead key, cursor rectangles, Wayland clipboard, Linux semantic service, live provider/media refresh, and focus/state recovery. The direct recovery layer is a readable compositor-owned panel with restart, rollback, safe-mode, and provider-disable controls. Multi-output, runtime mode/scale/rotation, rootless XWayland, tablet pressure, and executable bounded shaders now have VM or software-renderer evidence. The Framework development-live campaign has physical panel, Intel DRM, built-in keyboard, touchpad, and touchscreen evidence. Calibration accuracy, measured GPU/panel performance, full platform sleep/wake, latency, memory pressure, thermals, and an installed or immutable release gate remain open.