CLI Reference

The Maritime CLI lets you deploy and manage agents from your terminal. First time here? The quickstart walks the happy path; this page is the full reference.

Driving this CLI from an LLM? There's a ready-made instructions file at maritime.sh/docs/cli/llms.txt. Copy it into your agent's context (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.

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
FlagDescription
--email, --passwordSign in with email and password, no browser
--deviceUse the device-code approval (the default flow)
--nativeUse Maritime's own login even when the API offers Okibi Identity
--headlessUse 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; 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"
FlagDescription
-e, --emailEmail address (required)
-p, --passwordPassword (required)
-n, --nameDisplay 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>
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
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.

Argument / flagDescription
<name>Agent name (positional)
-t, --templateTemplate to base the agent on (see maritime templates)
-e, --envEnvironment variables as KEY=value (repeatable)
-r, --repoDeploy from a GitHub repo (must contain a Dockerfile)
-b, --branchGit branch to deploy (with --repo)
--publicServe a public, no-login web URL for this agent (web apps)
--portPort your app listens on (exposed publicly; default 8080)
--countCreate N identical agents named <name>-1 to <name>-N
--claim-laterNo 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
FlagDescription
--ramRAM in MB (per-agent override of the default)
--cpuvCPU cores, e.g. 0.5 or 2 (per-agent override)
--idleAuto-sleep N seconds after the last request
--diskWorkspace SSD in GB (clamped to your plan cap)
--frameworkFramework 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.

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
FlagDescription
--internetall, none, or list. --allow-out implies list
--allow-outOne allow entry, repeatable: domain, *.domain, IPv4 address, or CIDR; none clears
--deny-outOne deny entry (IPv4 address or CIDR), repeatable; deny wins over allow; none clears
--network-policyA preset by name or id (coding, llm-apis, or your own); none clears
--egressshared, fixed, country:<CC>, or through:<network>
--accessOne 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
FlagDescription
--sourceDeploy source: template, github, or docker
--repoGitHub repository URL
--imageDocker image name
--branchGit branch (for GitHub deploys)
-w, --waitBlock 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 shows the same list and the SDK guide 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.

# 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

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
FlagDescription
-n, --linesNumber of log lines to show (default: 50)
--levelFilter by level: info, warn, error, or debug
-f, --followFollow 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.

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).

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.)

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 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.

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)
maritime computers create --body-json '{"name":"desk-1","os":"windows"}'
maritime computers list
maritime computers get <computer_id>
maritime computers delete <computer_id>

# Ephemeral sandboxes: a sandbox (sbx-...) that ends when its lifetime ends
maritime sandboxes ephemeral create --name job-42 --lifetime 30m --size small
maritime sandbox exec <sandbox_id> -- uname -a
maritime sandboxes ephemeral extend <sandbox_id> --lifetime 1h
maritime sandboxes ephemeral kill <sandbox_id>

# Swarms: one command on many ephemeral 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

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

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.