Skip to content

RFC-0002 — Core runtime

Status: proposed v0 · 2026-10-07 · Implementation target, pending owner review.

Core ships a Hyprland plugin and hypruned, a per-session user daemon. The plugin owns only work that requires compositor authority or the live graphics context: capture, input dispatch, GPU resources, scene rendering and final state commits. The daemon owns public IPC, grants, package discovery/validation, supervision, filesystem work and persistence. Both belong in core so their private bridge can evolve together without becoming a public SDK ABI.

The daemon starts the reference shell or an explicitly configured alternative with scoped launch credentials. It never loads third-party code into Hyprland. M0 may keep trusted camera simulation in the plugin; loading, parsing and expensive geometry processing must leave its render callbacks. Do not put all rendering into a second compositor: that sacrifices direct window integration and creates a second input authority. Do not put package managers or sockets with blocking I/O inside the plugin.

The plugin is authoritative for runtime, output IDs, focus, surfaces and player pose. The daemon is authoritative for grants, installed package identity and shell leases; lease effects become visible only after plugin acknowledgement. The daemon composes the public state and assigns revisions after acknowledgement. No speculative success response.

Use a private inherited Unix socketpair from a core-owned launcher/adapter handshake, or an equivalently authenticated private connection if Hyprland loads the plugin first. Pin a private bridge version to the core build. Messages carry a session epoch, bounded command ID, operation and deadline; responses distinguish applied/rejected. No GL pointers, compositor objects or client buffer pointers cross processes. Exact private encoding is internal to core and not RFC-0003. Public IPC compatibility does not imply private bridge compatibility.

If the daemon dies, a plugin watchdog releases input grabs and restores ordinary desktop presentation within 1 second. If the plugin unloads or Hyprland exits, the daemon invalidates the session, disconnects clients and closes leases. Reconnection starts a new session and snapshot; do not replay pending mutations. A core-owned compositor binding for emergency exit always works without the shell or daemon. World mode must not interfere with the session lock; locking cancels interaction and stops captures immediately.

Owner Allowed work Forbidden work
Hyprland event/render thread Compositor API, GL object creation/destruction, guarded scene drawing, queued input/focus application Blocking IPC, file access, parsing large assets, extension code
Core CPU workers Immutable glTF decode, collision build, navigation preparation Compositor objects or GL context access
Daemon loop/workers Framing, authentication, registry, validation, persistence, process supervision Calling Hyprland internals or mutating plugin memory
Extension process Granted broker APIs, its own computation and optional approved Wayland surface Native plugin hooks, raw seat ownership

Use bounded queues and immutable scene snapshots. Workers publish prepared CPU assets with generation IDs; stale generations are discarded after world reload/unload. Only the render owner uploads and retires GPU resources. No detached threads retaining plugin pointers. Shutdown stops submissions, cancels workers, joins them, drains safe cleanup and unregisters hooks before unloading code.

Inside a Hyprland render pass, only draw using already prepared resources and read an immutable frame snapshot. Any operation that might trigger compositor rendering MUST be queued until the outer pass is fully unwound. This includes cursor visibility/shape changes, workspace changes, focus operations, window snapshot FBO capture, layer mapping/unmapping and teardown of compositor-owned resources.

A callback named “post windows” is still inside the pass. It is not a safe deferred phase. Queue work to the event loop after outer endRender, assert render nesting depth is zero, and make the required graphics context current via the supported adapter before capture/upload/cleanup. A nested snapshot render must skip Hyprune’s pass through a scoped capture/reentrancy guard. Never assume a zero-delay timer by itself proves the phase is safe. The adapter’s exact hook must be verified against the pinned Hyprland build.

The prototype’s hardware cursor update re-entered rendering and corrupted the active monitor render state, later crashing in CMonitor::useFP16. A software cursor in nested tests concealed it. Test both nested and real DRM sessions before declaring this solved.

The diagram on Architecture shows the ownership boundary. Per frame:

  1. After previous outer frame: process bounded deferred compositor operations, update capture leases, unsuspend participating clients, deliver eligible frame callbacks, refresh dirty captures, stage budgeted GPU uploads. Write next immutable frame state.
  2. Simulation: fixed 60 Hz trusted movement/physics, at most four catch-up steps, then drop excess elapsed time and report overrun. Input timestamps use a monotonic clock. Late worker results retain the prior valid scene; never block a frame.
  3. World pass: render opaque geometry to owned color/depth buffers, then transparent geometry, resolve MSAA if enabled. Apply world lighting and tone mapping once.
  4. Surface pass: sample compositor-approved window textures with the capture’s geometry/UV snapshot; depth-test against world geometry. Preserve desktop color through a separate output transform, with no artistic bloom/exposure on application pixels.
  5. Composite: present world + surfaces in the room output/workspace below top/overlay shell layers. Preserve compositor GL state through the adapter’s scoped state guard; never clear unrelated outputs or their damage.
  6. End outer frame: collect bounded timing/damage data, enqueue future work, return to Hyprland. The deferred phase starts only after Hyprland has completed its own render teardown.

Initial engineering budgets: 2 ms per frame for incremental GPU uploads, 4 ms capture work, 512 MiB world GPU assets, 256 MiB capture cache; all measured and configurable downwards. These are targets, not performance claims. Reuse previous textures under load, evict unfocused captures first and report degraded freshness. Avoid glFinish, synchronous readbacks and full-scene recapture. Multi-monitor simulation advances once per tick, not once per output.

Hidden workspaces suspend clients and suppress frame callbacks. An explicit capture lease identifies windows visible on an active world surface; the adapter must call the supported equivalent of setSuspended(false) and pace frame callbacks for those clients while needed. Snapshotting alone does not keep a browser/video alive. Restore prior suspension behavior and release leases on hide, disconnect, lock, close and world exit. Idle or obscured world surfaces do not get an unlimited capture exemption.

Track damage/committed buffer identity and geometry generation; unchanged windows reuse textures. Bound capture cadence by visibility and screen descriptor maxFps. Each captured texture carries matching logical size, crop, scale, transform and surface-tree offsets. Do not combine yesterday’s texture with today’s window geometry. Weak compositor handles are resolved on the owner thread; dead handles produce an unavailable surface, never a stale pointer dereference.

M0: exact Hyprland compatibility gate, unload-safe plugin, static original world, deferred queue assertions, emergency exit and daemon hello/snapshot. M1: live window capture with hidden-workspace liveness, input routing, shell leases and full RFC-0003 control. M2: validated world swaps and sandboxed extension hosts. M3: measured multi-output behavior and external conformance.

Required failure tests: cursor changes under hardware cursor rendering, nested snapshot recursion, workspace switch during capture, daemon/shell death, window close mid-drag, output removal, scene reload with pending worker results, repeated plugin unload/reload, session lock, and capture/VRAM saturation. Core must document actual support; this RFC is not evidence that these tests pass yet.