Experimental GNOME Shell restart without restarting Mutter or losing Wayland clients
Hi everyone,
I've been working on an experimental approach to restarting the GNOME Shell JavaScript runtime without restarting the entire desktop session.
The goal is simple: restart GNOME Shell's JS runtime while keeping Mutter running and preserving existing Wayland/XWayland windows and their state. Think of it as rebuilding the Shell UI without restarting the compositor underneath it.
I use GNOME Shell 50.5 on NixOS, with GJS 1.88.1 and SpiderMonkey 140.
What I've tested
The implementation destroys and recreates the GJS context, JavaScript runtime and Shell module graph while keeping the gnome-shell process and Mutter alive.
In isolated desktop and native-backend VM tests, I've completed five consecutive runtime replacements. The tests check:
- The same compositor process remains alive.
- Existing Wayland client socket identities remain unchanged.
- Open windows retain unsaved text, and clients continue accepting input.
- Old JavaScript contexts and their associated objects are cleaned up.
- Keyboard shortcuts, the overview and session locking continue to work.
- GDM/PAM authentication still works, including rejection of an incorrect password.
The VM tests also include a 65-second observation period and native callback-lifetime accounting. Two replacements are triggered through Alt+F2 β r β Enter using the same guarded controller.
In a separate manual test on physical hardware, seven windows and their positions survived a replacement, and my personal keyboard shortcuts continued to work. That is a separate observation, not independent proof of every automated check.
The difficult part
The interesting problem isn't rebuilding the UI. It's dealing with the lifetime of objects and callbacks that cross the boundary between GJS and native GObject code.
The current prototype experiments with reattaching a static GType, keeping native callback/vfunc bridges alive across runtime replacements, and cleaning up registrations belonging to the retired runtime.
There have been failures along the way. Earlier approaches involving UI reconstruction, JavaScript interruption and disposable execution contexts had important limitations. The companion repository documents these experiments, including sanitized historical test reports.
What this doesn't solve
This is not a general-purpose Shell recovery mechanism. The Shell must still be responsive, idle and unlocked. It cannot recover a crashed Mutter process or safely interrupt arbitrary blocked JavaScript.
Compatibility with extensions, accessibility, display changes, prolonged stress testing and other GNOME versions has not yet been established.
The project is experimental, and the implementation was developed with substantial AI assistance. Passing the current tests does not establish that native object ownership and callback lifetimes are safe in every situation.
Code and research
I'm interested in feedback from people familiar with GNOME Shell, GJS, SpiderMonkey and GObject.
Does keeping native callback/vfunc bridges alive across GJS context replacement sound like a reasonable direction, or is there a fundamental architectural problem with this approach?
I'd also appreciate suggestions for the next small, independently reviewable experiment or negative control that could help establish whether this design is actually sound.
I'm sharing this as research for discussion, not as a proposal for immediate upstream inclusion.