Skip to content

Feat/unix socket ipc - #34

Merged
xeri merged 16 commits into
masterfrom
feat/unix-socket-ipc
Nov 10, 2026
Merged

xeri merged 16 commits into
masterfrom
feat/unix-socket-ipc

Conversation

@xeri

@xeri xeri commented Nov 10, 2026

Copy link
Copy Markdown
Owner

No description provided.

xeri added 16 commits November 8, 2026 16:19
Off Windows, Serve returned ErrUnsupported; engine.Run treated that as a
clean first exit and cancelled everything, so the window opened and the
engine silently stopped.

The request loop and client move verbatim into untagged server.go and
client.go; only the endpoint is per-platform. Windows keeps the winio pipe
under pipeSecurity. Elsewhere the endpoint is $XDG_RUNTIME_DIR/proxyforward/
ipc.sock in a checked 0700 directory, the socket is 0600, and on linux each
peer's uid must be ours or root (SO_PEERCRED) — the same principals the pipe
ACL admits, since the pipe carries gateway admin ops. A socket path does not
refuse a second listener the way a pipe name does, so listen holds an flock
for the listener's lifetime: a live engine makes a second one fatal, and a
crashed one's stale socket is safely replaced.

Tests take their endpoint from PrivatePipeName, so the existing pipe tests
run unchanged on both platforms; engine_test now takes a private one too.
New unix tests cover socket/dir modes, stale-socket takeover, and refusing
a loosened directory. Docs and the Reality-check row say what still is
Windows-only.
The unix-socket IPC and every other !windows path only execute off Windows, and every Go test job ran on windows-latest, so nothing in CI exercised them. test-linux runs vet and go test -short on ubuntu-latest.
With no Linux options, Wails sets WebKitGTK's hardware acceleration policy
to never, so every frame was painted on the CPU: the glass blurs pinned a
core and the window ran at a few fps. Pass WebviewGpuPolicyAlways, and
ProgramName so the Wayland app id matches proxyforward.desktop.

