Desktop App
The Sonny desktop app is a Wails v2 shell around the same React UI the other clients use, bound to exactly seven narrow Go facades. It does not run sessions itself; it connects to the shared user-scoped supervisor. See Client Apps for that model and for what closing a window does and does not stop.
The pinned Wails version, build tags, and platform matrix are a recorded
architecture decision in the source repository
(docs/assurance/decisions/D21-2026-09-14-wails-baseline.md).
Install
sudo apt install sonny-desktop # Debian / Ubuntu
sudo dnf install sonny-desktop # Fedora / RHEL / Rocky / Alma
sonny-desktop depends on the exact same version of sonny, so installing
or upgrading the desktop cannot silently pair it with a different
client-protocol build. The native packages declare their WebKitGTK 4.1, GTK 3,
and GLib runtime requirements, use distribution-native post-install and
post-remove scriptlets to refresh the desktop and icon caches, and install a
package-scoped CycloneDX inventory at
/usr/share/doc/sonny-desktop/sbom.cdx.json.
What is on the machine
apps/desktop/
wails.json Wails project config: frontend commands, wailsjsdir
main.go entry point (//go:build desktop): embeds frontend/dist,
dispatches bootstrap check or calls app.Run
app/ Wails options and lifecycle (//go:build desktop)
bootstrap/ shared desktop config + supervisor bootstrap, including
the supported non-window verification path; no Wails
bridge/ ProxyBridge: owns the sonny editor-rpc child process
host/ Services: the HostServices facade
lifecycle/ token-gated close policy, client quit, window restore
desktopsettings/ token-gated, desktop-local settings and deep-link setup
packsource/ detached git-source review, signature verification and
exact-review installation
configeditor/ token-gated native review and transition-bound proof
windowcontrols/ token-gated, fixed native window state and actions
assetpolicy/ the packaged-asset content policy middleware
windowtoken/ the per-launch window token bridge and host share
frontend/ the Vite React frontend
bridge/ and host/ import no Wails package and carry no build tag, so they
build and test in the ordinary suite. main.go and everything in app/ import
github.com/wailsapp/wails/v2 and carry //go:build desktop, because importing
that module always requires cgo, GTK, and WebKitGTK on Linux and there is no tag
that turns it off. Gating it behind desktop is what keeps the default
go build ./... and CI free of that toolchain.
The seven facades
app/app.go binds exactly seven structs and nothing else:
| Facade | Package | Contract |
|---|---|---|
ProxyBridge | bridge | exported methods are exactly Send and Close |
Services | host | the HostServices facade, including OpenPath and RevealPath |
lifecycleService | lifecycle | close policy, quit, window restore |
settingsService | desktopsettings | desktop-local settings |
packSourceService | packsource | git-source review and exact-review install |
configEditorService | configeditor | native review, no config filesystem reads or writes |
windowControlsService | windowcontrols | fixed native window state and actions |
The narrowness is the point. The surface a webview can reach is a fixed list, not whatever a struct happens to export.
Bootstrap verification without a display
The shipping binary has a supported automation mode that does not initialize Wails or require a display server:
sonny-desktop --bootstrap-check
It follows the same production path as a graphical launch: reads
<config-dir>/desktop.yaml through desktopsettings.Store, resolves machine
organization configuration and the signed personal runtime through
internal/daemonbootstrap, selects the digest-pinned sonny binary, ensures or
adopts the user-scoped supervisor, then performs its authenticated diagnostics
read-back. The whole operation is bounded to two minutes, with the daemon launch
itself bounded to 90 seconds.
- stdout is exactly one JSON document, schema
sonny.desktop.bootstrap-check.v1: client version, effective non-secret desktop settings, the selected endpoint/binary/tenant, daemon and protocol versions, and whether the supervisor was started or adopted. Itsbootstrapmember is the sharedsonny.daemon.bootstrap/v1evidence — readiness, selected and effective profiles, configuration digest, and ordered source tiers. - stderr carries progress.
- Exit status is zero only after config parsing and authenticated readiness
both succeed with shared bootstrap state
ready. - A successful check whose signed configuration still lacks a provisioned secret
emits the full evidence with
needs-user-inputstate, then exits1. - An operational failure also exits
1and writesstatus: "error"plus a diagnostic string. Typed failures carry the samebootstrapdiagnostic that TUI and daemon callers can extract, includinginvalidandrepairablestate and the failing source tier.
The mode deliberately does not start editor-rpc: that child is
window-scoped, whereas this check proves the host-owned config and daemon edge
that must exist before the bridge can start.
Provider-sharing grants
The open session view projects daemon-owned provider-sharing grants. Its list and revoke calls are bound to that session ID, display only secret-free references, status and scope, require explicit confirmation before Stop sharing, and refresh durable state after the result.
Building it yourself
make desktop-frontend # produces frontend/dist, which main.go embeds
go build -tags desktop,production,webkit2_41 ./ui/apps/desktop
production is Wails’ own requirement for a runnable app; webkit2_41 selects
the WebKitGTK 4.1 pkg-config name.
make desktop-build also pins Wails’ Windows WebView2 strategy to download:
before a window is created, Wails checks the installed runtime against its
minimum, offers the native bootstrapper for a missing or outdated runtime, and
reports offline or denied-install failures through its native prerequisite
dialog. Linux packages must declare WebKitGTK 4.1 as a package dependency — the
linked binary cannot repair a loader dependency after process startup. macOS
uses the system WebKit supplied with the supported OS.
Uninstall
apt remove sonny-desktop or dnf remove sonny-desktop preserves user
settings, the local control database, sessions and journals.
Debian/Ubuntu has one additional destructive operation: an explicit
apt purge sonny-desktop deletes each local account’s profile sessions/ and
journal/ directories under
~/.local/share/sonny/profiles/{personal,org}/<profile>/. It prints what it
deleted and keeps the sonny base package, desktop settings, keys, caches, and
control.db. A package script cannot discover a custom XDG_DATA_HOME, so
delete profile sessions and journals there by hand when a full purge is
intended. Purge enumerates accounts from the files NSS database only, not
directory-service account records, and does not infer whether a listed home path
is local or remote. RPM has no separate purge transaction, so removal always
preserves user data.
Source of truth
ui/apps/desktop/README.mddocs/config/supervisor-daemon-lifecycle.mddocs/assurance/decisions/D21-2026-09-14-wails-baseline.mddocs/user/install.md(packaging behaviour)