This document combines the system architecture diagram and the documentation index. The diagram is a Mermaid block — text-based, version-controlled, and renderable by any Mermaid viewer (GitHub, mermaid-cli, browser extension).
For a navigation map and onboarding guide, see ONBOARDING.md.
graph TB
subgraph "Phone / Tablet (Client)"
BROWSER["Browser<br/>React SPA"]
end
subgraph "Deckd Daemon (Python)"
HTTP["HTTP Server<br/>aiohttp"]
WS["WebSocket Server<br/>aiohttp"]
DECKS["Deck Store<br/>decks.py"]
WATCHER["File Watcher<br/>watchfiles (YAML hot-reload)"]
EVENTS["Event Stream<br/>events.py"]
SENSORS["Sensor Polling<br/>platform.py (psutil)"]
end
subgraph "Action Dispatch"
ACTIONS["actions.py"]
end
subgraph "Input Injection"
UINPUT["/dev/uinput<br/>evdev (REL/KEY)"]
end
subgraph "D-Bus Services"
DBUS_SESSION["Session Bus"]
MPRIS["MPRIS2 Players<br/>Firefox, VLC, Spotify"]
KDE_FOCUS["org.deckd.Focus<br/>(KWin script)"]
GNOME_FOCUS["org.deckd.Focus<br/>(GNOME extension)"]
end
subgraph "OS / Shell"
SHELL["shell: (subprocess)"]
end
subgraph "Config"
YAML["YAML Decks<br/>decks/*.yaml<br/>decks.macos/*.yaml"]
end
BROWSER <-->|"WebSocket<br/>(JSON)<br/>protocol.py"| WS
BROWSER -->|"GET /health /diag /decks /metrics"| HTTP
BROWSER -->|"GET /media/art /mpris/art"| HTTP
WS <-->|"DeckMessage<br/>WidgetUpdateMessage<br/>MediaStateMessage<br/>ChromeMediaMessage<br/>EventMessage"| BROWSER
WS <-->|"Hello, press, jog, pad, type, key,<br/>media_command, select_view, clear_view"| BROWSER
WS -->|"focus-driven deck selection"| DECKS
WS -->|"sensor subscriptions"| SENSORS
WS -->|"diagnostic event push"| EVENTS
YAML -->|"read on startup + hot-reload"| DECKS
WATCHER -->|"watchfiles → reload"| DECKS
ACTIONS -->|"inject_key (KEY_*)<br/>inject_scroll (REL_WHEEL_HI_RES)<br/>inject_pointer (REL_X/Y)<br/>click (BTN_LEFT/RIGHT)"| UINPUT
ACTIONS -->|"subprocess.run"| SHELL
ACTIONS -->|"dbus-send / dbus-fast call"| DBUS_SESSION
DECKS -->|"resolve_deck(app)"| WS
DBUS_SESSION -->|"Player registration<br/>PlaybackStatus<br/>Metadata"| MPRIS
MPRIS -->|"MPRIS polling<br/>(mpris.py)"| WS
MPRIS -->|"album art proxy"| HTTP
KDE_FOCUS -->|"activeWindow push<br/>via callDBus"| DBUS_SESSION
GNOME_FOCUS -->|"activeWindow push<br/>via exported interface"| DBUS_SESSION
DBUS_SESSION -->|"focus watch<br/>(platform.py)"| WS
SENSORS -->|"cpu_percent<br/>mem_percent<br/>etc."| WS
EVENTS -->|"focus change, deck reload,<br/>action attempt, MPRIS transitions"| WS
-
Deck push: Client connects via WebSocket → daemon resolves focused app → pushes
DeckMessagewith widget grid, app badge, chrome settings. When focus changes (platform backend detects newAppInfo), daemon pushes a newDeckMessage. -
Button press: Client sends
press {id}→ daemon looks up widget by id in active deck → dispatches action (key, shell, dbus, terminal) viaactions.py. -
Scroll strip: Client sends
jog {id, delta}on drag → daemon emitsREL_WHEEL_HI_RESvia/dev/uinput. On release, client sendsjog_end {id, velocity}→ daemon runs momentum decay loop. -
Manual control: Client sends
pad,pad_tap,pad_drag→ daemon translates toREL_X/Y,BTN_LEFT/RIGHT. Client sendstype/key→ daemon injects keystrokes via/dev/uinput. Feedback-loop guard: daemon drops keyboard input while its own client window is focused. -
MPRIS media: Daemon polls session bus for
org.mpris.MediaPlayer2players → pushesChromeMediaMessage(passive playback indicator) andMediaStateMessage(per-player state) to connected sessions. Album art is proxied through HTTP routes (/mpris/{name}/art). -
VLC media: Daemon polls VLC's HTTP API (subpath of
/media/...) → pushesMediaStateMessagewith VLC-specific state. Art is proxied through/media/{id}/art. -
Live meters/stats: Daemon polls
SensorSourceinstances (e.g.cpu_percentvia psutil) → pushesWidgetUpdateMessageto sessions that have meter/stats widgets in their active deck. -
Hot-reload:
watchfilesmonitors the decks directory → on any.yamlwrite, re-loads all decks, resolves current focus → pushes newDeckMessageto every connected session. Invalid YAML pushesDeckMessagewitherrorset (last-good decks stay live). -
Diagnostic events: Client sends
enable_events→ daemon pushesEventMessageframes for focus changes, deck reloads, action outcomes, auth events, and MPRIS transitions. Event stream is per-session and opt-in.
| What | Where | Description |
|---|---|---|
| Domain vocabulary | CONTEXT.md | Ubiquitous language; defines Deck, Widget, Chrome, Action, AppInfo, Match, Bind, etc. |
| Operational reference | REFERENCE.md | Canonical CLI flags, env vars, diagnostic endpoints, project status. |
| ADR index | adr/README.md | All architecture decisions with summaries and amend/supersede relationships. |
| Spike tracker | SPIKES.md | Spike progress and implementation plan (de-risking work). |
| KDE focus investigation | spike-kde-wayland-focus.md | KWin/Wayland focus detection paths investigated. |
| Research | research/ | Research notes (Stream Deck built-in actions catalog, etc.). |
| Agent workflows | agents/ | Issue tracker conventions, triage labels, domain doc consumption. |
| Onboarding | ONBOARDING.md | Repository map, read order, protocol locations, development modes, verification ladder. |
| Wire protocol (Python) | daemon/deckd/protocol.py | Executable contract: all ServerMessage and ClientMessage types. |
| Wire protocol (TypeScript) | client/src/protocol.ts | TypeScript mirror of the wire protocol. |
| Deck schema | daemon/deckd/decks.py | Pydantic models for Deck, Widget, Action, Icon, MediaHttp. |
| HTTP/WS routes | daemon/deckd/server.py | All HTTP endpoints and WebSocket dispatch. |
| Platform backend | daemon/deckd/platform.py | PlatformBackend Protocol: focus watching, input injection, sensor sources. |
| Daemon CLI | daemon/deckd/main.py | Argparse: --decks-dir, --client-dist, --bind, --no-auth, --verbose. |
| deckctl CLI | daemon/deckd/cli.py | status, reload, deck, metrics subcommands. |
| Build/test commands | Justfile | All common commands: setup, dev, test, build, smoke. |
| Testing strategy | TESTING.md | Testing layers, what each fakes, and the planned desktop-integration tier. |
| Platform parity | PLATFORM-PARITY.md | What works on GNOME / KDE / X11 / macOS — capability matrix and per-backend notes. |
| User & setup guide | GUIDE.md | Install, per-platform setup, deck/configuration walkthrough, client features, dev loop. |
| README | README.md | Human-facing showcase: pitch, screenshots, status, comparison. |
Prose documentation describes intent, vocabulary, and decisions. It does not duplicate:
- Endpoint paths and parameters — see
daemon/deckd/server.pyfor routes. - Flag names and defaults — see
daemon/deckd/__main__.pyfor argparse definitions; the running daemon is self-documenting via--help. - Wire message shapes — see
daemon/deckd/protocol.pyfor the executable Pydantic models (ServerMessage,ClientMessage, and all their variants). - Deck field validation — see
daemon/deckd/decks.pyfor the Pydantic models (Deck,Widget,Action). Valid YAML is whatever passesDeck.model_validate(). - Action dispatch behavior — see
daemon/deckd/actions.py. Available primitives arekey,shell,dbus,terminal,url, andtext.
When in doubt, read the code. Tests (tests/) are the next best source — they exercise the public interfaces at the agreed seams.