Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

Read the daemon status

/v1/daemonGET
Version, pid, build id, listeners and Bluetooth state of the running daemon.
Authorizationoptional
bearerAuth?Bearer <token>
Required on the tunnel. An API token of the account, from ghosthand tunnel url --new-token. The unix socket needs no token
Response
200 · Daemon state
version *string
Version of the daemon binary, same as the npm package
pid *integer
Process id. It stays the same across POST /v1/daemon/restart.
executable *string
CLI binary that Ghosthand.app was installed from. Restart installs this path again, unless it gets executable.
safeRestart?boolean
Supports executable selection and ifIdle lease protection. Missing or false means the CLI must not automatically restart this daemon.
buildId *string
Changes when executable is rebuilt. Compare it with the CLI to find a stale daemon.
startedAt *string (date-time)
When the daemon (or its last restart) started
bluetooth *object
Show properties
state *"not-started" | "starting" | "on" | "permission-missing" | "permission-denied" | "permission-restricted" | "powered-off" | "unsupported" | "error"
not-started: allowed, but no USB iPhone uses Bluetooth mode yet; starts on the first Bluetooth command or when list or setup sees one. starting: waiting for Bluetooth or HID publication. on: the HID service is published. Permission states are checked before hardware state. Missing or denied permission tells the agent to run setup and poll setup status until ready. Only the setup app's buttons request permissions or open privacy settings. powered-off: turn Bluetooth on. unsupported: authorized, but Classic is unavailable. error: service failure.
error?string
Why Bluetooth is not available, e.g. it is off or not allowed for the daemon
listen *object
Where the daemon accepts requests
Show properties
unix *string
Path of the unix socket
tunnel?object
The connection to ghosthand.dev that serves remote MCP (https://ghosthand.dev/mcp) and this API from other computers. It runs while the Mac is signed in.
Show properties
connected *boolean
False while signed out, or while the daemon connects or reconnects
mac?string
This Mac's slug in the account, e.g. studio. MCP leases on it look like studio:42.
url?string
Base URL of this API through the tunnel, e.g. https://ghosthand.dev/macs/studio. Absent until connected.
Default · Error (RFC 9457 problem details). `code` is stable; switch on it, not on `title` or `detail`.
code *"invalid-request" | "unauthorized" | "not-found" | "device-not-found" | "no-device" | "multiple-devices" | "permission-denied" | "bluetooth-unavailable" | "bluetooth-disconnected" | "no-frame" | "device-locked" | "lock-failed" | "pairing-timeout" | "pointer-not-found" | "lock-state-unavailable" | "lockdown-unavailable" | "unlock-failed" | "mute-unconfirmed" | "text-not-found" | "wait-text-timeout" | "cancelled" | "lease-invalid" | "device-leased" | "recording-active" | "no-recording" | "recording-failed" | "daemon-busy" | "tunnel-offline" | "login-required" | "subscription-required" | "unsupported-in-mode" | "xctest-unavailable" | "task-failed" | "internal-error"
Stable error code. Switch on it; title and detail are for people.
title *string
Short summary of the code, the same for every error with this code
status *integer
HTTP status of the error. In stream mode the response status stays 200.
detail *string
What happened and what to do next. Agents should read it.
Request example
curl -X GET "https://ghosthand.dev/macs/studio/v1/daemon" \ -H "Authorization: Bearer <token>"
Response example
{ "version": "0.9.0", "pid": 51234, "executable": "/usr/local/bin/ghosthand", "buildId": "3f9a1c2e", "startedAt": "2026-10-07T08:55:00.000Z", "bluetooth": { "state": "on" }, "listen": { "unix": "/Users/tommy/.ghosthand/daemon.sock" } }