Running as a Service
Install the node as a user-level service so it survives reboots, and why a system-level service cannot work.
A node started from a terminal dies with the terminal. To keep one available, run it under a service manager, as your own user, not as a system service.
That distinction is the most important thing on this page, so it comes first.
Why the node runs as you
A node drives a desktop. Screen capture, mouse and keyboard input, the clipboard, and window management all need a live, logged-in desktop session, and a session belongs to a user.
A system-level service has no such session. It has no display to capture, no clipboard owner, and no session message bus. Commands do not merely run slower there. They fail, or worse, silently do nothing.
There is a second reason: consent. The riskiest commands raise a prompt on the machine's own screen, and a service with no desktop cannot show one. Because that prompt fails closed, a node without a session cannot run consent-required commands at all.
Do not install the node as a system-wide service, a LocalSystem Windows service, or a root systemd unit. Every configuration below is deliberately user-scoped.
Install the service
Run the node under the logged-in user account, not LocalSystem.
Register the service
sc.exe create SophonNode binPath= "C:\path\to\sophon-node.exe start --service" start= auto obj= ".\YourUser" password= "…"
sc.exe description SophonNode "Sophon Node - desktop automation endpoint"The obj= account must be the user who logs into the desktop. A service running as LocalSystem has no desktop to drive.
Manage it
sc.exe start SophonNode
sc.exe query SophonNode
sc.exe stop SophonNode
sc.exe delete SophonNodeLogs are written under that user's profile, beside the node's config file, so if you change the service account the logs move with it.
Use a user unit at ~/.config/systemd/user/sophon-node.service:
[Unit]
Description=Sophon Node
After=graphical-session.target
[Service]
ExecStart=%h/.dotnet/tools/sophon-node start --service
Restart=on-failure
RestartSec=10
[Install]
WantedBy=default.targetWantedBy=default.target is the user-session target. A system unit under multi-user.target would start without a desktop and be unable to do its job.
Manage it
systemctl --user daemon-reload
systemctl --user enable --now sophon-node
systemctl --user status sophon-node
journalctl --user -u sophon-node -fSurvive logout. By default a user service stops when the last session ends. To keep the node running on a machine you log out of:
loginctl enable-linger "$USER"X11 versus Wayland. The helper tools differ, and element queries are unavailable under Wayland. See Platform Support & Limits.
Use a LaunchAgent at ~/Library/LaunchAgents/ai.sophon.node.plist, not a LaunchDaemon:
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>Label</key>
<string>ai.sophon.node</string>
<key>ProgramArguments</key>
<array>
<string>/Users/you/.dotnet/tools/sophon-node</string>
<string>start</string>
<string>--service</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ProcessType</key>
<string>Interactive</string>
</dict>
</plist>Replace /Users/you with your actual home directory. launchd does not expand ~ or $HOME here.
ProcessType set to Interactive matters: it tells the system this process belongs to a user session and should not be throttled like background work.
Manage it
launchctl load ~/Library/LaunchAgents/ai.sophon.node.plist
launchctl list | grep sophon
launchctl unload ~/Library/LaunchAgents/ai.sophon.node.plistPermissions. Grant Accessibility to the node in System Settings → Privacy & Security, and add it to Login Items if you want it available before you open a terminal. An OS update can reset Accessibility, which shows up as input silently failing.
Updating
Stop the service, update the binary, start it again:
dotnet tool update -g sophon-nodeFor a self-contained build, take the new archive from /downloads/node and replace the extracted files. Nothing updates itself. A node checks for newer releases and verifies a release manifest's signature before trusting it, but it does not download or install anything. See Platform Support & Limits.
Monitoring
A node has two independent states, and reading them as one is the usual source of confusion.
| What | Values | Meaning |
|---|---|---|
| Pairing status | Pending, Approved, Revoked | Whether the device is allowed to connect at all. A revoked node is refused even while its process is running. |
| Connection | Online, Offline | Whether a heartbeat arrived in the last two minutes. Heartbeats go out every 30 seconds. |
The Devices page shows both: a pending-approval banner, a paired count, an online roll-up, and an online/offline filter. Each device's Overview shows its last heartbeat, its capabilities, and the command latency and refusal counters the node sends along with it.
There are no node lifecycle webhooks in this release. Nothing publishes a node.online or node.offline event, so alerting means polling: sophon nodes list --json from the Gateway CLI is the scriptable way to do it. Per-node webhook events are a roadmap item.
If the node process dies unexpectedly, it writes a crash record beside its own data and uploads it to the Gateway the next time it starts, so a service that keeps restarting leaves a trail rather than disappearing quietly.
The activity recorder is part of the process
If you turned on activity recording, it samples only while the node is running: the service being down is a gap in the history, not an error. Its settings are a Dashboard concern rather than a service one, though — changing them takes effect without restarting the node, so there is no reason to bounce the service to adjust recording.
Troubleshooting
The service starts but the node shows Offline. It cannot reach the Gateway. Only outbound access is needed; nothing listens inbound.
Commands fail with no obvious error. Almost always a session problem: the service is running as the wrong user, or as a system service. See the top of this page.
Consent-gated commands always time out. The prompt is being shown to a session nobody is looking at, or to none at all. It fails closed after 15 seconds.
It stops when you log out (Linux). Enable lingering, above.
Where to go next
- Install & Pair a Node — the config file and the CLI verbs
- Gates, Limits & Audit — why consent needs a real session
- Platform Support & Limits — per-OS requirements
- Logs — where the node writes, and how to raise verbosity
Install & Pair a Node
Install the node binary, pair it with your gateway, approve it, and start it, plus the config file and the five CLI verbs.
Control Runs
How remote.control executes an ordered batch of desktop actions, covering sequencing, observation, timeouts, and the one approval that covers the whole run.