stack-RFC-021 — Kroma ↔ Koder compositor (kompose) & DE (Kolide) contract

accepted

Co-design the Koder native GPU UI toolkit (Kroma, stack-RFC-019) WITH the Koder display server (kompose — a wlroots Wayland compositor) and the desktop environment (Kolide), the way GTK/GNOME/Mutter co-evolved — but as CLEAN PROTOCOLS between independent layers, NOT a monolith. The high-leverage decision, taken now because the Kroma windowing layer (S4e) is about to be wired: Kroma's window/surface layer is a BACKEND ABSTRACTION (GDK-style) so it stays multi-backend + standalone. Backends: HeadlessBackend (testing), WinitBackend (#1, generic Wayland/X11/Win/Mac — already runs on kompose), KoderWaylandBackend (#2, deep kompose/Kolide integration: wlr-layer-shell, tiling, session, a11y, Koder CSD). Shared seams: Verge (the design system, across shell + apps), one language-agnostic tiling tree (port koder_tiling Dart → neutral/Rust), AccessKit → the Koder a11y bus, dmabuf zero-copy present. The Kroma Wayland backend is validated headlessly against kompose via koder_test_compositor.

Owner's question (2026-06-24): "Just as GNOME was designed with GTK in mind, should we architecturally already think about kompose + Kolide while building the native GPU UI?" Answer: yes — co-design the SEAMS now, not a monolith. This RFC records that decision and the contracts it spawns. Taken now because S4e is about to wire Kroma's windowing — the one place this is cheap to get right and expensive to retrofit.

1. The pieces already exist (this is not hypothetical)

  • kompose (infra/linux/x) — the Koder Wayland compositor + display server

    (wlroots-based). The "Mutter".

  • Kolide (infra/linux/kolide) — the desktop environment. The "GNOME Shell".
  • Koder Shell (infra/linux/shell) — the shell surfaces.
  • Kroma (engines/sdk/kroma, stack-RFC-019) — the native GPU toolkit. The "GTK".
  • kodertestcompositor — a headless kompose test harness (spawns kompose with

    WLR_BACKENDS=headless over a test IPC) → the CI path to validate a Kroma Wayland backend with NO physical display.

  • koder_tiling — the recursive tile tree, but Flutter/Dart today (coupling).

2. Decision: co-design the seams (protocols), keep the layers independent

GTKGNOMEMutter are separate projects with clean protocols (Wayland, libadwaita, portals) — the co-design lives in the contracts, not a fused codebase. We do the same. A monolith is rejected (D9 reversibility; Kroma must stay usable standalone on WinMacdev — that constraint is what forces the abstractions to stay honest).

3. The seams (each an independent contract, co-designed now)

3.1 Backend abstraction — the load-bearing one (enacted this slice)

Kroma's windowsurfaceevent layer is a Backend trait (like GDK's X11WaylandWin/ Mac backends). The app produces a Scene; the Backend hosts + presents it.

  • HeadlessBackend — render to texture → PNG; the test/CI path (S0b–S4d use it).
  • WinitBackend (#1, S4e) — generic winit window; runs on WaylandX11Win/Mac, so it

    already runs on kompose as a normal client (wgpu's Wayland swapchain presents via dmabuf → zero-copy to the compositor). Good for ordinary apps (kterm, Grid, …).

  • KoderWaylandBackend (#2, S4f) — a Koder-native Wayland client for *eep DE

    integration*that winit's generic window can't express: wlr-layer-shell (panels/ shelloverlays for Kolide), the shared tiling tree, the Koder sessionportal, the a11y bus, Koder client-side decorations. This is where Kroma + kompose + Kolide feel like one system.

Rule: a widget/event path NEVER hard-wires winit; it talks to Backend. (This is the single change S4e makes vs the naive plan.)

3.2 GPU present — Wayland dmabuf, co-designed at the shell boundary

Both Kroma (VellowgpuVulkan) and kompose (wlroots) are GPU-native; Wayland linux-dmabuf already keeps the app→composite→scanout path on the GPU (no CPU copy) — wgpu's swapchain gives this for free on the winit backend. The co-design WIN is the shell↔app boundary: damagepartial-present, app-thumbnailpreview surfaces for the shell, and the tiling geometry negotiated GPU-side — things a generic toolkit+compositor pair negotiate loosely and a co-designed pair can tighten. (We do NOT claim GTK "can't" zero-copy — it can on Wayland; we claim co-design lets us optimize the boundary.)

3.3 Verge — the shared design system (the "Adwaita")

Kolide's shell chrome and Kroma apps consume the same Verge tokens → one look. Endgame: Kolide's UI is rendered ON Kroma (the shell built with the toolkit, like GNOME Shell on GTK/St), retiring the interim GNOME-parity path. Near-term: at minimum share the token layer.

3.4 One tiling tree (resolve the koder_tiling coupling)

koder_tiling (the TileNodesplitresize tree) is Flutter/Dart today; kterm is its 3rd consumer. The tree MODEL must become language-agnostic (a neutral spec, with a Rust impl for Kroma) so DE window-management (Kolide) and in-app tiling (kterm/Grid) share one model. Co-design the model now; don't let a 2nd Rust tiling tree grow ad hoc.

3.5 a11y / CSD / session

AccessKit builds the a11y node tree off the Kroma Widget tree → bridged to the Koder a11y bus (the AT-SPI equivalent). Client-side decorations + the session/portal are Koder protocols co-designed with kompose. These satisfy the stack-RFC-019 §4.2 a11y gate.

4. Validation

The Kroma Wayland backend (KoderWaylandBackend, and WinitBackend-on-Wayland) is tested headlessly against kompose via koder_test_compositor (headless wlroots + IPC) — the same off-laptop, no-display discipline as S0–S4d, but now exercising the real compositor instead of Xvfb.

5. What this RFC ratifies vs what it spawns

  • Ratified (by /k-go): the direction — co-design seams not monolith; the Backend

    abstraction; Verge-shared; one tiling tree; AccessKit→Koder-bus; validate via kodertestcompositor.

  • Spawns (follow-up specstickets, co-designed with komposeKolide owners): the

    concrete Wayland protocol set the KoderWaylandBackend speaks (layer-shell usage, CSD, session), the language-agnostic tiling-tree spec, the a11y-bus bridge.

6. Immediate effect (this slice)

engines/sdk/kroma/src/backend.rs introduces the Backend trait + HeadlessBackend (the existing render path, refactored behind it). S4e adds WinitBackend; S4f adds KoderWaylandBackend. Orthogonal to the AI-compute program (stack-RFC-020).