> ## Documentation index
> Fetch the complete index at: https://maritime.sh/llms.txt
> Setup procedure for AI agents (install, sign in, deploy, verify): https://maritime.sh/agents.md
> This page as HTML: https://maritime.sh/docs/cli

# CLI Reference

The Maritime CLI (`maritime`, from the npm package `maritime-cli`) is the command-line tool for deploying
and managing agents, sandboxes, swarms and computers from your terminal. First time here? The
[quickstart](https://maritime.sh/docs/quickstart) walks the happy path; this page is the full
reference.

> **Note:** **Driving this CLI from an LLM?** There's a ready-made instructions file at
> [maritime.sh/docs/cli/llms.txt](https://maritime.sh/docs/cli/llms.txt). Tell your agent to read
> it (in chat, or one line in CLAUDE.md, AGENTS.md or a system prompt) and it knows the auth flow,
> the JSON contract, and the gotchas. Guide:
> [Drive Maritime from an AI agent](https://maritime.sh/docs/ai-agents).
>
> Copy button: "Copy the CLI instructions" copies that one line, which links
> https://maritime.sh/docs/cli/llms.txt

## Installation

```
npm install -g maritime-cli
```

Requires Node.js 18 or later. The package installs a native binary for macOS, Linux or Windows
(x64 and arm64).

## Authentication

### maritime login

With no flags the CLI prints a short code and a URL. Approve the code in any signed-in browser
(Google and GitHub accounts work too) and the CLI saves the issued token. This works on headless
servers and over SSH. Email and password accounts can sign in without a browser. For CI, skip
login and set `MARITIME_TOKEN`.

```
maritime login                       # code + URL, approve in a browser
maritime login --json                # code and URL as one JSON line on stderr

# Email/password accounts, no browser:
maritime login --email user@example.com --password '...'

# CI: no stored login, the CLI reads the env var first
export MARITIME_TOKEN=mk_xxxxxxxxxxxx
```

| Flag | Description |
| --- | --- |
| `--email, --password` | Sign in with email and password, no browser |
| `--device` | Use the device-code approval (the default flow) |
| `--native` | Use Maritime's own login even when the API offers Okibi Identity |
| `--headless` | Use a non-browser login path |

A new account must click the link in its verification email before it can create, deploy or mint
keys. To get the email again, sign in at [maritime.sh/login](https://maritime.sh/login); the page
offers to resend it.

### maritime signup

Create a Maritime account with email and password and save its session. `-e` and `-p` are
required; the CLI does not prompt.

```
maritime signup -e user@example.com -p 'a long password' -n "Jane Doe"
```

| Flag | Description |
| --- | --- |
| `-e, --email` | Email address (required) |
| `-p, --password` | Password (required) |
| `-n, --name` | Display name (optional) |

### maritime logout

Log out and clear stored credentials.

```
maritime logout
```

### maritime whoami

Show the current credential: its source (`MARITIME_TOKEN` or a saved login), the user, and the
expiry. With `--json` it prints `{ authenticated, source, method, expires_at }` (plus `email`
when known). It exits 4 when no credential is set or a login token has expired. It reads the
credential locally and does not call the API, so a revoked `mk_` key still shows as
authenticated; `maritime doctor` checks auth against the API.

```
maritime whoami --json
maritime doctor
```

### maritime keys

Mint a long-lived API key (`mk_…`) for non-interactive use. Set it as `MARITIME_TOKEN` so an AI
agent, script, or CI never runs an interactive login. Keys never expire unless you pass
`--expires-in-days`; revoke anytime.

```
# Mint a key (shown once, store it securely):
maritime keys create --name my-agent --json

# Default is FULL access (provision,deploy,secrets,manage). Narrow it for
# anything you hand to an agent or CI ("manage" can mint further keys):
maritime keys create --name ci-deploy --scopes deploy

# Expire after 30 days, or restrict to one project:
maritime keys create --name temp --expires-in-days 30
maritime keys create --name tenant --project-id <project_id>

# Use it everywhere:
export MARITIME_TOKEN=mk_xxxxxxxxxxxx

# List / revoke:
maritime keys list
maritime keys revoke <key_id>

# A key that only reaches one sandbox (default lifetime 1 day):
maritime keys create --sandbox <sandbox_id> --ttl-seconds 3600
```

*Recording: Mints a key scoped to deploys, lists your keys, exports the new key as MARITIME_TOKEN,
and confirms the session with maritime whoami. No browser login involved. The key shown on screen
was revoked right after recording.*

## Projects: a directory is an agent

Like `vercel` or `railway`, you can link a directory to an agent. After that, the agent argument
is **optional on every command**; the CLI resolves the linked agent by walking up from the current
folder. Two files: `maritime.json` (committed config: name, template, persona, env keys) and
`.maritime/` (the machine-local link, git-ignored).

```
maritime init support-bot     # scaffold maritime.json + AGENTS.md, create & link
maritime chat "what's open?"  # talks to support-bot, no name needed
maritime logs                 # support-bot's logs
maritime deploy               # reconcile the agent to maritime.json, then redeploy
```

*Recording: maritime init scaffolds maritime.json and AGENTS.md, creates the agent, and links the
directory. maritime chat then works without naming the agent, and maritime deploy pushes the
edited persona to the running agent.*

### maritime init

Scaffold `maritime.json` + `AGENTS.md` (and the Claude Code skill for this CLI), create the agent,
and link this directory to it. Defaults the template to `openclaw` and the name to the folder.

```
maritime init                 # name from the folder
maritime init support-bot     # explicit name
maritime init support-bot --instructions "Answer support tickets"
maritime init bot --framework custom      # your own code, no template
maritime init bot --no-create # just write the config files
```

### maritime link

Link this directory to an existing agent (interactive picker if no name is given). Re-run to
change it.

```
maritime link support-bot
maritime link                 # pick from your agents
```

## Agent Management

### maritime create

Create a new agent. Just give it a name: the platform picks an applicable size for the framework,
and the agent auto-sleeps when idle and wakes on the next message. No sizing required.

```
maritime create my-agent

# From a template (see maritime templates):
maritime create researcher --template openclaw

# With initial environment variables:
maritime create my-agent -e OPENAI_API_KEY=sk-... -e MY_VAR=hello

# From a GitHub repo (must contain a Dockerfile), served on a public URL:
maritime create my-app --repo https://github.com/you/app --public --port 3000
```

Serving a repo on a public URL is covered in [Public web agents](https://maritime.sh/docs/web-apps).

| Argument / flag | Description |
| --- | --- |
| `<name>` | Agent name (positional) |
| `-t, --template` | Template to base the agent on (see `maritime templates`) |
| `-e, --env` | Environment variables as KEY=value (repeatable) |
| `-r, --repo` | Deploy from a GitHub repo (must contain a Dockerfile) |
| `-b, --branch` | Git branch to deploy (with `--repo`) |
| `--public` | Serve a public, no-login web URL for this agent (web apps) |
| `--always-on` | Never auto-sleep. Older seat plans only: the current plans run machines on demand and refuse it with `always_on_not_sold` |
| `--port` | Port your app listens on (exposed publicly; default 8080) |
| `--count` | Create N identical agents named <name>-1 to <name>-N |
| `--claim-later` | No account needed: create one agent under a temporary account for 72 hours. The response carries a `claimUrl` the user opens to keep it, and a temporary `token` for that agent |

**Advanced: per-agent resource overrides.** All optional; omit them and the agent gets the
platform defaults. Useful for hand-tuning a single agent (e.g. a reseller dialing resources per
client).

```
maritime create acme-bot --ram 2048 --cpu 1.5 --idle 600 --disk 20
```

| Flag | Description |
| --- | --- |
| `--ram` | RAM in MB (per-agent override of the default) |
| `--cpu` | vCPU cores, e.g. 0.5 or 2 (per-agent override) |
| `--idle` | Auto-sleep N seconds after the last request |
| `--disk` | Workspace SSD in GB (clamped to your plan cap) |
| `--framework` | Framework when not using a template (default: custom) |

### Network settings

What the agent may reach on the internet, where its traffic exits, and which of your private systems
it may use. The same flags work on `maritime agents create`, `maritime agents update`, and
`maritime computers create`. Read what is in force with `maritime agents network <agent_id>` and
try a destination from inside with `maritime agents network test <agent_id> <host:port>`. Presets
live under `maritime network presets`, and `maritime connect` makes this machine a private
network your agents can reach. Full reference: [Networks](https://maritime.sh/docs/networks).

```
maritime agents create --name worker --template-id codex \
  --internet list --network-policy coding --allow-out api.example.com
maritime agents update <agent_id> --egress fixed --access db.office.private
maritime agents network test <agent_id> api.example.com:443

maritime network presets list
maritime network presets create --name internal --allow '*.corp.example.com' --deny 169.254.0.0/16
maritime connect --allow localhost:5432      # runs until Ctrl+C
```

| Flag | Description |
| --- | --- |
| `--internet` | all, none, or list. `--allow-out` implies list |
| `--allow-out` | One allow entry, repeatable: domain, `*.domain`, IPv4 address, or CIDR; none clears |
| `--deny-out` | One deny entry (IPv4 address or CIDR), repeatable; deny wins over allow; none clears |
| `--network-policy` | A preset by name or id (coding, llm-apis, or your own); none clears |
| `--egress` | shared, fixed, country:<CC>, or through:<network> |
| `--access` | One private resource by address or id, repeatable; none clears |

### maritime list

List your agents: a table on a terminal, JSON when piped or with `--json`. Alias: `maritime ls`

```
maritime list
maritime list --json | jq -r '.results[].name'
```

### maritime deploy

Deploy an agent. Accepts an agent name or ID prefix.

```
maritime deploy my-agent

# Deploy from a GitHub repo:
maritime deploy my-agent --source github --repo https://github.com/user/repo

# Deploy a specific Docker image:
maritime deploy my-agent --source docker --image myimage:latest
```

| Flag | Description |
| --- | --- |
| `--source` | Deploy source: template, github, or docker |
| `--repo` | GitHub repository URL |
| `--image` | Docker image name |
| `--branch` | Git branch (for GitHub deploys) |
| `-w, --wait` | Block until the deployment completes (useful in scripts and CI) |

### maritime start / stop / restart

Manual lifecycle control, **rarely needed**. Serverless agents auto-sleep when idle and auto-wake
on the next message or trigger; reach for these for ops or debugging.

```
maritime start my-agent
maritime stop my-agent
maritime restart my-agent
```

### maritime delete

Delete an agent. On a terminal it asks you to type the agent name; without a terminal it refuses
(exit 2) unless `--yes` is passed.

```
maritime delete my-agent

# Skip confirmation:
maritime delete my-agent --yes
```

## Talking to agents

### maritime chat

Send a message to a running agent and print its reply. Reads the message from arguments or stdin
(handy for long prompts). Sleeping serverless agents auto-wake. Alias: `maritime send`.

```
maritime chat my-agent "summarize today's incidents"

# Long prompt via stdin:
cat prompt.txt | maritime chat my-agent

# Multi-turn with a conversation id:
maritime chat my-agent "and the one before?" --conversation-id abc123

# Machine-readable (returns { agent_id, agent_name, conversation_id, response }):
maritime chat my-agent "hi" --json

# Inside a linked directory every argument is the message, so name another agent with -a:
maritime chat -a other-agent "hi"
```

### maritime message

Send a message through an agent's project front door as one of your end users. The same `--user`
always reaches the same instance. `--wait` is 0 to 60 seconds (default 30).

```
maritime message my-agent --user cust_42 "where is my order?" --json | jq -r '.data.reply'
```

## Your end users

An end user is one of **your** users, keyed by the id you already hold for them. Their agents, spend
caps, delegated tokens and costs hang off that record; the [Users page](https://maritime.sh/users)
shows the same list and the [SDK guide](https://maritime.sh/docs/sdk/end-users) covers the model. The
CLI is for support work and scripts.

### maritime end-users

Alias: `maritime users`. Commands that take a body read it from `--body-json` (or `--stdin`); the
fields are the ones in the [REST API](https://maritime.sh/docs/api#end-users).

```
# Create or update one (safe to run on every sign-in):
maritime users upsert user_42 --display-name "Ada" --tags pro

# Their agent, exactly once (a repeat call returns the same agent):
maritime users create user_42 --body-json '{"templateId":"openclaw"}'
maritime users agents user_42

# Caps and sizing, applied to every agent they own (set-policy replaces the whole policy):
maritime users set-policy user_42 --body-json '{"policy":{"llm_spend_cap_cents_per_month":500,"wakes_per_hour":60}}'
maritime users policy user_42
maritime users clear-policy user_42

# A short-lived eu_ token for their own browser or app (shown once):
maritime users token user_42 --body-json '{"scopes":["chat","files"],"ttlSeconds":900}' --json
maritime users revoke-token user_42

# Find, inspect, suspend, resume, delete:
maritime users list --limit 50
maritime users get user_42
maritime users suspend user_42
maritime users resume user_42
maritime users delete user_42            # agents and tokens go with it
```

Policy fields: `llm_spend_cap_cents_per_month`, `compute_minutes_cap`, `wakes_per_hour`,
`idle_sleep_seconds`, `mem_mb`, `vcpus`, `disk_gb`. Token scopes: `chat`, `files`, `console`,
`logs`, `app`; the TTL is 30 to 3600 seconds (default 900).

## Observability

*Recording: The agent gets a task over maritime chat, maritime logs prints its timestamped
lifecycle entries, and maritime info shows the full record: status, framework, image, env keys,
and server placement.*

### maritime logs

View logs from an agent.

```
maritime logs my-agent

# Show last 100 lines:
maritime logs my-agent -n 100

# Filter by level:
maritime logs my-agent --level error

# Stream logs live:
maritime logs my-agent -f
```

| Flag | Description |
| --- | --- |
| `-n, --lines` | Number of log lines to show (default: 50) |
| `--level` | Filter by level: info, warn, error, or debug |
| `-f, --follow` | Follow log output (stream new lines as they arrive) |

### maritime env

Manage environment variables for an agent.

```
# List variables:
maritime env list my-agent

# Set one or more (marked secret + encrypted by default):
maritime env set my-agent OPENAI_API_KEY=sk-... ANOTHER=value

# Bulk import from a .env file (or stdin), then hot-reload:
maritime env import my-agent ./prod.env --reload

# Set a non-secret variable:
maritime env set my-agent LOG_LEVEL=debug --no-secret

# Remove a variable:
maritime env remove my-agent LOG_LEVEL

# Apply pending changes to a running agent without a full restart:
maritime env reload my-agent

# Pull the agent's env into a local .env file (secret values are masked):
maritime env pull my-agent .env
```

Env changes apply on the next container boot unless you pass `--reload` (or run
`maritime env reload`). Secrets behavior is covered in
[Env vars & sizing](https://maritime.sh/docs/configuration#environment-variables).

### maritime status

Print one agent's record from the API, including its status.

```
maritime status my-agent
```

### maritime usage

What your agents cost over a range: `usage get` per agent, `usage daily` per day. `--from` and
`--to` take ISO dates (default: the last 30 days). Every agent row carries its `externalUserId`;
group on it for a per-user bill. Under a seat plan the hosting number is your plan price allocated
over your agents day by day; AI is the real spend from your credits.

```
maritime usage get --json                       # last 30 days, per agent
maritime usage daily --from 2026-10-01 --to 2026-10-08
maritime usage get --from 2026-09-01 --to 2026-10-01 --json \
  | jq '.results.agents | group_by(.externalUserId) | map({user: .[0].externalUserId, cents: (map(.totalCostCents) | add)})'
```

### maritime info

Show an agent with its env var count, triggers, and last deployment.

```
maritime info my-agent
```

### maritime history

Show deployment history for an agent: every build/deploy attempt with its outcome.

```
maritime history my-agent

# Show last 30 deployments:
maritime history my-agent -n 30
```

### maritime triggers

Manage the cron/webhook/telegram/discord triggers wired to an agent. Subcommands, not a bare
argument:

```
maritime triggers list my-agent

maritime triggers create my-agent --type cron --cron "0 9 * * *" --prompt "post the daily digest"
maritime triggers create my-agent --type webhook

maritime triggers delete my-agent <trigger-id>
```

A cron trigger wakes the agent on schedule and delivers `--prompt` as a message on each fire, or
the agent can publish its own schedule (see [Triggers & schedules](https://maritime.sh/docs/triggers)).

## Lifecycle

### maritime sleep

Put an active agent to sleep. Sleeping agents keep their full state, still count against your
plan, and auto-wake on triggers. (Wake timings and the full model are in
[How Maritime works](https://maritime.sh/docs/how-it-works#sleep-and-wake).)

```
maritime sleep my-agent
```

### maritime open

Open the agent's dashboard page in your default browser.

```
maritime open my-agent
```

## Discovery

### maritime templates

List available agent templates. This command does not require authentication.

```
maritime templates
```

### maritime guide

Print the machine-readable usage guide for driving the CLI from code or an AI agent. `--json`
emits a one-shot manifest of every command, flag, and the contract, introspected live so it can't
drift.

```
maritime guide          # human-readable contract
maritime guide --json   # full command/flag manifest
```

## maritime exec

Runs a non-interactive command in an agent. It takes the full agent ID (no name lookup) and a
required `--body-json` with the [REST exec](https://maritime.sh/docs/api#exec) body: `command` (a
string or an array of strings), and optionally `detached` and `timeout` in seconds (default 60
attached, 3,600 detached). Words after the agent ID are not sent.

```bash
maritime exec <agent_id> --body-json '{"command":"ls /data"}' --json | jq -r '.results.stdout'
maritime exec <agent_id> --body-json '{"command":"npm run build","detached":true,"timeout":600}'
```

The result has `executionId`, `status`, `exitCode`, `stdout`, and `stderr`. A detached call
returns the ID at once; fetch or cancel the run through the REST API. Older agents return combined
output in stdout and omit executionId and status.

## More commands

These take IDs, not names. Run any of them with `--help` for every flag, and `--dry-run` to print
the request without sending it.

```
# Agents by ID
maritime agents update <agent_id> --instructions "..." --name new-name
maritime agents url visibility <agent_id> public
maritime agents url share <agent_id> --ttl-seconds 86400 --end-user customer_42
maritime agents upload-source ./my-harness            # then agents create --upload-key
maritime agents create --name my-harness --from-dir ./my-harness

# Files in an agent's volume
maritime files list <agent_id> --path /data
maritime files upload <agent_id> ./report.pdf --dest-dir /data/reports
maritime files download <agent_id> --path /data/out.csv
maritime files write <agent_id> --path /data/notes.md --content "hello"

# Computers (os: linux, windows or android; arch: x86_64 or arm64 for android; screen: Linux only)
maritime computers create --os windows
maritime computers create --os android --arch arm64
maritime computers create --screen 1024x1024
maritime computers list
maritime computers get <computer_id>
maritime computers delete <computer_id>

# Sandboxes: a sandbox (sbx-...) that ends when idle (--idle, default 5m) or at its time limit (--time-limit, default 24h)
maritime sandboxes create --name job-42 --idle 10m --time-limit 30m --size small
maritime sandbox exec <sandbox_id> -- uname -a
maritime sandbox usage <sandbox_id>
maritime sandboxes extend <sandbox_id> --time-limit 1h
maritime sandboxes kill <sandbox_id>

# The sandbox group: one-shot runs, shells, files, snapshots
maritime sandbox run -- python3 -c 'print(2+2)'      # fresh sandbox, run, print cost, kill
maritime sandbox shell <sandbox_id>                  # interactive; a paused sandbox resumes first
maritime sandbox cp ./data.csv <sandbox_id>:/home/user/data.csv
maritime sandbox url <sandbox_id> 3000               # private URL with a token unless --public
maritime sandbox pause <sandbox_id>
maritime sandbox save <sandbox_id> my-snapshot       # disk and memory, start new sandboxes --from it
maritime sandbox snapshots list

# Swarms: one command on many sandboxes
maritime swarms run --count 10 --command "python eval.py" --wait
maritime swarms run --inputs inputs.jsonl --command "python eval.py" --size 2x4 --timeout 10m
maritime swarms list
maritime swarms get <swarm_id>
maritime swarms cancel <swarm_id>
maritime swarms results <swarm_id> -o results.jsonl

# Outbound webhooks, projects, deployments
maritime webhooks create --body-json '{"url":"https://example.com/hook","events":[]}'
maritime projects list
maritime projects instances <project_id>
maritime deployments for-agent <agent_id>
maritime deployments create --agent-id <agent_id> --repo https://github.com/acme/agent --branch main

# Plan and the free week
maritime billing status                              # plan, trial, and whether machines can start
maritime billing checkout                            # Stripe Checkout link that starts the free week
maritime onboard                                     # sign in, mint a key, start the free week

# Health and tools
maritime env pull my-agent .env -f                   # -f overwrites an existing file
maritime doctor                                      # auth and connectivity
maritime mcp                                         # serve these commands as MCP tools over stdio
```

## Scripting & AI agents

Handing the CLI to Claude, Cursor, or another assistant? There's a ready-made prompt and a
walkthrough on [Drive Maritime from an AI agent](https://maritime.sh/docs/ai-agents); the tables
below are the underlying contract.

The CLI is built to be driven non-interactively. Authenticate with the `MARITIME_TOKEN` env var
(an `mk_…` key from `maritime keys create`), pass `--json` on every command, and branch on the
exit code.

With `--json`, 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**, with the HTTP status and the API's
response body for API errors. A 409 also prints `{"error": "...", "code": 5}` on stdout. The global
`--agent` flag sets `--json --compact --no-input --no-color --yes` at once; `--compact` keeps only
key fields, so leave it off when you need the full response.

```
export MARITIME_TOKEN=mk_xxxxxxxxxxxx

# results on stdout, errors on stderr, branch on exit code:
maritime list --json | jq -r '.results[] | select(.status=="error") | .name'

reply=$(maritime chat my-agent "status?" --json | jq -r '.response')
```

| Exit code | Meaning |
| --- | --- |
| `0` | success |
| `1` | other error (network, unexpected) |
| `2` | usage (unknown flag, missing argument, missing --yes) |
| `3` | not found (HTTP 404, no such agent) |
| `4` | auth (no credential, expired, HTTP 401 or 403, including email_not_verified) |
| `5` | API error (other HTTP errors, such as 402, 409, 422, 5xx) |
| `6` | partial failure (the API accepted a batch but some items failed) |
| `7` | rate limited (HTTP 429, after three retries) |
| `10` | config (the config file could not be read) |

> **Note: Agent name resolution**
>
> The top-level agent commands (`chat`, `logs`, `status`, `env`, `delete` and the rest) accept
> the full agent name or an ID prefix, matched by exact name first and then by ID prefix. If you
> omit it inside a linked directory, the CLI uses the linked agent. Commands that show
> `<agent_id>` in their help (`exec`, `files`, `agents ...`) need the full ID.
