# Geph GUI renders blank white window — WebKitWebProcess spins at 100% CPU (Linux/Flatpak)

**Component:** Flatpak GUI (`io.geph.GephGui`, `gephgui-wry` / Wry + WebKitGTK)
**Affected versions:** first observed with 5.9.0; still present in 5.9.1. Worked before 5.9.0.
**Severity:** GUI completely unusable. Tunnel itself is unaffected.
**Platform scope:** Linux desktop only so far. Windows and Android are reported unaffected —
note that both use Chromium-based WebViews, while Linux desktop is the only WebKitGTK
platform, so a WebKit-specific fault would show exactly this pattern.

> **Framing note:** this report does *not* claim 5.9.0 alone introduced a code regression.
> The breakage appeared after 5.9.0 *combined with* a subsequent system/runtime refresh, and
> is not seen on other platforms. The most likely shape is an **interaction** between the new
> 5.9.x frontend and a specific Linux/WebKit environment. The hard evidence below is
> presented as-is so the maintainers can judge.

---

## Summary

The GUI opens a window with a normal title bar but a **completely blank white content
area**. The WebKit render process (`WebKitWebProcess`) sits in a **tight user-space busy loop
at exactly 100% of one CPU core**, indefinitely, from process start. There is no crash, no
stderr output, and no error in the journal.

The tunnel daemon is a separate systemd service and works perfectly — only the GUI is broken.

---

## Environment

| Item | Value |
|---|---|
| OS | Ubuntu 26.04.1 LTS, kernel 7.0.0-34-generic |
| Desktop | GNOME Shell 50.1, **Wayland** session |
| CPU / GPU | Intel Meteor Lake-P, Intel Arc Graphics (i915 / xe driver) |
| Geph install | Flatpak, `io.geph.GephGui`, branch `master`, commit `ee6081f9` (2026-09-27) |
| Runtime | `org.gnome.Platform/x86_64/50` (WebKitGTK `libwebkit2gtk-4.1.so.0.24.3`) |
| Sandbox perms | `sockets=x11;wayland;`, `devices=dri;` — GPU device access verified working |
| Tunnel daemon | `geph-manager.service` (systemd) — **works normally** |

### Timeline

| When | Event |
|---|---|
| before 9/20 | GUI worked normally |
| 2026-09-20 01:54 | Geph updated to 5.9.0 (Flatpak deploy) |
| 2026-09-20 09:11–09:14 | **`org.gnome.Platform` 50 + Locale 50 updated by Flatpak** (~7 h later) |
| 2026-09-24 … 09-27 | kernel updates; `xserver-common` updated 09-26 |
| 2026-09-27 | Geph updated to 5.9.1 in an attempt to fix this — **did not help** |
| 2026-09-28 12:11 | Geph + runtime updated again — **still broken** |

The reporter's recollection is that the blank window did **not** appear immediately on
installing 5.9.0, but after that install *plus* a subsequent system/runtime refresh.

---

## Symptoms

1. Window opens; title bar and window decorations render correctly.
2. Content area is pure white, permanently.
3. `WebKitWebProcess` consumes exactly 100% CPU continuously (measured: 503 jiffies per
   5 s wall clock = a full core).
4. **No** error on stdout/stderr, no crash, no journal entry.
5. Repeated GTK warning on each window reopen:
   `gtk_widget_get_scale_factor: assertion 'GTK_IS_WIDGET (widget)' failed`
6. Killing and restarting the GUI reproduces it every time.

---

## Evidence

### 1. It is a user-space busy loop, not a block or I/O wait

```
voluntary context switches over 3 s:  delta = 0
```

Zero voluntary context switches while burning a full core means the process never enters the
kernel — no syscalls, no sleeping. This is a pure CPU spin (the signature of a tight
JavaScript loop under JSC), not a deadlock, not waiting on I/O, IPC or a GPU fence.

### 2. It is NOT related to the rendering / GPU path

Each setting was applied via `flatpak override --user --env=...`, and the variable was
**verified present** in the render process's `/proc/<pid>/environ`. **None changed the
symptom at all** (still exactly 503 jiffies / 5 s):

