CLI Commands
nanoterm --help prints the full command tree at any time. Every
subcommand also accepts --help and ships with at least one example.
For an agent-readable JSON catalog, run nanoterm commands.
Global flags
| Flag | Description |
|---|---|
--org <id> | Override the active org for this command (otherwise uses ~/.nanoterm/config.json / NANOTERM_ORG_ID) |
--json | On error, emit a single JSONL line on stderr with kind, retryable, request_sent, attempt, status, code — designed for agents to make retry decisions |
The --json field shape is stable. request_sent: false means the
server never saw the request (safe to retry non-idempotent operations
like ws create); request_sent: true with retryable: true means
the server saw it and may have side effects — agents should check
state before retrying.
nanoterm --json ws create --image ghcr.io/momenntal/nanoterm-claude-code 2>err.jsonl
echo $? # exit code
cat err.jsonl
# {"level":"error","kind":"network","retryable":true,"request_sent":false,"upstream":"ECONNREFUSED"}
auth
Manage authentication and API keys.
nanoterm auth login
Open the browser for sign-in. When you authorize the CLI on the
/cli-auth page, a machine-scoped API key is minted and written to
~/.nanoterm/config.json (mode 0600). The CLI listens on a one-shot
localhost port for the callback and exits after the first hit or a
5-minute timeout.
nanoterm auth login
nanoterm auth set-key <key>
Save an API key for CLI usage.
nanoterm auth set-key nt_a1b2c3d4...
nanoterm auth status
Show current authentication status.
nanoterm auth status
# Authenticated
# API: https://api.nanoterm.dev
# Key: nt_a1b2c3d4e5f6...
# Config: /Users/you/.nanoterm/config.json
nanoterm auth logout
Clear stored credentials.
nanoterm auth logout
ws
Manage workspaces.
nanoterm ws create
Create a new workspace.
| Flag | Description | Default |
|---|---|---|
-i, --image <image> | Base image | ghcr.io/momenntal/nanoterm-claude-code |
-s, --size <size> | small, medium, large | small |
-n, --name <name> | Workspace name | auto-generated |
-p, --port <mapping...> | host:container to pin, or just container to auto-pick | — |
--policy <id> | Attach a policy | — |
-r, --repo <url> | Git repo to clone into /workspace | — |
-b, --branch <branch> | Branch to clone | default branch |
# Default Claude Code workspace
nanoterm ws create
# Specific image and size
nanoterm ws create --image ghcr.io/momenntal/nanoterm-codex --size medium
# Clone a repo at create time (auth via GITHUB_TOKEN secret)
nanoterm ws create --image claude-code --repo https://github.com/foo/bar.git
# Pin host ports for forwarded apps
nanoterm ws create -p 13000:3000 -p 18080:8080
When the image is nanoterm-claude-code or nanoterm-codex, the
output reminds you which secrets auth-* command to run before the
agent can talk to its provider — see secrets below.
nanoterm ws list
List all workspaces. Alias: nanoterm ws ls.
nanoterm ws list
# ID NAME STATUS IMAGE
# ws_a1b2c3d4 my-agent running ghcr.io/momenntal/nanoterm-claude-code
# ws_e5f6g7h8 test-runner stopped ubuntu:22.04
nanoterm ws ports <id>
Show the host-port mapping plus any auto-detected listeners inside the workspace.
nanoterm ws ports ws_a1b2c3d4
# Mapped (host → container URL):
# CONTAINER HOST URL
# 3000 http://localhost:13000
#
# Active listeners:
# PORT PROCESS DETECTED
# 3000 next-dev 08:01:23
#
# Forward an unmapped port: nanoterm proxy ws_a1b2c3d4 <port>
nanoterm ws stop <id> / start <id>
Stop preserves the filesystem; start resumes from it. Compute billing pauses while stopped.
nanoterm ws stop ws_a1b2c3d4
nanoterm ws start ws_a1b2c3d4
nanoterm ws rm <id>
Terminate and remove permanently.
nanoterm ws rm ws_a1b2c3d4
# ✓ Workspace removed
exec
Run a one-shot command in a workspace. Stdout/stderr stream to your terminal; the CLI exits with the command's exit code.
nanoterm exec <workspace-id> [flags] -- <command...>
| Flag | Description |
|---|---|
--cwd <path> | Working directory inside the workspace (default /workspace) |
--detach | Don't wait — return immediately. Pair with --log and nanoterm exec-wait to follow up. |
--log <path> | Log file inside the workspace for stdout+stderr (with --detach) |
Flag position is flexible — --cwd, --detach, --log work either
before OR after the workspace ID. The -- separator marks the start
of the workspace command.
# All four lines are equivalent
nanoterm exec ws_a1b2c3d4 --cwd /workspace/api -- pnpm build
nanoterm exec --cwd /workspace/api ws_a1b2c3d4 -- pnpm build
nanoterm exec ws_a1b2c3d4 -- pnpm build
nanoterm exec --cwd=/workspace/api ws_a1b2c3d4 -- pnpm build
# Multi-token shell pipeline (use bash -lc)
nanoterm exec ws_a1b2c3d4 -- bash -lc "ls -la | head"
# Detached background job — see exec-wait below
nanoterm exec ws_a1b2c3d4 --detach --log /tmp/build.log -- pnpm build
If a command exits 127 and looks like a shell one-liner (pipes,
redirects, &&, etc.), the CLI hints at nanoterm shell — most
likely you wanted the command run through a shell rather than as
its own argv.
exec-wait
Wait for a detached exec --detach (or shell --detach) job to
finish, then exit with the job's exit code. Polls a sidecar
<log>.exit file written by the detach wrapper.
nanoterm exec-wait <workspace-id> --log <path> [--interval <sec>] [--timeout <sec>]
nanoterm exec ws_a1b2c3d4 --detach --log /tmp/install.log -- npm install
nanoterm exec-wait ws_a1b2c3d4 --log /tmp/install.log
echo $? # the npm install exit code
Why a sidecar file, not ps -p <pid>: detached children may be
reparented to a non-reaping PID 1 inside the container and linger as
<defunct>, which would cause naive ps polling to never terminate.
The sidecar file is written exactly once and is unaffected by zombie
state — exec-wait is the canonical way to await a detached job.
shell
Open an interactive bash session in the workspace. Same wire as
exec but with a pre-configured bash -l PTY — convenient for
ad-hoc poking around.
nanoterm shell ws_a1b2c3d4
# agent@ws-a1b2c3d4:/workspace$ ls
Ctrl+D exits.
attach
Attach a full PTY to a long-running agent process inside the workspace. Use this when the workspace already has a foreground agent running and you want to take over the terminal.
nanoterm attach ws_a1b2c3d4
# Connected. Press Ctrl+D to detach.
Supports color, window resize, and interactive programs (vim, top,
etc.). For a fresh shell instead, prefer nanoterm shell.
files
Read, write, copy, and tail files inside a workspace.
nanoterm files cp <src> <dst>
Copy a file in either direction. The argument with a <id>: prefix
is the workspace side.
# Local → workspace
nanoterm files cp ./script.sh ws_a1b2c3d4:/workspace/script.sh
# Workspace → local
nanoterm files cp ws_a1b2c3d4:/workspace/out.txt ./out.txt
nanoterm cp (top-level) is kept as a hidden alias.
All files cp / files write operations are byte-verified server-side:
the API stats the file after writing and returns {bytes, mtime}, which
the CLI compares against what was sent. A mismatch (silent truncation
through a misconfigured proxy, e.g.) fails loudly with
FILE_WRITE_BYTE_MISMATCH instead of pretending the upload succeeded.
nanoterm files write <id> <path>
Write stdin to a file inside the workspace. Useful for piping generated content without a local temp file.
echo "hello" | nanoterm files write ws_a1b2c3d4 /workspace/hello.txt
cat config.yaml | nanoterm files write ws_a1b2c3d4 /etc/myapp/config.yaml
nanoterm files tail <id> <path>
Poll-stream the tail of a file as it grows. Polling, not inotify — works the same on every runtime.
nanoterm files tail ws_a1b2c3d4 /workspace/build.log
nanoterm tail (top-level) is kept as a hidden alias.
secrets
Manage org-scoped secrets. They're encrypted at rest and injected into every workspace as environment variables.
nanoterm secrets list
nanoterm secrets list
# NAME TYPE UPDATED
# ANTHROPIC_API_KEY env 2026-04-15
# GITHUB_TOKEN env 2026-04-12
nanoterm secrets set <name>
Set an env-type secret. Pass the value via --from-env (recommended)
or stdin; --value is allowed but logs to shell history.
# Pull from your own shell env
nanoterm secrets set ANTHROPIC_API_KEY --from-env ANTHROPIC_API_KEY
# Or via stdin
echo "$ANTHROPIC_API_KEY" | nanoterm secrets set ANTHROPIC_API_KEY --stdin
nanoterm secrets rm <name>
Delete a secret.
nanoterm secrets auth-claude
Run the Claude Code subscription OAuth flow on a sandboxed
workspace, capture the resulting ~/.claude.json, and save it
as an org secret. Every future workspace gets it pre-injected.
nanoterm secrets auth-claude
# Opens the browser; on completion the credential is stored.
For Claude API-key usage instead, set the env directly:
nanoterm secrets set ANTHROPIC_API_KEY --from-env ANTHROPIC_API_KEY
nanoterm secrets auth-codex
OpenAI Codex CLI auth. Only --api-key is supported today (no
ChatGPT subscription flow).
nanoterm secrets auth-codex --api-key sk-...
nanoterm secrets auth-github
Save a GitHub PAT as the GITHUB_TOKEN secret, used by ws create --repo and any workspace git operation.
nanoterm secrets run -- <cmd>
Run a local command with org secrets pre-loaded into its environment
(sister of infisical run). The command runs on your machine,
not in a workspace — useful for scripts that need the same secrets
your agents already use.
nanoterm secrets run -- env | grep API
nanoterm secrets run -- pnpm dev
nanoterm secrets status
Sanity-check: shows which agent providers (Claude, Codex) have working credentials in the org.
snapshot
Local Podman runtime only. Commits the running container to a local image. Restoring creates a new workspace from that snapshot; the source workspace is untouched.
# Commit
nanoterm snapshot create ws_a1b2c3d4 --name before-migration
# List
nanoterm snapshot list ws_a1b2c3d4
# Restore into a new workspace
nanoterm snapshot restore ws_a1b2c3d4 sn_x1y2z3
# Diff against the live workspace
nanoterm snapshot diff ws_a1b2c3d4 sn_x1y2z3
# Delete (also removes the local image)
nanoterm snapshot rm ws_a1b2c3d4 sn_x1y2z3
proxy
Forward an unmapped container port to your local machine — useful
when an app starts listening after ws create ran, so port
pinning wasn't possible upfront.
nanoterm proxy ws_a1b2c3d4 3000
# Forwarding http://localhost:3000 → ws_a1b2c3d4:3000 (Ctrl+C to stop)
nanoterm ws ports <id> lists detected listeners that are good
candidates for proxying.
On startup, proxy performs a one-shot WebSocket pre-flight to
verify auth + workspace existence + container reachability before
opening the local listener. If the upstream returns 401/403, or the
workspace doesn't exist, you get a clear error and the listener never
opens — no more "listening on :15173 but every connection 401s".
Other groups
These exist and have full --help text — kept terse here:
| Group | What it does |
|---|---|
nanoterm keys ... | Create / list / revoke API keys (mirror of the dashboard's API Keys page) |
nanoterm policy ... | Create / list / update governance policies |
nanoterm members ... | List org members, invite by email, revoke invitations |
nanoterm org current / switch <id> | Show or change the active org for the CLI |
nanoterm billing plans / current / upgrade | Inspect plan and quota usage |
nanoterm audit list | Recent audit-log entries for the org |
nanoterm commands | JSON catalog of every command + flags — for agent consumption |
Auto-update notice
The CLI checks npm at most once per day and prints a one-line notice when a newer version is available. The check is best-effort (silent on network failure) and runs in the background — it never blocks the command you ran.
If the API ever raises the minimum required CLI version, you'll
get a 426 CLI_VERSION_TOO_OLD response from the server with the
exact upgrade command. Run npm install -g @nanoterm/cli@latest
and retry.