Handler/migration counts, calculateBackoff -> calculateDelay, machine-id binding (cross-platform machineid + fallbacks, not hostname), last-reviewed dates, and made [[cross-references]] relative so they resolve.
4.1 KiB
Desktop Component
A credential-less tray window onto the local agent — it can see, it can ask, it cannot mutate.
Doctrine
The desktop app (desktop/, Tauri 2) holds no tokens, reads no protected config,
and never talks to the fleet server. Its entire world is the agent's local API
socket, and its authorization is its operating-system identity: the user running it
is either in the redflag-local group or the kernel refuses the connection. See
security/01-trust-boundaries, Local Trust
Boundary.
The credential-less surface is a design decision, not an inevitability — the
reasoning lives in security/06-standalone-authority:
group membership only gates the request; the judgment stays in the gates and the
root-owned mint key, and same-host token schemes don't reproduce an off-host
boundary. The decision is held open in one respect: the socket does not capture
peer identity (SO_PEERCRED), so on a multi-user host an approval journals as an
event, not as a person. Whether that needs per-user attribution (peer creds, or a
polkit prompt) is an open call, not settled doctrine.
Fleet-server context, when it comes, joins this same app rather than spawning a second local-only application.
Structure
desktop/
├── src/main.rs # Tauri shell: tray, window, localapi client, health reporter
├── tauri.conf.json # Window config, dev/build frontend wiring
└── icons/icon.png # 8-bit RGBA — tray icon constructor rejects 16-bit PNGs
web/
├── index.desktop.html # Desktop entry (separate from the fleet SPA's index.html)
├── src/desktop/ # LocalAgentApp — status dashboard, no auth surface
└── vite.desktop.config.ts# Builds to dist-desktop/, emits the entry as index.html
One Rust binary, one React entry. The frontend is embedded at build time
(frontendDist: ../web/dist-desktop); release builds are self-contained — no dev
server, no localhost dependency. Dev builds (cargo build without tauri build)
compile in the Vite devUrl and require npm run dev:desktop running.
The fleet SPA and the desktop entry share the web/ tree but are distinct
applications: the desktop entry never renders the sign-in screen because it has
nothing to sign in to.
Agent IPC
The Rust shell speaks plain HTTP/1.1 over the local socket
(/var/lib/redflag/agent/localapi/redflag-agent.sock; named pipe
\\.\pipe\RedFlagAgentLocal on Windows). The frontend reaches it only through
Tauri invoke commands — the webview itself has no socket access.
local_status→GET /v1/identity+GET /v1/status— the dashboard snapshot, polled every 5s.- Health reporter thread →
POST /v1/desktopevery 30s with version, uptime, and window state. This is how the agent (and through it, the fleet) knows a tray is alive on the host.
On EACCES the shell diagnoses rather than reports: it distinguishes "user not in
redflag-local" (gives the usermod command) from "membership added but the login
session predates it" (tells the user to re-log) by comparing /etc/group against
the process's live groups. Group membership is stamped at login; this is the
component's most common support question, so the error message carries the answer.
Lifecycle
Installed and provisioned by the agent install script (binary, autostart entry,
group enrollment — linux.sh.tmpl step 7c). Updated through the capability gate
like every other binary: desktop-self tokens, hash-verified and atomically
swapped by the helper (components/04-helper). Server-side minting
and pre-tray host healing are in progress — docs/tasks/UPDATE-002. The component
is not yet in release version lockstep; closing that is docs/tasks/INSTALL-001.
Cross-References
- security/01-trust-boundaries — the Local trust boundary this client lives behind
- components/02-agent — the localapi server side
- components/04-helper — how the desktop binary itself updates
Last reviewed: 2026-06-14