Hosts relay
Run a shell command or read a file on your desktop from your phone, a browser or a script — no SSH, no inbound port, same account only.
The hosts relay lets any client of yours execute a small, fixed vocabulary of operations on one of your desktops that has the codai desktop app open. The desktop keeps an outbound SSE connection to the gateway and announces itself as a host; a requester POSTs an exec and the gateway relays it, then relays the answer back. Nothing is persisted, nothing listens on the desktop, and in v1 host and requester must belong to the same user.
phone / browser / script gateway desktop (host)
────────────────────── ───────── ───────────────
POST /v1/hosts/:dev/exec ───────▶ rendezvous by device id ─────▶ event: exec (over its SSE)
{ op, args } runs the op locally
200 { ok, result } ◀─────── rendezvous by req_id ◀───── POST /v1/hosts/exec/:reqId/resultThis surface is served by https://ai.codai.ro and is not part of the published Shared Sessions v1 spec; treat it as remote sessions v1.
Which desktops are online
curl https://ai.codai.ro/v1/hosts -H "Authorization: Bearer $CODAI_API_KEY"{
"hosts": [
{
"device_id": "1c2f…-uuid",
"name": "Work laptop",
"platform": "desktop",
"os": "windows",
"hostname": "DRAGOS-PC",
"roots": ["E:\\gh\\codai", "C:\\Users\\dragos\\Documents"],
"since": 1790160000000,
"last_seen": 1790160045000
}
]
}Only your own devices appear, most recently seen first. since and last_seen are epoch milliseconds; a host drops off the list 45 seconds after its last heartbeat. roots are the directories the desktop will let file operations touch.
Run something on a host
POST /v1/hosts/:deviceId/exec — needs your key and an x-codai-device header identifying the requester.
Prop
Type
curl https://ai.codai.ro/v1/hosts/1c2f…/exec \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "x-codai-device: $(uuidgen)" -H "x-codai-device-platform: cli" \
-H "Content-Type: application/json" \
-d '{ "op": "shell", "args": { "cmd": "git status --short", "cwd": "E:\\gh\\codai" }, "timeout_ms": 30000, "label": "docs check" }'Response — the host's answer, verbatim:
{
"req_id": "c0de…-uuid",
"ok": true,
"result": { "code": 0, "stdout": " M apps/docs/content/docs/en/sessions/hosts.mdx\n", "stderr": "", "truncated": false, "timed_out": false, "duration_ms": 412 },
"error": null
}A host-side failure — a path outside roots, a bad argument, a command that could not start — is still HTTP 200 with ok: false and error set. Only relay problems are HTTP errors:
| Status | code | When |
|---|---|---|
400 | bad_request | Missing x-codai-device, unknown op, timeout_ms out of range. |
404 | not_found | deviceId is not one of your devices (the gateway never reveals other users' device ids). |
409 | host_offline | It is your device but it is not connected as a host right now — open codai desktop. |
504 | host_timeout | The host did not answer within timeout_ms — host did not answer within 30000 ms. |
Operations
The desktop implements each op with its own permission-checked commands; file paths must fall under the host's roots.
op | args | result |
|---|---|---|
shell | { cmd: string, cwd?: string, timeout_ms?: number } — PowerShell on Windows, the login shell elsewhere; timeout_ms is clamped to the request's. | { code, stdout, stderr, truncated, timed_out, duration_ms } |
fs_read | { path: string, max_bytes?: integer } — default 64 KB, max 512 KB. | { path, content, size, truncated } |
fs_write | { path: string, content: string } | { path, bytes, created } |
fs_list | { path: string } | { path, entries: [{ name, kind, size }], truncated } |
fs_roots | {} | { roots: string[] } |
info | {} | { os: 'windows' | 'linux' | 'macos', hostname, roots, platform: 'desktop' } |
shell runs with the desktop user's privileges. The relay adds no sandbox of its own — the desktop app's autonomy setting and rules gate what it will execute, and every request shows up in its UI with your label. Keep API keys that can reach /v1/hosts as tightly scoped as any SSH key.
Building a host
You only need this section if you are writing your own host (the codai desktop app already is one).
Announce yourself
Open GET /v1/hosts/stream with your key and the host's own x-codai-device. Describe the machine in the query string (a GET has no body): ?os=windows&hostname=DRAGOS-PC&roots=E:\gh\codai,C:\Users\dragos\Documents — os ≤ 40 chars, hostname ≤ 120, up to 32 roots.
The first frame is hello, then a ping every 15 s; each ping refreshes your presence (TTL 45 s). The server closes the stream after 6 hours — reconnect with backoff (the desktop uses 1 s → 30 s).
event: hello
data: {"device_id":"1c2f…","heartbeat_ms":15000}
event: ping
data: {"t":1790160045000}
event: exec
data: {"req_id":"c0de…","op":"shell","args":{"cmd":"git status --short","cwd":"E:\\gh\\codai"},"timeout_ms":30000,"label":"docs check","from_device_id":"9a0b…","ts":1790160050000}Execute and reply
For every exec frame, run the op and POST /v1/hosts/exec/:reqId/result with { "ok": boolean, "result"?: any, "error"?: string ≤ 4000 }. The body must stay under 2 MB, and reqId must be the 36-character UUID you received. A missing or late reply simply lets the requester's timeout_ms expire.
curl -X POST https://ai.codai.ro/v1/hosts/exec/c0de…/result \
-H "Authorization: Bearer $CODAI_API_KEY" -H "x-codai-device: 1c2f…" \
-H "Content-Type: application/json" \
-d '{ "ok": true, "result": { "code": 0, "stdout": "…", "stderr": "", "truncated": false, "timed_out": false, "duration_ms": 412 } }'The req_id is an unguessable capability handed only to you; the gateway matches the reply to the waiting requester by it and answers { "ok": true }.
Enforce your own roots
The gateway relays; you decide what runs. Refuse paths outside your advertised roots, clamp shell timeouts to the request's timeout_ms, cap output sizes (set truncated: true), and surface every request in your UI with its label and from_device_id.