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:

FacadePackageContract
ProxyBridgebridgeexported methods are exactly Send and Close
Serviceshostthe HostServices facade, including OpenPath and RevealPath
lifecycleServicelifecycleclose policy, quit, window restore
settingsServicedesktopsettingsdesktop-local settings
packSourceServicepacksourcegit-source review and exact-review install
configEditorServiceconfigeditornative review, no config filesystem reads or writes
windowControlsServicewindowcontrolsfixed 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. Its bootstrap member is the shared sonny.daemon.bootstrap/v1 evidence — 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-input state, then exits 1.
  • An operational failure also exits 1 and writes status: "error" plus a diagnostic string. Typed failures carry the same bootstrap diagnostic that TUI and daemon callers can extract, including invalid and repairable state 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.md
  • docs/config/supervisor-daemon-lifecycle.md
  • docs/assurance/decisions/D21-2026-09-14-wails-baseline.md
  • docs/user/install.md (packaging behaviour)