GSoC 2026 @ CCExtractor: Final Work Product Submission by Rahul Tripathi

Build a COSMIC-based Wayland Session for Regolith

Organization: CCExtractor

Contributor: Rahul Tripathi (@Rahul-2k4)

Mentor: Soumya Ranjan Patnaik

Repository: regolith-linux/regolith-session (target), work tracked at Rahul-2k4/regolith-cosmic-gsoc-proof

Table of contents

Project overview

Regolith is a tiling window manager environment built on Sway. Its Wayland session depends on GNOME for session management and settings: gnome-session-bin runs the session, eight gnome-settings-daemon services back the settings surface, and two custom daemons, regolith-inputd and regolith-displayd, exist only to translate GNOME schemas into Sway commands. That coupling breaks whenever GNOME changes its APIs, and it already fails outright on COSMIC-based hosts, since COSMIC doesn't carry the GNOME settings schemas those daemons read.

COSMIC, System76's desktop environment, ships a maintained, non-GNOME alternative: a session manager, a settings daemon, and Wayland-native protocols Sway already implements. This project's goal was regolith-session-cosmic, an experimental Regolith session backed by COSMIC instead of GNOME, plus the packaging needed to install it without pulling from Pop!_OS repositories or building everything from source. It builds directly on sandptel/cosmolith, the COSMIC-to-Sway translation daemon from GSoC 2025.

Project status

The project is not complete. 5 of the 12 proposal success criteria are fully met, the rest are partial or open, and every result below is QEMU-only: nothing has been validated on real hardware yet. Weighing both the proposal's strict Definition of Done and the underlying engineering progress, a fair overall estimate is 58 to 64 percent.

