00 CONTROL SURFACE // Overview & process architecture SRC connector-app/ · scripts/ · deploy/
00.A

What Connector is

README.md

A console that operates other machines

Connector is a native Qt 6 application that treats every computer on a Tailscale tailnet as an addressable system with a readiness state, a shell target, and a remote display. It pairs and streams with Moonlight in-process, drives Sunshine on the far end, and exposes the whole thing as a semantic RPC surface that a CLI, a daemon, or an LLM agent can operate.

  • C++20 / Qt 6.4+
  • QML cockpit
  • Unix-socket RPC
  • MCP server
  • macOS client · Linux X11 host
  • v0.1.0

Most remote-desktop tools stop at "here is a window into that machine." Connector's premise is that the window is the least interesting part. The interesting part is the state model underneath it — is this host online in the tailnet, is its host runtime answering, does this client hold a trust record, is there a working shell path — and the fact that those four answers are independent of each other.

That model is why the app ships three separate entry points instead of one. Connector.app is the visual cockpit. connectord is a headless control plane that keeps working when nobody is logged into a desktop. connector-mcp.py is the bridge that lets an agent drive either one. They all speak the same JSON-line protocol over the same style of local Unix socket.

Provenance notice

This repository vendors two large upstream projects under vendor/Moonlight (moonlight-qt) and Sunshine — both GPLv3, both other people's work. Nothing on this site claims them. See 00.F — Provenance & licensing for the exact boundary between what was authored here and what was imported.

00.B

Readiness model

CommandDeck.qml

The device rail collapses four independent signals into one visual state. Colours below are the literal hex values returned by visualAccent().

ready #42e59a — online + hostReachable + clientTrusted + advancedReady
ssh #6cd9ff — sshReady but the host runtime is not answering
setup #ffd58c — reachable or still probing, but not trusted yet
blocked #ff7c7c — offline, or no usable path at all
idle #8c9b94 — no device selected

Full state lattice →

00.C

Repository instrumentation