| Setting | Purpose | Result |
|---|---|---|
| `WEBKIT_DISABLE_DMABUF_RENDERER=1` | bypass DMABUF zero-copy path | no effect |
| `WEBKIT_DISABLE_COMPOSITING_MODE=1` | disable accelerated compositing | no effect |
| `GDK_BACKEND=x11` | fall back to XWayland | no effect |
| `JSC_useJIT=0` | disable JavaScriptCore JIT | no effect |

### 3. It is NOT a WebKit *version* regression

Reproduced **identically** on two runtimes, confirmed via the process's actual mount info
(`/proc/<pid>/mountinfo`), i.e. the older runtime really was in use:

| Runtime | WebKitGTK | `libwebkit2gtk-4.1.so` | Result |
|---|---|---|---|
| `org.gnome.Platform//50` | 2.50+ | `0.24.3` | 100% CPU spin |
| `org.gnome.Platform//47` | 2.46 | `0.19.4` | **100% CPU spin (identical)** |

### 4. GPU access inside the sandbox is fine

Verified from inside the sandbox — `/dev/dri/renderD128` and `/dev/dri/card1` both open
successfully. Not a device-permission problem.

### 5. The frontend code itself runs fine in Blink — it only breaks in JavaScriptCore

The GUI frontend is served by `gephgui-wry` over local HTTP (`http://127.0.0.1:5678`).
Loading **the exact same URL** in Chromium works perfectly: the page title updates to 迷雾通,
the Svelte app mounts, and component styles are injected.

### 6. Suspicious: the served HTML ships only a "legacy" entry

```html
<title>Vite + Svelte + TS</title>
<script id="vite-legacy-polyfill" src="./assets/polyfills-legacy-DtlIMfjT.js"></script>
<script id="vite-legacy-entry"
        data-src="./assets/index-legacy-B_jLTHuj.js">System.import(...)</script>
```

* Output of `@vitejs/plugin-legacy`.
* **There is no `<script type="module">` modern entry at all** — every engine, including
  fully modern ones, is forced down the legacy (SystemJS + core-js) path.
* No `nomodule` attribute either, so the polyfill loads unconditionally.
* `polyfills-legacy-*.js` (171 KB) contains core-js; `index-legacy-*.js` is 978 KB.
* Probing for a modern sibling (`index-B_jLTHuj.js`) returns 404 — only the legacy build is
  shipped.

**Hypothesis (not confirmed):** the core-js legacy polyfills, or the SystemJS loader, enter
an infinite loop under JavaScriptCore. Consistent with every observation above — a pure
user-space spin, independent of graphics stack and of WebKit version.

---

## What could NOT be ruled out

Being explicit about the limits of this investigation:

* **Every comparison test above ran on the *current, already-updated* system.** They rule out
  "runtime / WebKit version" as the variable, but they **cannot** rule out a contributing
  system-level change (kernel, X server, drivers), because no pre-update system state was
  available to test against.
* The one result that argues *against* a system-level cause: the same page renders fine in
  Chromium on the very same kernel, X server and drivers.
* The core-js hypothesis could **not** be confirmed by direct intervention, because the
  Flatpak app files are read-only and the bundle could not be modified or swapped for a
  modern build.
* Downgrading Geph was considered and **deliberately rejected**: 5.9.0 changed the account
  mechanism in response to a security incident, so running a pre-5.9.0 build would mean
  reverting to the old account mechanism.

---

## Impact

GUI 100% unusable. Users cannot connect, switch exits, or change settings through the UI.

---

## Workaround

The bundled `geph5` binary is a full CLI client and **does not require root**:

```bash
geph5 status
geph5 connect
geph5 disconnect
geph5 exits
geph5 exit-constraint set "<name>"
geph5 logs
```

Because the tunnel is `geph-manager.service` and the GUI is only a frontend, this fully
replaces the GUI for day-to-day use. (May be worth documenting for Linux users.)

---

## Suggested next steps

1. Ship a modern (ES module) build alongside the legacy one, with the standard
   `type="module"` + `nomodule` feature-detection pair, so modern engines take the modern
   path. This is the most likely fix if the polyfill hypothesis holds — and is cheap to try.
2. If the legacy build must stay, investigate the core-js / SystemJS interaction with
   JavaScriptCore.
3. Happy to run further diagnostics, or to test a patched/dev build on this machine — the
   environment is reproducible on demand.