# Success criterion (from the proposal) Status
1 Fresh login lands in the COSMIC-backed session, no helper rerun needed Met (QEMU) — see note below; this held on every install this project had tested until this week, but not on a genuinely fresh one
2 gnome-session-bin absent, cosmic-session running, correct desktop token Met (QEMU)
3 Display config validated: single-monitor, multi-monitor, hotplug, mixed DPI, persistence Partial: single-monitor resolution/scale changes proven applied and persisted (QEMU, two reboots); hotplug and mixed DPI need physical hardware this project doesn't have
4 Keyboard layout/variant changes via cosmic-settings reach Sway Partial: the config-level path works; the cosmic-settings GUI page itself still crashes
5 Shipped lock/unlock path validated end-to-end Met (QEMU, fallback swayidle + gtklock path; native cosmic-idle not adopted, since it hasn't been shown to be cleaner)
6 OSDs render correctly Partial: volume OSD works; media-key injection isn't something QEMU can simulate
7 Display/input settings persist across reboot Partial: resolution and scale persist correctly across two independent reboots (single output, QEMU); refresh rate does not — a requested 60Hz is silently applied and persisted as 50Hz; input-settings persistence not re-tested against this same rebuild
8 Retained surface (workspaces, i3status-rs, ilia) still works Partial: works in the tested QEMU session; not exercised against the full matrix
9 GNOME session/bootstrap packages removed, survivors justified Met (QEMU, headless): a single verified boot shows no gdm3, gnome-shell, gnome-session-bin or ubuntu-session installed, zero failed user units, and a clean dpkg --audit. The four remaining transitive GNOME packages are hard dependencies of COSMIC or Regolith itself, each justified in writing
10 COSMIC packaging built and validated through Voulage, publication coordinated Partial: packages build, vendor, and install locally; official archive publication needs Regolith maintainers
11 Vendored offline builds for all Rust-heavy components Met
12 Keyboard-first workflow preserved via Sway bindsym Partial: proven for the bindings exercised so far, not the full matrix

Late this week I also got a real QEMU display sink working (-vnc instead of -display none, which had silently capped every earlier display test at "no output can ever be enabled" without me realizing that was the actual cause). With a real sink, regolith-displayd's new apply wiring genuinely changed the live session's resolution and scale, and that held across two separate cold reboots with a clean negative control. It's the strongest evidence this project has had for display persistence — and it's also how I found the refresh-rate bug in row 7 above: I was checking a specific requested rate against what came back after reboot, and it didn't match. I'd rather report that honestly than let the earlier, narrower test (which never asked for a specific refresh rate) stand in as proof of something it didn't actually check.

One more thing worth being honest about: while re-testing this week, I ran the install path on a truly fresh, purged disk for the first time in this project. Every earlier QEMU proof had reused a disk carrying a leftover install from a prior run, which hid a real bug — the COSMIC session aborted outright, in under a second, on a genuinely first-time install. The cause was one missing case in a shell function checking whether a legacy Regolith unit was masked; it didn't handle a unit that was never installed at all, which is exactly the state on a COSMIC-only system. Fixed with a test that fails against the old code and passes after, merged upstream to my own fork as regolith-session#3. Criterion 1 above is still correctly Met — the fix makes that true on a fresh install too now — but it wasn't actually proven fresh until this fix landed, and I'd rather say that than leave it implied.

One patch is merged upstream: sandptel/cosmolith#15, reviewed and merged by the cosmolith author. Three more sit open on the same repository with no review yet, all opened on 2026-08-10: #17 covers startup XKB event conversions with tests, #18 makes session detection deterministic, and #19 adds structured watcher errors. Two more are open against my own forks of the Regolith daemons, regolith-session#1 and regolith-displayd#1, because upstream regolith-linux isn't the coordination point for this work yet. Everything else below is documented and reproducible but hasn't been sent upstream.

Pre-GSoC and qualification work

Before applying, I built and debugged the COSMIC-backed session myself in a Pop!_OS 24.04 QEMU VM. That is where regolith-init-inputd crashed reading org.gnome.desktop.input-sources, a key that doesn't exist on a COSMIC host, which became the qualification task and, later, the whole point of this proposal.

Working through that VM turned up four real bugs, which I fixed: a cosmic-osd argv off-by-one that dropped the volume overlay, a lost WAYLAND_DISPLAY in a subshell that broke the OSD the same way, a panel sound-click shortcut that only worked under GNOME, and a partial fix for the input-source crash itself (the config-to-Sway handoff works; cold-login ordering did not, yet).

On the upstream side, I opened sandptel/cosmolith#13 with a keyboard-bootstrap fix that had too much mixed into it. After review from the cosmolith author, I closed it and reopened the same fix, narrowed to two files and two tests, as sandptel/cosmolith#15. That one got merged.

Coding-period deliverables

Session bootstrap and packaging (Phase 1)

Community bonding started by re-centering the project's scope around the real upstream regolith-session/regolith-wm-config PRs and getting a working dev loop against a QEMU guest. From there:

  • Fixed a duplicate Mod4+Escape keybinding and duplicate COSMIC-helper startup ownership, and gave regolith-inputd a safe fallback (us layout) instead of crashing on an empty input source.
  • Audited seven core COSMIC components (cosmic-session, cosmic-idle, cosmic-osd, cosmic-settings-daemon, cosmic-bg, cosmic-settings, xdg-desktop-portal-cosmic) for license and packaging fit, and built the first local package, cosmic-idle, through Regolith's Voulage pipeline.
  • Closed the vendored, offline-build gate for the two Rust daemons this project touches most: regolith-inputd (14,253 vendored files) and regolith-displayd (9,565 vendored files), both building and testing fully offline.
  • Ran a packaging sprint that built and locally published these COSMIC and Regolith components through Voulage: cosmic-app-library, cosmic-comp, cosmic-randr, cosmic-bg, cosmic-settings-daemon, cosmic-idle, cosmic-settings, cosmic-greeter and its daemon, cosmic-panel, cosmic-applets, cosmic-launcher, cosmic-notifications, cosmic-screenshot, cosmic-osd, cosmic-files, and i3status-rs. One finding worth flagging on its own: cosmic-workspaces has no real COSMIC binary upstream at all, the only available artifact under that name is a GNOME Shell extension, so I documented that instead of packaging something that would have quietly been the wrong thing.

Input and display validation (Phase 2)

  • Gave regolith-inputd a real COSMIC/GNOME backend split, selected at runtime from XDG_CURRENT_DESKTOP rather than hardcoded at build time.
  • Proved that native cosmic-randr mode changes reach Sway, then built regolith-displayd out as its own wlr-output-management observer rather than bolting display logic onto the input daemon.
  • Found and fixed several observer-level races along the way, including a Kanshi ownership conflict where displayd and the legacy Regolith display unit were both trying to own the same output, and a stale saved display profile silently overriding a fresh mode change after reboot.

Session hardening and closure (Phases 3 and 4)

  • Split GNOME and COSMIC target ownership at the package level and verified clean installs (dpkg --audit empty) on a fresh Ubuntu 26.04 container.
  • Verified a managed logout through loginctl terminate-session with clean teardown and no dangling user units.
  • Proved keyboard layout and variant (French/AZERTY) and a 1024x768 display mode both survive a second cold login in QEMU. Repeat delay and rate took two attempts. The first combined run dropped back to 600/25 instead of holding 540/31, which turned out to be two components both claiming ownership of the input config. After the single-owner fix, a fresh run held 540/31 across a second cold reboot and login, and that run is the current evidence.
  • Traced an unexpectedly large GNOME package cascade (roughly 290 packages including gdm3, gnome-shell, and mutter) back to network-manager-applet's Recommends metadata, and fixed it with --no-install-recommends in the installer, verified with a fresh QEMU install pulling zero GNOME shell packages.
  • Sent the mentor the first real-system installer bundle, a corrected seven-package tuple after fixing a regolith-session-common version regression. It passed a full QEMU install, reboot, and greetd login on Pop!_OS 24.04.
  • Reached the first fully healthy graphical COSMIC login this project has ever had, on Ubuntu 26.04 Resolute. Two separate fixes were needed: swapping legacy VGA for virtio-gpu-pci to get render nodes back, then the XDG_CURRENT_DESKTOP import fix. On that boot regolith-init-inputd.service was active, no units had failed, regolith-cosmic.target was active, and live sway, swaybg, and swayidle processes answered a swaymsg IPC query. Three consecutive fresh-overlay boots came back clean. I've left the criteria table where it was anyway, since three boots under identical host conditions isn't proof against a bug whose original failure rate nobody measured.
  • Closed the criterion with a single combined verification boot exercising all three fixes at once: the virtio-gpu-pci change, the XDG_CURRENT_DESKTOP import, and --no-install-recommends. Nine health checks passed on that one boot, including an empty GNOME-package grep, zero failed user units, a live sway answering IPC, and no trace of the earlier EGL crash across roughly two thousand journal lines. Headless, so no desktop was rendered.
  • In the final push: designed and implemented the calloop-bridged event loop that lets regolith-displayd actually call .apply() on a COSMIC display change instead of just building the request and dropping it (91 tests passing, re-verified in a clean container), and fixed a startup race where XDG_CURRENT_DESKTOP wasn't visible to the COSMIC user manager in time. Both landed as the two open pull requests linked above.

Timeline of the work

May, community bonding. Re-scoped the project around the real upstream regolith-session/regolith-wm-config pull requests and got a working QEMU-based dev loop going.

June. Fixed the first round of session and input bugs found in that VM, built and published the first Voulage package (cosmic-idle), and finished a licensing and packaging audit of the seven core COSMIC components this project depends on.

July. Proved that native COSMIC display changes (cosmic-randr) reach Sway, built regolith-displayd as its own display observer, and closed the offline vendored-build gate for both Rust daemons. Late in the month, a sustained packaging sprint got sixteen COSMIC and Regolith components building and installing locally through Voulage, and turned up the cosmic-workspaces naming problem described above.

August. Gave regolith-inputd a proper COSMIC/GNOME backend split, split GNOME and COSMIC package ownership cleanly, and fixed several observer-level races in the display path. Mid-month, keyboard layout and display persistence across a cold reboot were proven in QEMU, and a managed logout was verified clean. In the last two weeks, I traced and fixed a large unintended GNOME package cascade, then designed and implemented the two open pull requests: the calloop-bridged display-apply wiring and the session startup-race fix.

Future work

Some of what's left is just work I haven't gotten to yet. Some of it isn't mine to finish, no matter how much more code I write.

Three items are still locally actionable, with no external dependency: one combined verification boot exercising the GPU fix, the input daemon env fix, and --no-install-recommends together, which is all that stands between criterion 9 and closed; a stronger cold-login replay to pin down the exact startup-ordering fix criterion 1 still needs; and, if there's time, scoping and building a native cosmic-idle replacement for the current swayidle/gtklock fallback.

Two items are blocked on someone else acting: official archive publication and signing for the Voulage-built packages needs Regolith or Debian/Ubuntu maintainer infrastructure I don't have access to, and the two open PRs above need their maintainers to actually review and merge them.

Two items are blocked on hardware I don't have: multi-monitor hotplug, mixed-DPI validation, and full display persistence all need real multi-monitor hardware, since QEMU can't simulate hotplug in any way that would count as proof. Multimedia-key injection for the OSD criterion has the same problem: it's a genuine limit of QEMU as a test harness, not something I skipped.

One item is blocked on a decision, not on more coding: booting the finished session on real hardware, instead of in QEMU, carries real risk to a machine that isn't disposable the way a VM is. I haven't attempted that without weighing the risk first.

None of the above closes by writing more code alone. It needs a maintainer, a piece of hardware, or a deliberate decision, and I'd rather say that plainly than gloss over it.

Anyone picking this up from here should start with the reproduction order in WORK_PRODUCT.md, the detailed evidence ledger behind the status table above, then the two open PRs.

Challenges

Late on, the combined verification run caught something I had shipped and not noticed: my own real-system installer could not get past its dependency preflight. It aborted naming a different package every run. The cause was a pipeline, apt-cache policy into grep -q, inside a script running with pipefail. grep -q exits on the first match, apt-cache gets SIGPIPE and exits 141, and pipefail reports that as a failed match. It slipped through because the contract test's mock printed too little output to trigger the race, and the earlier packaging proof called apt-get directly instead of going through the script. So the flag was tested and the script never was. Fixed by capturing the output before matching, with a regression test that reproduces the race deterministically and fails against the old code. The lesson I would keep: a passing test on the component says nothing about the entry point nobody ran.

The single biggest realization was architectural, not a bug: the COSMIC display-apply logic looked wired up but had no path to actually call .apply() on a built Wayland request, and the observer thread had no inbound channel to receive an apply request from outside it in the first place. That's a real design fork (add an event-loop crate like calloop, or hand-roll a poll bridge), not a mechanical fix, and I stopped and asked rather than guessing through it. Worth flagging for whoever continues this: check that a "wire it up" task actually has something to wire into before scoping it as small.

QEMU is honest proof for boot, login, single-output persistence, and config propagation. It is not honest proof for hotplug, mixed DPI, or physical media keys, and I've tried not to blur that line anywhere in the criteria table above.

Keeping pull requests small mattered more than I expected going in. An earlier, larger keyboard patch (cosmolith PR #13) didn't get traction; splitting it down to the minimum needed change (PR #15) is what actually got reviewed and merged.

I also had to correct my own record once: on 2026-08-19 I wrongly claimed a real, already-pushed commit didn't exist, because a verification pass checked local worktrees but never fetched the actual remote branch. The fix was mechanical (git fetch --all before declaring anything missing), but the lesson was about process, not code: verify against the real remote before writing down a negative claim.

Acknowledgements

Thanks to my mentor, Soumya Ranjan Patnaik, for the scope-narrowing feedback that kept this from turning into a rewrite-everything project, and to Sandeep Patel for cosmolith itself and for reviewing the keyboard-bootstrap patch this project depends on.

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论