counted from the working tree, vendor/ excluded
17,301 lines C++ / ObjC++ connector-app/src + shared/src
6,514 lines QML 23 files in connector-app/qml
5,962 lines Python resources/*.py + scripts/*.py
43 app RPC methods connectorcontrolserver.cpp
34 daemon RPC methods connectordaemonserver.cpp
33 / 11 / 2 MCP tools / res / prompts scripts/connector-mcp.py
6 built executables connector-app/CMakeLists.txt
8 registered ctest suites CMakeLists.txt

These are file-and-symbol counts from the repository, not performance figures. No benchmark, latency, or adoption number appears anywhere on this site, because the repository does not contain any.

00.D

Process architecture

CMakeLists.txt · connectorcontrolprotocol.cpp

Six executables come out of the build. They are deliberately not one binary: the daemon must survive with no desktop session, the stream helper must be a GUI process that can crash without taking the cockpit down, and the CLI must be small enough to drop on a box that only has Qt Core and Network.

Connector process architecture Operator and agent clients connect through two Unix domain sockets to either the connectord daemon or the Connector desktop app. Both spawn short-lived Moonlight helper processes, and both reach remote Linux hosts over SSH and the Sunshine stream ports. A · OPERATOR AND AGENT EDGE MCP client Codex · Ian Brain · any MCP host Operator shell interactive terminal Human at the cockpit QML window · menubar · in-session overlay B · CONTROL NODE — macOS or Linux, one user account connector-mcp.py FastMCP · stdio / streamable-http scripts/connector-mcp.py connectorctl Qt Core + Network only connectorctl_main.cpp ConnectorApp — QML front end Main.qml · CommandDeck · CommandThisComputer · CommandSettings 23 QML files, registered as module URI "Connector" daemon.sock $XDG_RUNTIME_DIR/connector/ · mode 0600 control.sock /tmp/connector-control-<sha1(home)[0:12]>/ default CONNECTOR_CONTROL_SOCKET override connectord ConnectorDaemonServer → ConnectorDaemonBackend links Qt::Core, Qt::Network, connector-shared no Moonlight, no GUI, no ui.* methods ConnectorApp ConnectorControlServer → ConnectorBackend links connector-moonlight-core + connector-shared full 43-method surface incl. ui.* and host.* connectorstream QApplication · SDL · stdin JSON connectorpair CliPair::Launcher connectorapps NvHTTP app discovery embedded Python helpers qrc:/connector/*.py — staged onto the remote host C · TAILNET — WireGuard mesh, no port forwarding ssh · tailscale ssh BatchMode=yes, publickey only ConnectTimeout=5 StrictHostKeyChecking=accept-new connectordaemonbackend.cpp · ssh.prepare Sunshine host runtime 47989 http · 47990 web UI (port + 1) 47984 · 48010 also bound by the runtime systemd --user sunshine.service probed before every connect host-side helpers connector-linux-hostctl.py connector-x11-workspace.py connector-host-overlay.py connector-linux-michost.py :48200 RTSP / ENet / RTP video + audio shell control stream
Figure 00.1 — process architecture Everything above the tailnet band lives in one user account on the control node. The two Unix sockets are the only supported entry into the running system; both are created with QLocalServer::UserAccessOption and re-chmoded to owner-only after listen(). Note that connectord links neither Moonlight nor Qt Quick — when it needs a stream or a pairing it execs the helper binaries in row B.
ConnectorApp
The cockpit. Owns the QML tree, the device model, the session manager, the local host controls, and the automation socket. The only process that can answer ui.*.
connectord
Headless. Tailscale discovery, SSH profiles, SSH execution, settings, remote host-runtime startup. Explicitly refuses ui.* and host.set_* with a message telling the caller to use the desktop app.
connectorstream
GUI stream helper. Reads one JSON command per line on stdin (disconnect, release_mouse, toggle_fullscreen, toggle_stats, toggle_mouse, switch_display) and prints JSON events on stdout.
connectorpair
One-shot pairing. --host, --pin, --timeout-ms. Emits searching / pairing / success / failed events as JSON lines.
connectorapps
One-shot app enumeration against a host's Sunshine HTTP port, including a raw TLS handshake to capture the server certificate when the HTTP path did not supply one.
connectorctl
The universal CLI. Twenty-odd verbs that map one-to-one onto RPC methods, plus a raw rpc escape hatch.
00.E

First contact

README.md · scripts/connectorctl-daemon.sh

The daemon wrapper resolves the socket path before it runs anything, so the same CLI binary can be pointed at the daemon or at the desktop app without recompiling.

control node — daemon socket
$ scripts/ensure-connectord.py --print-socket
/run/user/1000/connector/daemon.sock

$ scripts/connectorctl-daemon.sh rpc app.ping
{
    "capabilities": [
        "devices.start_host",
        "ssh.exec",
        "connectorstream.launch"
    ],
    "control_protocol_version": 1,
    "mode": "daemon",
    "name": "Connector",
    "socket": "/run/user/1000/connector/daemon.sock",
    "version": "0.1.0"
}

$ scripts/connectorctl-daemon.sh ui tree
ui.tree requires the Connector desktop app or streaming session process.
The headless daemon only exposes device discovery, settings, and SSH control right now.

That last error is not a stub. ConnectorDaemonServer::desktopRequired() is a deliberate, explicit refusal for every ui.* method plus host.set_enabled, host.set_credentials, host.approve_pair, host.revoke_client and host.set_default_monitor — the operations that need a live QML tree or a local desktop session. The daemon would rather tell you where to go than half-answer.

"This is a high-trust local-owner automation surface. It is intended for the same user account that launched Connector."

README.md — Agent Control
00.F

Provenance & licensing

vendor/moonlight-qt · vendor/sunshine

Connector is not a from-scratch streaming stack and does not pretend to be. It vendors two mature GPLv3 projects and builds a control layer on top of them. The boundary is legible in the build system: four static libraries are compiled out of vendor/moonlight-qt and linked into the authored executables.

Authored versus vendored layers A four-layer stack. The top layer is authored Connector code. Below it a CMake static-library layer built from vendored Moonlight sources. Below that the vendored Moonlight and Sunshine trees, both GPLv3. At the base the external toolchain: Qt 6, SDL2, OpenSSL, PipeWire and X11. LAYER 1 — AUTHORED IN THIS REPOSITORY Ian Greenberg connector-app/qml 23 files · 6,514 lines the cockpit connector-app/src backend · session mgr control + daemon servers shared/src control protocol client · command paths resources/*.py hostctl · x11-workspace host-overlay · michost scripts/ · deploy/ MCP server · wrappers systemd units LAYER 2 — CMAKE STATIC LIBRARIES BUILT FROM VENDORED SOURCE connector-app/CMakeLists.txt connector-moonlight-core connector-moonlight-common-c connector-h264bitstream connector-qmdnsengine LAYER 3 — VENDORED UPSTREAM · NOT AUTHORED HERE GPLv3 vendor/moonlight-qt Moonlight Game Streaming Project client pairing, app discovery, session, decoders vendor/sunshine LizardByte Sunshine host capture, encode, pairing service, web UI LAYER 4 — EXTERNAL TOOLCHAIN AND SYSTEM LIBRARIES Qt 6.4+ (Core Gui Qml Quick QuickControls2 Network Svg OpenGL Concurrent Widgets) · SDL2 · OpenSSL · libsecret / Keychain · PipeWire · X11 / xrandr · Tailscale CLI discovered with find_package and pkg-config; not vendored links compiled from depends on
Figure 00.2 — the authorship boundary Layer 3 is other people's software. Layer 2 is a set of CMake targets that compile that software's sources under Connector-prefixed target names — the code is upstream's, the build wiring is not. Layers 1 and 2 are what this site documents. Because the vendored trees are GPLv3, anything distributed from this repository inherits GPLv3 obligations.
Component provenance, with the path that settles it
ComponentPathOriginLicense posture
QML cockpitconnector-app/qml/Authored hereLinks GPLv3 statics
Backend, session manager, control serverconnector-app/src/Authored hereLinks GPLv3 statics
Control protocol + clientshared/src/Authored hereQt only
Linux host helpersconnector-app/resources/*.pyAuthored hereStandalone Python
MCP server & wrappersscripts/Authored hereStandalone Python / shell
systemd unitsdeploy/Authored hereConfig
Moonlight client stackvendor/moonlight-qt/Vendored upstreamGPLv3
Sunshine host stackvendor/sunshine/Vendored upstreamGPLv3

The Sunshine tree carries its own CONNECTOR_VENDOR_SOURCE.md recording the exact imported commit (dfb5163c3cbaed1c8052c6d0f9d6cca4c320f405) and noting that because it was vendored as plain files rather than a submodule, scripts/embed-sunshine-runtime.sh has to hydrate Sunshine's own third-party dependencies into the build directory before configuring it.

Identity import

One nice consequence of embedding Moonlight rather than shelling out to it: moonlightruntime.cpp reads the standalone Moonlight client's QSettings store — organization "Moonlight Game Streaming Project", application "Moonlight" — and copies the client certificate, private key, unique id and saved host array into Connector's own settings namespace. If you already trusted a host in Moonlight, Connector inherits that trust instead of re-pairing.

00.G

Where to go next