agents.md
Maritime: setup procedure for AI agents
Maritime is a cloud platform that gives every AI agent its own computer: Agent boxes, Sandboxes (with Swarms) and Computers on demand (Linux and Windows desktops and Android phones). An agent you deploy here, from a template, a GitHub repo or a Docker image, runs in an Agent box: a real Firecracker micro-VM with a disk at /data that survives sleep, restart, and redeploy. It sleeps when idle and wakes in about a second. It costs $1 a month per 2 GB of memory, awake or asleep.
Billing, so you can tell the user what this costs: Plans are Starter $20, Growth $100, Scale $500 and Dedicated $5,000 a month, plus larger contracts with invoicing by a call. The plan fee becomes the same amount of usage credit every month, at the same rates on every plan. Plans differ by how many machines can be awake at once (5, 25, 60 and 500), and on Dedicated the Maritime team also sets up your apps, images and network with you. Machines are billed per second. An agent box, the VM an agent runs in, is $1 a month per 2 GB of memory (4 GB is $2, 8 GB is $4), awake or asleep, with unlimited awake hours and 5 GB of disk included; more disk is $0.08 per GB-month. The others are billed at hourly rates while awake: a sandbox or one sandbox of a swarm is $0.0055 per vCPU-hour plus $0.00275 per GB-hour (the default small size, 2 vCPU and 4 GB, is $0.022 an hour), a Linux desktop $0.12 an hour, a Windows desktop $0.18 and an Android phone $0.15. Asleep, these pay only for their disk, $0.08 per GB-month. Usage past the credit is billed at the same rates on the same invoice. New accounts start with a 7-day free trial: a card is needed to start the first machine and is not charged then, and usage stops at $20. Cancel within the trial and nothing is charged; otherwise $20 is charged on day 7 and becomes that month's credit. Plans include no AI credits.
Step 1: identify yourself and your environment
Note which of these you are, it changes step 3:
- Claude Code, Codex CLI, Cursor, Gemini CLI, OpenCode, Aider: you run in the user's terminal, usually with no browser you can open. Prefer the device login.
- An agent inside a sandbox with no outbound browser at all: same as above.
- A desktop app with a browser: the browser login also works.
Check the environment:
node --version # need 18 or later
maritime --version # absent means install below
Step 2: install the CLI
curl -fsSL https://maritime.sh/install.sh | sh # no Node needed; checks the download's sha512
# or: npm install -g maritime-cli # the command is `maritime`
maritime --version
If a flag in this procedure is unknown to the installed CLI, run npm install -g maritime-cli@latest and retry once.
Step 3: onboard (sign in, API key, payment) in one command
maritime onboard --json
It runs only the steps the account still needs, and is safe to run again. It prints one JSON object. Branch on step:
sign_in: give the personsign_in.urland say the page shows the codesign_in.codefor them to confirm. They sign in with Google, GitHub or email. When the account has no card yet, the same page continues straight to Stripe Checkout, so the person follows one link. Then runnext_command(maritime onboard --wait --json).checkout: the account needs a payment method. Two ways, pick one:- A person adds a card: give them
checkout_url(Stripe Checkout). The first 7 days are free, nothing is charged today. - You pay yourself, when
agent_payis present and the person has a Stripe Link wallet: runagent_pay.pay_command(npx @stripe/link-cli mpp pay <url> -X POST). The server answers HTTP 402 with a Stripe Challenge, link-cli asks the person to approve one payment in the Link app, and pays with a Shared Payment Token. The plan starts at once (no free week). No card form. Then runnext_command.
- A person adds a card: give them
billing_action: a failed payment or a used-up free week. Give the personbilling_url.done: ready.api_key(shown once) is a long-livedmk_key, already saved for the CLI; export it as MARITIME_TOKEN for SDK or HTTP use. Go to step 5.
Step 4: the email verification wall
Until the user clicks the link Maritime emailed them, these are refused with HTTP 403 and email_not_verified in the error (exit 4): create, deploy, start, restart, keys create, computer creation. Listing, status, logs, and templates work before verification.
When you see email_not_verified: tell the user to open their inbox and click the link; if it did not arrive, they sign in at https://maritime.sh/login, which offers to resend it. Wait for them to say it is done, then retry the same command. Do not create a second account.
Step 5: learn the contract
maritime guide --json # every command and flag, from the installed CLI
Rules that apply to every command: pass --json; results are JSON on stdout; errors print Error: ... on stderr with the HTTP status and the API's response body; exit codes are 0 ok, 1 other error, 2 usage, 3 not found, 4 auth, 5 API error, 6 partial failure, 7 rate limited, 10 config.
Never guess flags. Full contract: https://maritime.sh/docs/cli/llms.txt
Step 6: deploy
Choose exactly one recipe.
A. The user wants a ready-made agent (no code of their own):
maritime templates --json # ids: openclaw, hermes, zeroclaw, claude_code, codex, dsh
maritime create <name> --template openclaw --json
B. The user has a repo with a Dockerfile at its root (any framework):
maritime create <name> --repo <git url> --json # builds on Maritime, 1 to 3 minutes
The container must listen on 0.0.0.0:$PORT (injected, never hardcode 8080), answer GET /health with any 2xx, answer POST /chat with JSON {"message": "..."} within 30 seconds, and keep state under /data. If the repo does not meet this, add the smallest server that does before deploying. Contract and examples: https://maritime.sh/docs/frameworks/custom.md
C. The user wants a public web app on its own HTTPS URL:
maritime create <name> --repo <git url> --public --port <port> --json
D. The user's product should give every end user their own agent: stop, and point them at the SDK instead of deploying by hand: https://maritime.sh/docs/build.md
Secrets the agent needs go in after create, never into the repo:
maritime env set <name> KEY=value --reload --json
Scheduled work: timers inside a sleeping VM do not fire. Use maritime triggers create <name> --type cron --cron "<expr>" --json.
Step 7: verify
maritime status <name> --json # poll until .status is "active"
maritime chat <name> "hello" --json | jq -r '.response' # templates and chat-capable containers
maritime logs <name> --json # custom containers: the server is listening
A build failure shows in maritime history <name> --json (buildLog). Fix the Dockerfile and run maritime deploy <name> --json. Do not create a second agent to work around a failed build.
Step 8: leave the repo ready for next time
If you deployed from a repo and .claude/skills/maritime/SKILL.md does not exist, run maritime init --no-create there (it writes the skill and a maritime.json), or fetch https://maritime.sh/SKILL.md into that path. The next agent session in that repo then knows Maritime without reading this page.
Step 9: report
End with exactly this shape, filled in with what actually happened:
- Status: deployed and verified | deployed, not verified | blocked (why)
- Agent:
, , dashboard https://maritime.sh/agents - Claim link, if you used --claim-later: the URL and the expiry time
- Auth: which path from step 3 was used; whether verification is pending
- Changes made: files added or edited in the repo, env vars set (names only)
- Verification: the status value and the chat reply or log line you saw
- Next action for the user: one line, or "none"
Do not finish with a list of steps that were not attempted. Execute the procedure first, then report the actual result.
Known behaviours
maritime login(no flags) prints a code and a URL and waits for the user to approve it in any browser. It does not need a browser on this machine.- Without a terminal,
createwith no name anddeletewithout--yesexit 2 instead of prompting. - Flag-parse errors (unknown option, missing argument) exit 2 with plain text on stderr. Check
maritime <command> --help. agent_unavailablein a chat error: the agent is not running. Runmaritime start <name>, wait a few seconds, retry.- A create refused for billing exits 5 with HTTP 402 in the error (exit 7 for HTTP 429 when too many machines are awake); the API body on stderr carries a code such as
trial_required(add a card to start the free trial),trial_cap_reached,plan_required,payment_required,concurrencyorseat_limit. Show the API's message to the user unchanged; it names the fix. - Most templates need no model key: they call models through Maritime's model proxy, charged at the provider's list price from the account's model budget.
claude_codeneeds ANTHROPIC_API_KEY as a secret env var. - The
--claim-latertemporary key cannot create a second agent (seat_limit) and expires with the claim link. After the claim, the user mints a normal key. - Long-running automation should use an API key, not a login token:
maritime keys create --name <purpose> --json(after verification) and exportdata.raw_keyas MARITIME_TOKEN. mk_ keys never expire unless created with--expires-in-days.