# 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](/content/docs/quickstart/index.html) walks the happy path; this page is the full
reference.

## 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](/content/login/index.html); 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>
```

## 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
```

### 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
```

| 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](/content/docs/networks/index.html).

```
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](/content/users/index.html)
shows the same list and the [SDK guide](/content/docs/sdk/end-users/index.html) 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](/content/docs/api#end-users/index.html).

```
# 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
```

## Observability

### 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](/content/docs/configuration#environment-variables/index.html).

### 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](/content/docs/triggers/index.html)).

### 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](/content/docs/how-it-works#sleep-and-wake/index.html).

```
maritime sleep my-agent
```

### maritime open

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

```
maritime open my-agent
```

### 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](/content/docs/api#exec/index.html) 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](/content/docs/ai-agents/index.html); 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) |
