# DSH Remote Protocol v1 This document is the platform-neutral contract shared by the native iOS and Android DSH Remote clients. It does not add a relay or move Harness execution off the user's computer. ## Compatibility identifier - Contract name: `dsh-remote` - Contract version: `1` - Current bundled Harness: `@deepseek-ai/dsh@0.1.5-rc.2` - Desktop adapts the retained Remote v1 methods and live events to the upstream Typert gateway. - Clients must ignore unknown JSON object fields. - A missing optional capability must degrade to a readable, retryable state. - A server response with a mismatched `rpcId` must be rejected. `host.describe` remains the authoritative connection check. A successful HTTP status from another route is not sufficient evidence that a host is Harness. ## Transport Remote supports exactly two trust models: 1. A Desktop-issued private-LAN URL imported from its QR code. The URL may use private-address HTTP and must include the random bearer credential supplied by Desktop. 2. User-managed HTTPS, normally provided by Tailscale Serve. Credentials, Tailnet membership, and ACLs remain owned by the user. HTTP RPC calls use `POST /api/`. Live events use `GET /api/events.mux` upgraded to WebSocket (`wss` for HTTPS, `ws` for the authenticated private-LAN exception). When a bearer credential exists, both transports send `Authorization: Bearer `. Desktop exchanges the local Harness launch token for a browser cookie inside its proxy. LAN bearer credentials and Tailscale membership remain the client trust boundary; the upstream cookie is never given to the phone. Both proxies expose only the reviewed Remote API routes. Clients must reject manually entered cleartext URLs. The only cleartext HTTP exception is a private LAN endpoint imported from a Desktop-generated pairing QR code. ## RPC envelope Request: ```json { "type": "client-request", "rpcId": "client-generated-uuid", "method": "session.list", "payload": {} } ``` Success response: ```json { "rpcId": "client-generated-uuid", "result": { "ok": true, "value": {} } } ``` Failure response: ```json { "rpcId": "client-generated-uuid", "result": { "ok": false, "error": { "code": "stable-machine-code", "message": "Human-readable host message" } } } ``` ## Reviewed capability boundary The LAN proxy exposes only these RPC routes: - `host.describe` - `workspace.list` - `session.list` - `session.create` - `session.history` - `session.attachment` - `session.models` - `session.selectModel` - `session.prompt` - `session.updateQueue` - `session.cancel` - `fileReferences/list` - `sessionReferenceResolver/candidates` - `subagent.list` - `subagent.history` - `subagent.prompt` - `subagent.interrupt` - `respond` The only upgrade route is `events.mux`. Settings, credentials, arbitrary directory operations, raw terminal access, plugin installation, and workspace mutation remain outside the mobile boundary. ## Live events Each WebSocket message contains an `rpcId` plus an object payload. Version 1 recognizes these payload `type` values: - `session/event` - `session/projection` - `session/queue` - `approval/requested` - `approval/resolved` - `question/requested` - `question/resolved` Unknown event types are ignored. The client combines live events with bounded polling so reconnects and process suspension cannot permanently hide state. ## Client behavior - Preserve project names, paths, prompts, and model output as user content. - Localize product-owned labels and errors only. - Keep access credentials, model calls, repositories, and execution on the user's computer. - Never expose a generic cleartext network policy for the whole application. - Bound image decoding and base64 expansion; the wire limit is 136 MiB. - Persist saved computers locally and provide explicit removal of one or all computers. - Provide an offline Demo client without network or model calls. - Treat approvals and structured questions as unresolved until the host emits or returns an authoritative resolution. ## Cross-platform acceptance Every native client must cover: - connection parsing and endpoint policy; - matching and mismatched RPC identifiers; - API and HTTP failures; - session, queue, approval, and question event parsing; - WebSocket reconnect behavior; - unknown fields and unknown event types; - English and Simplified Chinese product copy; - light, dark, large-text, and screen-reader semantics; - fresh install, saved-computer, offline Demo, and destructive data-removal flows.