# Maritime CLI: instructions for AI agents Copy this file into your agent's context (CLAUDE.md, AGENTS.md, or a system prompt), or let the agent fetch it from https://maritime.sh/docs/cli/llms.txt. It teaches an LLM to operate Maritime (https://maritime.sh): every computer an agent needs (sandboxes, Linux and Windows desktops, Android phones). Agents deployed here run in persistent micro-VMs with storage at /data; they sleep when idle and wake in about a second on the next message or trigger. Billing: plans from $20 a month, and the plan fee becomes usage credit; machines are billed per second at hourly rates, with a 7-day free trial (card needed, usage capped at $20). Details: https://maritime.sh/pricing The CLI describes itself offline: `maritime guide` prints its usage guide, and `maritime guide --json` emits the full command and flag manifest, introspected live from the installed CLI. Prefer that manifest, and `maritime --help`, over guessing flags. `--dry-run` on any command prints the HTTP request without sending it. ## Setup 1. Install: curl -fsSL https://maritime.sh/install.sh | sh (no Node needed; it checks the download's sha512), or npm install -g maritime-cli (the npm package is maritime-cli; the command is maritime; needs Node 18+. If a flag below is unknown, run npm install -g maritime-cli@latest) 2. Authenticate, first path that works wins: - MARITIME_TOKEN in the environment (an mk_ key or a login token); the CLI reads it before anything else. - Any account, browser or not: maritime login --json prints one JSON line on stderr with user_code and verification_uri_complete; show both to the user and wait for their approval in any signed-in browser. - Email/password account, no browser: maritime login --email --password - No account, user present: maritime signup -e -p -n --json (ask the user for the email and password; never invent them) - No account, do not stop: add --claim-later to maritime create. No login needed; the agent runs under a temporary account for 72 hours. The JSON response carries data.claimUrl (the user opens it to keep the agent) and data.token (a temporary key for that agent; export it as MARITIME_TOKEN to operate it). One agent only; template agents must bring their own model key (-e ANTHROPIC_API_KEY=...). 3. Verify: maritime whoami --json prints {authenticated, source, method, expires_at} and exits 4 when no credential is set or a login token expired. It does not call the API, so a wrong mk_ key still reports authenticated; maritime doctor checks it. 4. The email verification wall: until the user clicks the link Maritime emailed them, create, deploy, start, restart, keys create, and computer creation are refused with HTTP 403 and email_not_verified in the error (exit 4). Ask the user to click the link. To get it again they sign in at https://maritime.sh/login, which offers to resend it. Then retry the same command. List, status, logs, and templates work before verification. 5. Long-running automation: once verified, mint a key that never expires: maritime keys create --name my-agent --json (data.raw_key is shown once), and export MARITIME_TOKEN=mk_... Full setup procedure, step by step: https://maritime.sh/agents.md MARITIME_API_URL overrides the API base (default https://api.maritime.sh). ## The contract - Pass --json on EVERY command. Results go to stdout: reads print {"meta": {...}, "results": ...}; writes print {"action", "resource", "path", "status", "success", "data"}; chat, status and whoami print their object directly. Errors print "Error: ..." on stderr; API errors include the HTTP status and the API's response body. A 409 also prints {"error": "...", "code": 5} on stdout. Branch on the exit code, never scrape human text. - The global --agent flag sets --json --compact --no-input --no-color --yes. --compact keeps only key fields; leave it off when you need the full body. - Exit codes: 0 success, 1 other error, 2 usage (unknown flag, missing argument, missing --yes), 3 not found (HTTP 404), 4 auth (no credential, expired, HTTP 401/403), 5 API error (other HTTP errors such as 402, 409, 422, 5xx), 6 partial failure, 7 rate limited (HTTP 429), 10 config. - The top-level agent commands (chat, logs, status, env, delete and the rest) take the exact agent name or an ID prefix. Inside a directory linked with maritime init/link, the agent argument can be omitted. Commands whose help shows (exec, files, agents ...) need the full ID. - In a linked directory, chat treats every argument as the message. To reach another agent from there, use maritime chat -a "...". ## Core tasks # Discover templates, create an agent, wait for it, talk to it: maritime templates --json maritime create my-agent --template openclaw --json maritime status my-agent --json # wait for "active" maritime chat my-agent "introduce yourself" --json | jq -r '.response' # No account yet and nobody to ask: deploy now, claim later (72 h, 1 agent): maritime create my-bot --repo https://github.com/you/agent --claim-later --json # Deploy your own code (any framework; Dockerfile at the repo root): maritime create my-bot --json maritime deploy my-bot --source github --repo https://github.com/you/agent --branch main --wait --json # Serve a repo on a public no-login URL (sleeps idle, wakes on the next visit): maritime create my-app --repo https://github.com/you/app --public --port 3000 --json # Encrypted env vars (secret by default; --reload pushes into the running agent): maritime env set my-agent OPENAI_API_KEY=sk-... --reload --json maritime env import my-agent ./prod.env --reload --json maritime env list my-agent --json # Inspect and debug: maritime list --json maritime logs my-agent --level error --json maritime info my-agent --json maritime history my-agent --json # build/deploy attempts + buildLog maritime exec --body-json '{"command":"ls /data"}' --json | jq -r '.results.stdout' # Message an agent AS one of your end users (sticky per-user routing through # the project front door; same --user always reaches the same instance): maritime message my-agent --user cust_42 "where is my order?" --json | jq -r '.data.reply' # Your end users (the people your product serves), each with their own agent: # (end-users, alias users; bodies go in --body-json, fields as in the REST API) maritime users upsert user_42 --display-name "Ada" --tags pro --json # safe on every sign-in maritime users create user_42 --body-json '{"templateId":"openclaw"}' --json # their agent, exactly once maritime users set-policy user_42 --body-json '{"policy":{"llm_spend_cap_cents_per_month":500}}' --json maritime users policy user_42 --json # read; clear-policy removes it maritime users token user_42 --body-json '{"scopes":["chat"],"ttlSeconds":900}' --json # eu_ token maritime users list --json · get · agents · suspend · resume · delete maritime usage get --from 2026-09-01 --to 2026-10-01 --json # per agent, each row has externalUserId maritime usage daily --json # per day # Lifecycle (rarely needed; agents sleep and wake on their own): maritime start my-agent --json maritime sleep my-agent --json maritime restart my-agent --json # also picks up new env vars maritime delete my-agent --yes --json # --yes is REQUIRED non-interactively ## Scheduling Timers inside a sleeping agent do not fire. Put scheduled work on platform triggers: maritime triggers list , and maritime triggers create --type cron --cron "0 9 * * *" --prompt "...". A cron trigger wakes the agent and delivers --prompt as a message on each fire. A custom container can instead serve GET /schedules and Maritime registers the wakes for it. Details: https://maritime.sh/docs/triggers maritime exec takes the full agent ID and a required --body-json: {"command": "..." or ["argv", ...], "detached": false, "timeout": 60}. Words after the agent ID are not sent. timeout defaults to 60 seconds attached or 3,600 seconds detached. A detached call returns executionId at once; fetch or cancel it through the REST API: https://maritime.sh/docs/api#exec On agents with detached support, disconnecting does not cancel execution. Do not repeat submission after a lost response without accounting for a command that may already be running. Older agents support attached exec with combined output and no execution ID. Detached submission on those agents returns HTTP 501 with unsupported: outdated vm. Please use a new agent. Use a new agent for detached execution. Restarting an older agent does not add this feature. ## Gotchas - create and delete never hang without a terminal: a missing name, or delete without --yes, exits 2. - Env changes apply on the next boot unless you pass --reload (or run maritime env reload ). - If chat fails with agent_unavailable in the error, the agent is not running: maritime start , wait a few seconds, retry. - A 404 or "not running" on file/exec operations usually means the agent is asleep or errored: start it and retry; do not recreate it. - mk_ keys never expire unless created with --expires-in-days; login tokens do. Use mk_ for automation. - LLM access: most templates work with no model key (Maritime's model proxy, charged at the provider's list price, no markup, from your AI credits). The claude_code template is the exception: it always needs your own ANTHROPIC_API_KEY as a secret env var. ## Something wrong with these docs? If a command, flag or example here does not match what the CLI does, or a call fails and the error does not tell you what to do, tell Maritime. Any mk_ key works. Put the X-Request-Id of the failing call in requestId when there is one and name yourself in tool. Never include keys, tokens or env values. curl -X POST https://api.maritime.sh/api/v1/feedback \ -H "Authorization: Bearer $MARITIME_API_KEY" \ -H "Content-Type: application/json" \ -d '{"kind":"cli","subject":"maritime env set","message":"","tool":"claude-code/2.1.0"}' kind is one of docs, sdk, api, cli, computers, dashboard, other. Capped at 5 reports a minute and 20 a day per account. Reference: https://maritime.sh/docs/sdk/feedback ## More - Human-readable CLI reference: https://maritime.sh/docs/cli - Drive-Maritime-from-an-AI-agent guide: https://maritime.sh/docs/ai-agents - REST API: https://maritime.sh/docs/api - Building a product where every customer gets an agent? Use the SDK from your backend instead: https://maritime.sh/docs/build