Once composited on the GPU, NVIDIA's EGL Wayland layer enables explicit
sync on the window surface, GTK3 commits without the acquire point, and the
compositor ends the session with Wayland error 71 ("explicit sync is used,
but no acquire point is set"). linuxGraphicsDefaults sets
__NV_DISABLE_EXPLICIT_SYNC=1 before GTK starts unless the user set it.
The pre-paint colour exists for WebView2's resize flash. On Linux, Wails
routes WindowSetBackgroundColour into WebKitGTK and GTK CSS on the GTK
thread, and switching theme segfaulted the app there. theme.ts now asks
the runtime for the platform once and only makes the call on Windows,
syncing as soon as the platform is known.

The colour's alpha was 1: Wails' Windows backend rounds any non-zero alpha
up to 255, but WebKitGTK reads it as a real alpha, which made the Linux
webview background almost transparent. Use 255 in both places; Windows
behaviour is unchanged.
WebKit tiles large GPU layers at 512 px. The ambient aurora is a
viewport-sized layer drifting by sub-pixel translate and scale; with the
2x buffers GTK3 uses under fractional scaling, every tile edge resampled
differently each frame, showing a flickering seam grid on the dark theme.
linuxGraphicsDefaults now also sets WEBKIT_LAYERS_TILE_SIZE=4096x4096
unless the user set it, so one tile covers the layer at normal sizes.
The gateway wizard hands out a tkt_ enrollment ticket by default, and
SetupAgent routes it to agent.enroll_ticket with agent.token empty. Validate
still required agent.token, so every ticket pairing in the GUI failed with
"refusing to save invalid config: agent.token: required". Only the legacy
shared-token code could pair.

An agent is paired when it holds either credential. The ticket stays in the
config after enrollment (the gateway authenticates an allowlisted key first
and ignores a spent ticket), so the field comment claiming the agent clears
it is corrected. TestSetupAgentWithEnrollmentTicket drives the wizard path.
The window opened at a fixed 1360x820 with a 1280x760 minimum, in logical
pixels. A 1080p display at 150% is 1280x720 logical (~672 above the
taskbar), so the window could not fit: it overflowed every edge, centred,
with the title bar off-screen, and the minimum stopped the OS from ever
shrinking it. A frameless window's title bar is its only drag handle and
caption buttons, so there was no way to move, restore, or leave it.

The minimum drops to 1024x640 (the layout already has a narrow step below
1150 px and every screen scrolls). At startup, before the page loads, the
window is shrunk and centred to the monitor's size less room for the
taskbar, or maximised when even the floor would not fit. fitWindowSize is
pure and pinned against common screens in TestFitWindowSize.
On NVIDIA's driver, WebKit's web process segfaulted inside
libnvidia-eglcore from its Skia GPU painting threads, 3-14 minutes into a
session (four core dumps), and a dead web process takes the whole app down
with it. When the NVIDIA driver is loaded, linuxGraphicsDefaults now sets
WEBKIT_SKIA_ENABLE_CPU_RENDERING=1 unless the user set it: page content is
rasterised on the CPU while compositing (transform and opacity animation,
backdrop blur) stays on the GPU. Mesa drivers keep GPU painting.
Every Copy button on Linux silently cleared the clipboard. Wails'
ClipboardSetText writes PRIMARY right after CLIPBOARD, and under GTK3 on
Wayland that second write empties CLIPBOARD (reproduced with a bare GTK3
probe: CLIPBOARD alone sticks, CLIPBOARD then PRIMARY leaves it empty), yet
it reports success, so copyText's fallback never ran.

On Linux copyText now copies through WebKit itself (execCommand on a
throwaway selection, synchronously inside the click), which writes
CLIPBOARD only; navigator.clipboard is the fallback. Windows keeps the
Wails runtime path. The host platform moves into platform.ts so the theme's
Windows-only background sync and the clipboard read one source.
RunStarted always printed the legacy shared-token pairing code on the
console. With accept_shared_token off the validator refuses that code, so a
headless operator was shown a secret that cannot pair anything. When shared
tokens are off it now says agents join with an enrollment ticket instead.
The Linux GUI needed per-driver WebKitGTK workarounds just to paint (GPU
policy, NVIDIA explicit sync, Skia CPU raster, layer tile size). Linux now
runs the gateway/pair/agent commands only, and a bare launch says which to
run instead of opening a window.

runGUI and the frontend/dist embed move to gui.go (!linux), along with
window.go and its test; gui_linux.go answers a bare launch with errNoGUI.
A linux build therefore pulls in no Wails and no cgo: one static binary
that compiles without the frontend. The Linux webview options and
linuxGraphicsDefaults go. Windows and macOS are unchanged.
Linux no longer builds the GUI, so build-linux is a plain static go build
(CGO_ENABLED=0) instead of a wails build against gtk/webkit, and neither
linux job waits on the frontend job or downloads frontend/dist: gui.go,
which holds the embed, is !linux.
govulncheck reports by reachability for the GOOS it loads. On the ubuntu
runner that is now the headless linux build, which never reaches Wails or
the GUI wiring in gui.go, so the shipped Windows build gets its own
GOOS=windows pass.
The --config help on agent, gateway and pair hardcoded the Windows
%APPDATA% path, which was wrong on Linux, where those commands are the
only way to run. It now prints config.DefaultPath.
Fitting the window to the screen wrapped a.Startup in a closure for
OnStartup. Wails keeps lifecycle callbacks out of the JS bindings by
matching OnStartup's function name against the bound methods, so the
closure made Startup a frontend-callable binding and CI's stale-wailsjs
check failed.

OnStartup is the a.Startup method value again, and the fit runs through
a new App.BeforeStartup field at the top of Startup, still before the
page loads. Regenerated bindings now match the committed ones.
wails build generates frontend/wailsjs by building the app with -tags
bindings for the host and running it. With gui.go excluded on linux, that
binary hit errNoGUI, so wails build from a linux machine could not
generate bindings. gui.go and window.go now build for !linux || bindings,
and gui_linux.go for linux && !bindings. The bindings build opens no
window and needs no cgo.

commands.md notes the exception and how to check the bindings from linux
without the wails CLI.
@xeri
xeri merged commit 967596e into master Nov 10, 2026
11 checks passed
@xeri
xeri deleted the feat/unix-socket-ipc branch November 10, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant