Connect your AI client
Keep using your own AI client — we provide the host agent and MCP connector. Install the agent on your machine, connect your AI client, then run commands or long-running jobs using the machine code.
Setup instructions for AI agents · Concise index
Before you start
Install on the machine you want to control. Its code is the credential: anyone who knows a current code can run commands until you reset it or block their account. Anonymous access works for a new code's first hour; sign in for ongoing access. Access and security.
Pick your client
Install the agent below, then connect your client.
Install the agent
Run the agent on the machine you want to control. Pick your platform — the download or install command updates below.
Download the desktop app. It bundles the agent and gives you a small menu-bar UI to copy or reset the code, pause access, and get notified when a remote operator connects. No terminal needed.
Download for macOSApple Silicon (M1–M4) · Intel (x64)
Download the desktop app. It bundles the agent and gives you a tray UI to copy or reset the code, pause access, and get notified when a remote operator connects. No terminal needed.
Download for WindowsWindows x64
Use the signed release installer on the Linux box (server, VM, Raspberry Pi). It detects your CPU architecture and installs the agent as a systemd service that starts on boot. Do not pipe /install to sudo bash: that would execute mutable website content as root before it can authenticate itself.
2d76d381fc8ed38e7dfb53882e14b2980ee105e0b49ff31cf55403e19e648407 through a channel independent of aicommander.dev (for example, directly with your operator). A fingerprint copied only from this page does not protect you if this website/CDN itself is compromised.$ bash <<'AICOMMANDER_VERIFY'
set -euo pipefail
base="https://aicommander.dev"
expected_key_sha256="2d76d381fc8ed38e7dfb53882e14b2980ee105e0b49ff31cf55403e19e648407"
command -v curl >/dev/null || { echo "curl is required" >&2; exit 1; }
command -v openssl >/dev/null || { echo "OpenSSL 3.x is required" >&2; exit 1; }
case "$(openssl version 2>/dev/null)" in *LibreSSL*) echo "LibreSSL cannot verify this signature" >&2; exit 1;; esac
openssl_help="$(openssl pkeyutl -help 2>&1 || true)"
[[ "$openssl_help" == *"-rawin"* ]] || { echo "OpenSSL pkeyutl -rawin is required" >&2; exit 1; }
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
metadata="$(curl -fsS --proto '=https' --tlsv1.2 "$base/dist/latest")"
version="$(printf '%s' "$metadata" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([0-9][0-9.]*\)".*/\1/p')"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Invalid release version" >&2; exit 1; }
curl -fsS --proto '=https' --tlsv1.2 "$base/dist/v/$version/install" -o "$tmp/install"
curl -fsS --proto '=https' --tlsv1.2 "$base/dist/v/$version/install.sig" -o "$tmp/install.sig"
curl -fsS --proto '=https' --tlsv1.2 "$base/install.pub" -o "$tmp/install.pub"
[[ -s "$tmp/install" && -s "$tmp/install.sig" && -s "$tmp/install.pub" ]]
actual_key_sha256="$(openssl pkey -pubin -in "$tmp/install.pub" -outform DER 2>/dev/null | openssl dgst -sha256 | awk '{print $NF}')"
[[ "$actual_key_sha256" == "$expected_key_sha256" ]] || { echo "Signing-key fingerprint mismatch" >&2; exit 1; }
openssl pkeyutl -verify -pubin -inkey "$tmp/install.pub" -rawin \
-in "$tmp/install" -sigfile "$tmp/install.sig" >/dev/null \
|| { echo "Installer signature verification failed" >&2; exit 1; }
sudo bash "$tmp/install"
AICOMMANDER_VERIFY
x86_64 & arm64 (systemd). Runs as root by default — guard the session code like a password.
Need a Node-managed foreground run (dev box, container, CI)? The same agent is also on npm as @aicommander/agent (needs Node ≥20 on the target):
npx @aicommander/agent
sudo npm i -g @aicommander/agent && sudo aicommander-agent install
npx is an ephemeral foreground run: it prints the full session code, but stops when the process or terminal closes and provides no service or reboot persistence. aicommander-agent install requires Linux with a running, reachable systemd manager; merely having a systemctl binary is not enough. On a compatible host it writes and enables aicommander-agent.service (logs/status keep the code masked — reveal it with sudo aicommander-agent status --reveal). Without systemd, use foreground mode only for temporary access, or configure aicommander-agent run under the platform's native init/process manager or a container restart policy for persistence. The native release flow verifies the installer before sudo and then enforces the binary checksum and Ed25519 signature; the npm package instead relies on npm registry/tarball integrity.
Full technical details and the session-code lifetime: The Linux agent.
CLI agents
Terminal coding agents. Most connect to the remote relay over HTTP with one command. The first use runs a one-time browser sign-in and binds the connector to your account; append ?anonymous=1 to the URL for the quick no-sign-in path (drive a machine by its code).
Native remote-HTTP MCP — nothing to install. The first connection opens a one-time browser sign-in and binds the connector to your account.
claude mcp add --transport http aicommander https://aicommander.dev/mcp
Verify with claude mcp list (look for aicommander: ✔ Connected), then mention a code in chat: "Run npm test on AIC-7K3P-WX9M-RTBN."
Skip the sign-in? Use https://aicommander.dev/mcp?anonymous=1 instead for the anonymous-by-code path (no account; works for a code's first hour).
Recent Codex connects to remote HTTP MCP servers directly:
codex mcp add aicommander --url https://aicommander.dev/mcp
The first connection runs a one-time browser sign-in and binds the connector to your account; append ?anonymous=1 to the URL for the no-sign-in anonymous-by-code path. Pass --url before the URL — without it Codex treats the address as a command and fails with os error 2. Prefer a local stdio process? Add this to ~/.codex/config.toml instead:
[mcp_servers.aicommander]
command = "npx"
args = ["-y", "@aicommander/mcp"]
env = { AICOMMANDER_SERVER = "https://aicommander.dev" }
On Windows use command = "npx.cmd" for the stdio variant — plain npx resolves to npx.exe and the spawn fails with the same os error 2.
Harness landing: Keep Codex — give it your machines.
Grok Build / the grok CLI connects to remote HTTP MCP servers directly:
grok mcp add --transport http aicommander https://aicommander.dev/mcp
The first connection runs a one-time browser sign-in and binds the connector to your account; append ?anonymous=1 to the URL for the no-sign-in anonymous-by-code path. Prefer a local stdio process? Run grok mcp add aicommander -- npx -y @aicommander/mcp (set AICOMMANDER_SERVER=https://aicommander.dev), or declare the HTTP server in ~/.grok/config.toml:
[mcp_servers.aicommander]
url = "https://aicommander.dev/mcp"
Harness landing: Keep Grok — give it your machines.
Gemini CLI connects to remote Streamable HTTP MCP servers directly:
gemini mcp add -s user --transport http aicommander https://aicommander.dev/mcp
Use --transport http for Streamable HTTP (legacy SSE uses a different flag). User scope writes ~/.gemini/settings.json. First connect may open a one-time browser sign-in; append ?anonymous=1 for the no-sign-in anonymous-by-code path. Prefer a local stdio bridge?
gemini mcp add -s user -e AICOMMANDER_SERVER=https://aicommander.dev aicommander npx -y @aicommander/mcp
Harness landing: Keep Gemini — give it your machines.
opencode connects to remote HTTP MCP servers directly:
opencode mcp add aicommander --url https://aicommander.dev/mcp
The first connection runs a one-time browser sign-in and binds the connector to your account; append ?anonymous=1 to the URL for the no-sign-in anonymous-by-code path. For the optional account features, attach your API key as an environment variable: add --env AICOMMANDER_TOKEN=<your-key> to the command. Then aliases work — see Accounts.
Harness landing: Keep OpenCode — give it your machines.
Qwen Code connects to remote Streamable HTTP MCP servers directly:
qwen mcp add --transport http aicommander https://aicommander.dev/mcp
Use --transport http for Streamable HTTP (writes httpUrl in ~/.qwen/settings.json or project .qwen/settings.json). First connect may open a one-time browser sign-in; append ?anonymous=1 for the no-sign-in anonymous-by-code path. Prefer a local stdio bridge?
qwen mcp add -e AICOMMANDER_SERVER=https://aicommander.dev aicommander npx -y @aicommander/mcp
Harness landing: Keep Qwen Code — give it your machines.
goose connects to remote MCP servers over Streamable HTTP (not SSE). Quick one-shot session:
goose session --with-streamable-http-extension "https://aicommander.dev/mcp"
Persist it with goose configure → Add Extension → Remote Extension (Streamable HTTP), or add this to ~/.config/goose/config.yaml (goose uses uri and type: streamable_http):
extensions:
aicommander:
enabled: true
type: streamable_http
name: aicommander
description: Remote machines via AI Commander
uri: "https://aicommander.dev/mcp"
timeout: 300
First connect may open a one-time browser sign-in; append ?anonymous=1 for the no-sign-in anonymous-by-code path. For account aliases, add headers: { Authorization: "Bearer <your-key>" } under the extension (or enter that header in goose configure). Docs: Using extensions.
Harness landing: Keep goose — give it your machines.
Amp connects to remote MCP servers by URL. Add AI Commander with the CLI:
amp mcp add aicommander https://aicommander.dev/mcp
Or set amp.mcpServers in ~/.config/amp/settings.json (user) or .amp/settings.json (workspace):
"amp.mcpServers": {
"aicommander": {
"url": "https://aicommander.dev/mcp"
}
}
Personal remote definition on ampcode.com: amp mcp remote --personal add aicommander https://aicommander.dev/mcp. First connect may open a one-time browser sign-in; append ?anonymous=1 for the no-sign-in anonymous-by-code path. For account aliases, add headers: { Authorization: "Bearer <your-key>" } (Amp supports ${VAR_NAME} env substitution). Docs: Amp MCP.
Harness landing: Keep Amp — give it your machines.
Kilo Code connects to remote MCP servers by URL. In Settings → Agent Behaviour → MCP Servers, add a Remote (HTTP) server, or edit kilo.jsonc:
"mcp": {
"aicommander": {
"type": "remote",
"url": "https://aicommander.dev/mcp",
"enabled": true
}
}
Global: ~/.config/kilo/kilo.jsonc. Project: ./kilo.jsonc or ./.kilo/kilo.jsonc. First connect may open a one-time browser sign-in (OAuth); append ?anonymous=1 for the no-sign-in anonymous-by-code path. For account aliases, add headers: { Authorization: "Bearer <your-key>" } (Kilo supports {env:VARIABLE_NAME}). Docs: Using MCP in Kilo Code.
Harness landing: Keep Kilo Code — give it your machines.
OpenHands connects to remote MCP servers over HTTP. Add AI Commander with the CLI:
openhands mcp add aicommander --transport http https://aicommander.dev/mcp
Or edit ~/.openhands/mcp.json:
{
"mcpServers": {
"aicommander": {
"url": "https://aicommander.dev/mcp",
"auth": "oauth"
}
}
}
First connect may open a one-time browser sign-in; append ?anonymous=1 for the no-sign-in anonymous-by-code path. Prefer stdio? openhands mcp add aicommander --transport stdio --env AICOMMANDER_SERVER=https://aicommander.dev npx -- -y @aicommander/mcp. Docs: OpenHands MCP Servers.
Harness landing: Keep OpenHands — give it your machines.
pi installs the @aicommander/mcp stdio bridge from npm:
pi install npm:@aicommander/mcp
The bridge defaults to the hosted relay (https://aicommander.dev), so no extra config is needed to drive a machine by raw code. For aliases, set AICOMMANDER_TOKEN=<your-key> in the bridge's environment — see Accounts.
Coding GUIs & IDEs
Editors and AI coding tools — Cursor, Continue, Windsurf, Antigravity, Zed, and others — accept an MCP server as JSON (or YAML for Continue) and run it locally over the same stdio bridge. The config is identical everywhere; only the file location differs:
{
"mcpServers": {
"aicommander": {
"command": "npx",
"args": ["-y", "@aicommander/mcp"],
"env": {
"AICOMMANDER_SERVER": "https://aicommander.dev"
}
}
}
}
- Cursor —
~/.cursor/mcp.json(or Settings → MCP → Add server). - Windsurf —
~/.codeium/windsurf/mcp_config.json(or Settings → Cascade → MCP servers). - Continue —
~/.continue/config.yamlmcpServerslist, or drop JSON/YAML in.continue/mcpServers/. Harness landing: Keep Continue — give it your machines. - Sourcegraph Cody — VS Code
settings.json/ JetBrainscody_settings.jsoncody.mcpServerswith the same stdiocommand/args/envmap (npx -y @aicommander/mcp). Or Cody extension → MCP Settings. Harness landing: Keep Cody — give it your machines. - Cline — Cline → MCP Servers /
cline_mcp_settings.jsonwith the same stdiomcpServersmap. Harness landing: Keep Cline — give it your machines. - Roo Code — Roo Code → MCP Servers /
mcp_settings.jsonor project.roo/mcp.jsonwith the same stdiomcpServersmap. Harness landing: Keep Roo Code — give it your machines. - Zed — Settings → AI → MCP Servers /
context_serversin settings.json (localnpx @aicommander/mcpor remote URLhttps://aicommander.dev/mcp). Harness landing: Keep Zed — give it your machines. - Antigravity — add the server in Settings → MCP / Tools; paste the same JSON when it asks for a server definition.
- Any other IDE — wherever it stores its MCP server list, use the same three keys (
command,args,env).
On Windows use "command": "npx.cmd". Add "AICOMMANDER_TOKEN": "<your-key>" to env for alias-based access. Restart the editor; the remote_exec and session_status tools appear.
Chat apps — ChatGPT & Claude (MCP connectors)
The desktop / web chat apps connect to the relay as a custom connector — a remote HTTPS MCP endpoint. Adding the connector runs a one-time browser sign-in and binds it to your account (so saved-machine aliases resolve); the client auto-registers, so no manual credentials are needed. For the quick no-sign-in path, point it at https://aicommander.dev/mcp?anonymous=1 instead. A confidential OAuth client (Client ID + Client Secret) is an optional way to authenticate — see Optional: pre-provisioned OAuth credentials below.
ChatGPT
In ChatGPT, enable Developer mode in Settings → Security and login. Then open ChatGPT Plugins, select plus (+), enter a name and description, and add the public MCP URL under Connection:
- URL:
https://aicommander.dev/mcp - Auth: leave it empty — the connector auto-registers and you complete a one-time browser sign-in that binds it to your account. (For a client that needs pre-provisioned credentials, paste a Client ID + Client Secret instead; see below. For the anonymous-by-code path, use
https://aicommander.dev/mcp?anonymous=1as the URL.)
Harness landing: Keep ChatGPT — give it your machines (OpenAI / GPT / Astra).
Claude Desktop
Custom connector (recommended): Customize → Connectors → Add custom connector, URL https://aicommander.dev/mcp, leave auth empty — the connector auto-registers and you complete a one-time browser sign-in that binds it to your account. (For the anonymous-by-code path use https://aicommander.dev/mcp?anonymous=1; for a client that needs pre-provisioned credentials, paste a Client ID + Client Secret — see below.) Or use the stdio config file claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/) with the same JSON as the IDE config above, then restart Claude Desktop.
Optional: pre-provisioned OAuth credentials (Client ID + Secret)
Account binding is the default at https://aicommander.dev/mcp: just point the connector there and complete the one-time browser sign-in — the client auto-registers (Dynamic Client Registration), so most clients need no manual credentials. You only need a confidential OAuth client — a Client ID and a one-time Client Secret — for clients that require pre-provisioned credentials and can't self-register. You still complete the normal browser sign-in when you connect, and that interactive login is what decides which account the connector's access is bound to.
- In the dashboard, go to OAuth clients and create a client. For the Redirect URI, Claude (Desktop, web, mobile) uses the fixed callback
https://claude.ai/api/mcp/auth_callback(pre-filled); Claude Code uses a local loopback, so addhttp://localhost/callbackandhttp://127.0.0.1/callback. You get a Client ID and a one-time Client Secret. - Point the connector at
https://aicommander.dev/mcpand, in its Auth / advanced settings, paste theclient_idandclient_secret. - Connect — Claude opens a browser sign-in; completing it binds the connector to your account.
https://aicommander.dev/mcp and sign in. For the explicit anonymous opt-in (drive a machine by its code, no sign-in), append ?anonymous=1 to the URL: https://aicommander.dev/mcp?anonymous=1.Chat apps without MCP
No connector support, or don't want to set one up? Any capable chat model — the ChatGPT app, the Claude app, or similar — can drive a machine if it can make HTTP requests through browsing, code interpreter, or a generic fetch tool. There is no config to paste; give it the relay, the machine code, and the goal.
Give it the relay, the machine's code, and the goal. For example:
# Paste this into ChatGPT / Claude (with web or tool access enabled)
Connect to my computer AIC-7K3P-WX9M-RTBN through aicommander.dev
and tell me its disk usage.
The REST API is documented at https://aicommander.dev/openapi.json
(POST https://aicommander.dev/api/v1/exec with JSON {"code","command"}).
No auth is needed for the first hour after a code is created or refreshed;
sign in (free) to use the code at any time after that.
The model can then call /api/v1/exec with the code and a command like df -h, read the JSON result, and answer. You can keep going in plain language ("now restart nginx", "tail the last 50 lines of the app log") and it will make new calls as needed.
aicommander.dev/llms-full.txt for onboarding and /openapi.json for the machine-readable API. With an account, give it an API key (Authorization: Bearer <key>) so it can reach machines by alias — see Accounts.
Any MCP client (raw HTTP+SSE)
The relay speaks standard MCP over HTTP with SSE responses. Point any compliant client at:
# Endpoint — no auth header needed
POST https://aicommander.dev/mcp
Accept: text/event-stream
Tools exposed include remote_exec(code, command, cwd?, timeout_ms?), session_status(code), detached jobs, file transfer, and (with an API key) list_machines(). Command output is buffered; SSE carries heartbeats while exec runs and one final result event.
REST API & Agent Skill (no MCP needed)
If your client doesn't speak MCP — or you want a single HTTP call — use the REST API. Auth matches MCP: no token means anonymous access during a code's first hour, while Authorization: Bearer <api-key> enables alias-based access after sign-in. There is no OAuth browser flow on this surface; REST clients paste an API key. Harness landing for terminal pair-programmers: Keep Aider — give it your machines.
# Is the machine online?
$ curl -s https://aicommander.dev/api/v1/status/AIC-7K3P-WX9M-RTBN
# Run a command (buffered JSON result)
$ curl -s -X POST https://aicommander.dev/api/v1/exec \
-H 'Content-Type: application/json' \
-d '{"code":"AIC-7K3P-WX9M-RTBN","command":"df -h"}'
The exec response is { "ok": true, "exitCode": 0, "stdout": "…", "stderr": "…", "truncated": false }. Add "cwd" and "timeout_ms" as needed. With Accept: text/event-stream, the server sends heartbeat comments while the command runs and one final result event; stdout and stderr are still buffered. Machine-readable spec: /openapi.json.
Install the Agent Skill (instead of the MCP server)
SKILL.md is an open Agent Skills format supported by Claude Code and other agents. If you prefer a skill over an MCP server, install the hosted AI Commander skill into your agent's skills directory. It is served from the relay, so there is nothing to clone. The example below uses Claude's ~/.claude/skills/ location; point it at your agent's skills folder if it differs:
$ mkdir -p ~/.claude/skills/aicommander && \
curl -fsSL https://aicommander.dev/skill/SKILL.md -o ~/.claude/skills/aicommander/SKILL.md
Your agent auto-loads it on the next session — quote an AIC-… code and it drives the machine over the REST API, with no MCP server to add and no OAuth. View the skill: /skill/SKILL.md.
Installing the agent on a QNAP NAS (separate skill)
QNAP's QTS / QuTS hero is not a normal Linux distribution: there is no systemd, /usr/local and /etc are rebuilt from a ramdisk at every boot, and the standard installer fails there with curl: (23) Failure writing output to destination. A second, install-time skill walks an agent through the procedure that works. Install it only when the target machine is a QNAP NAS:
$ mkdir -p ~/.claude/skills/aicommander-qnap && \
curl -fsSL https://aicommander.dev/skill/qnap/SKILL.md -o ~/.claude/skills/aicommander-qnap/SKILL.md
View the skill: /skill/qnap/SKILL.md.
Running GPU / long jobs (separate skill)
Heavy work — PyTorch training, fine-tuning, dataset processing, long builds — is exactly the work that dies with the session that started it: normally you'd babysit it over ssh with tmux and nohup, and hope the laptop stays awake. Run it as a detached job instead of a plain remote_exec / POST /api/v1/exec command. A third skill covers those tools and the conventions around them: picking a machine from its reported GPUs, a uv workspace per run, reserving a card with gpu_index, paging a training log, keeping model caches off the system partition, and getting artifacts off the box. Install it when the user has a GPU machine:
$ mkdir -p ~/.claude/skills/aicommander-gpu && \
curl -fsSL https://aicommander.dev/skill/gpu/SKILL.md -o ~/.claude/skills/aicommander-gpu/SKILL.md
View the skill: /skill/gpu/SKILL.md.
Long-running jobs
A training run, a long build, or an overnight dataset job shouldn't need a session kept alive. Start it as a detached job: the call returns a jobId straight away and the work keeps running on your machine after the chat ends and after your client disconnects. No tmux, no screen, nothing to keep open. A job also outlives the agent itself restarting on all three platforms; on Linux each job started by an agent that already has this feature is launched into its own transient systemd scope, outside the agent service's control group, so stopping or restarting the service — an agent upgrade included — leaves it running, provided the box runs systemd and the agent runs as root. Without both, jobs stay in the service's control group and a restart still ends them, and a job that was already running when the agent was upgraded to that version is in no scope either, so the upgrade's own restart ends it. See Start ML training on your own GPU for the full walkthrough.
From an MCP client — just ask
Five tools cover the whole lifecycle. In practice you phrase it in plain language and the client picks the tool:
- Start it — “on
gpu-box, start./.venv/bin/python train.pyin/home/u/aic-jobs/mytrainon GPU 0 as a job called mytrain” →remote_job_start(code, command, cwd?, env?, name?, gpu_index?), which answers with thejobId.cwdmust be an absolute path that already exists, andenvtakes string values only. - See what's running — “what jobs are running on
gpu-box?” →remote_job_list(code, status?, include_command?). - Check on it — “how did mytrain go?” →
remote_job_status(code, job_id, include_command?), which reportsrunning,exitedwith the exit code, orunknown(the process is gone and no exit code was recorded — after an agent restart, aSIGKILL, an OOM kill, or a cancellation that had to escalate — so the outcome genuinely can't be determined). - Read the log — “show me the last 200 lines of mytrain” →
remote_job_logs(code, job_id, tail_lines?, offset_bytes?, max_bytes?). Stdout and stderr are interleaved, the last 200 lines by default. Each reply is capped at 256 KiB and carries anextOffsetBytes; feed that back asoffset_bytesto follow a growing log instead of re-reading it. - Stop it — “cancel mytrain” →
remote_job_cancel(code, job_id), which kills the whole process tree and releases any reserved GPU.
Passing gpu_index reserves that NVIDIA card for the job, so a second run asking for the same card is refused (gpu_busy, naming the holder) rather than both runs hitting CUDA OOM. On agents 1.0.50 and newer, when the machine's GPU list is known, an index that is not on it is refused with invalid_request rather than starting a phantom job; an older agent takes any in-range index and lets the job fail inside CUDA. Jobs have no elevated option.
Over REST
Same operations, same authorization as /api/v1/exec. code goes in the JSON body for the POST and as a query parameter everywhere else:
# Start a job — returns the jobId immediately
$ curl -s -X POST https://aicommander.dev/api/v1/jobs \
-H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' \
-d '{"code":"gpu-box","command":"./.venv/bin/python train.py","cwd":"/home/u/aic-jobs/mytrain","name":"mytrain","gpu_index":0}'
# → {"ok":true,"job":{"jobId":"9f2c1b7ad4e05386","name":"mytrain","status":"running","exitCode":null,"gpuIndex":0,"logBytes":0,"truncated":false, …}}
# List jobs on the machine
$ curl -s 'https://aicommander.dev/api/v1/jobs?code=gpu-box' -H 'Authorization: Bearer YOUR_API_KEY'
# Poll one job's status
$ curl -s 'https://aicommander.dev/api/v1/jobs/9f2c1b7ad4e05386?code=gpu-box' -H 'Authorization: Bearer YOUR_API_KEY'
# Tail the log, then follow it with offset_bytes=<nextOffsetBytes>
$ curl -s 'https://aicommander.dev/api/v1/jobs/9f2c1b7ad4e05386/logs?code=gpu-box&tail_lines=200' -H 'Authorization: Bearer YOUR_API_KEY'
$ curl -s 'https://aicommander.dev/api/v1/jobs/9f2c1b7ad4e05386/logs?code=gpu-box&offset_bytes=131072' -H 'Authorization: Bearer YOUR_API_KEY'
# Cancel it
$ curl -s -X DELETE 'https://aicommander.dev/api/v1/jobs/9f2c1b7ad4e05386?code=gpu-box' -H 'Authorization: Bearer YOUR_API_KEY'
A log reply is { "ok": true, "logs": { "chunk": "<base64>", "offsetBytes": …, "nextOffsetBytes": …, "eof": …, "truncated": … } } — chunk is base64 because process output is arbitrary bytes. eof: true means you've read to the current end of the file, not that the job finished. Machine-side refusals get their own status: 409 gpu_busy, 429 too_many_jobs, 404 not_found, 400 invalid_request. Full reference: Docs → Detached jobs.
To get a checkpoint or a render off the machine, use POST /api/v1/pull — it returns a download link, and access to the stored copy ends after 24 hours. An hourly, retrying sweep removes inaccessible expired bytes afterward. Above its 100 MiB limit, have the job push the artifact to storage you already control (aws s3 cp, rclone, scp) as its final step.
Accounts & API keys
The hosted relay is https://aicommander.dev/mcp; there is nothing to deploy. HTTP connections run a one-time browser sign-in and bind the connector to your account so saved-machine aliases resolve. For the no-account path, use https://aicommander.dev/mcp?anonymous=1 and a machine's code during its first hour.
Remote HTTP clients include Claude Code, Grok, Gemini CLI, opencode, Qwen Code, Codex, ChatGPT and Claude Desktop connectors. Local stdio clients use @aicommander/mcp: pi, Cursor, Continue, Windsurf, Antigravity and Claude Desktop's config file.
You can try a fresh machine code without an account for the first hour. For ongoing access, sign in with a passkey or email magic link — no password. An account lets you:
- Save machines under friendly aliases (e.g.
prod-dbinstead ofAIC-7K3P-WX9M-RTBN). After a machine is linked, use the alias or the raw code while the owner allows access. - Generate or revoke a personal API key in the dashboard and set it as the Bearer token in any MCP client — then reach all your linked machines by alias.
With an API key set (Authorization: Bearer <key>, or AICOMMANDER_TOKEN=<key> for the @aicommander/mcp stdio bridge), remote_exec and session_status accept either a raw AIC-… code or one of your aliases.
Owner controls — who can reach your machine
The machine's owner stays in charge of access at all times:
- Block or unblock a specific account — at any time. From the CLI,
aicommander-agent block-admin <n>andaicommander-agent unblock-admin <n>; from the desktop tray, Linked Accounts…. Blocking refuses that account's access and stops it re-linking; the account stays listed in a Blocked section, and unblocking restores access instantly. - Reset the code —
aicommander-agent change-codeor Reset Access Code in the desktop app. This mints a brand-new code; the old code dies immediately and all links and blocks are cleared. It's the master "lock everyone out / start over" action.
Service tokens — for automation
remote_exec and /api/v1/exec give an AI root-level, interactive access — great when you're driving the machine yourself. For unattended work — a cron job, a CI step, or one AI agent calling another with claude -p — there's a deliberately narrower lane: secure exec, driven by a service token. It's the opposite of the root path: a non-root sandbox, pinned to one machine, limited to an allowlist of commands.
- Non-root, checked every time. Commands run as a dedicated
aicommander-execuser, not root. The agent refuses privileged primary/supplementary groups and any unreadable or ambiguous local account/group data before spawn. - Allowlisted, no shell, known runners denied. You send an
argvvector and the exact command basenames you permit. There's no shell, so&&,;and$(…)are inert text. The Worker and agent also refuse known shells/runtimes (sh,bash,python*,node, etc.), argument runners (env,find,xargs,awk,sed, etc.), privilege tools and container/orchestration clients (sudo,docker,kubectl, etc.). - Pinned & long-lived. A token works on exactly one machine, never expires, and skips the 24h dashboard re-activation gate — so automation keeps running. Revoke it anytime.
- Audited without payloads. Each run logs the command basename, exit code, duration, caller IP and byte counts — never your arguments, input, or output.
Create a token in the dashboard (Service tokens → pick a machine → choose allowed commands) — the plaintext token (aics_…) is shown once. Then call the secure-exec endpoint:
# Run an allowlisted command, non-root, on the pinned machine
$ curl -s -X POST https://aicommander.dev/api/v1/secure-exec \
-H 'Authorization: Bearer aics_…' \
-H 'Content-Type: application/json' \
-d '{"argv":["df","-h"]}'
The response mirrors /api/v1/exec ({ "ok": true, "exitCode": 0, "stdout": "…", "stderr": "…", "truncated": false }, output capped at 8 MiB; add a base64 "input" for stdin, plus optional "cwd" / "timeout_ms"). Only a service token is accepted here — an account API key, admin token, or anonymous request is rejected. Create tokens in the dashboard; list/revoke them via /api/v1/secure-tokens and view recent runs via /api/v1/secure-exec/audit; full reference in the API docs.
claude -p inside the sandbox, provision the aicommander-exec user's own ~/.claude (or set CLAUDE_CONFIG_DIR).