Headless Mode
Run CCP non-interactively from CI, dev VMs, scripts, and AI agents.
Headless Mode
CCP is designed to run unattended in CI pipelines, ephemeral dev VMs, and AI-agent loops. One env var puts the CLI into headless mode, an auth token can be passed in via the environment, and identity flags let you target specific resources without prompts.
Enabling Headless Mode
Set CCP_HEADLESS=1 in the environment:
export CCP_HEADLESS=1This is the single canonical switch. With it set, every interactive prompt is auto-confirmed, every TTY-only command (ccp db connect) errors fast, and styled output is replaced with terse one-line [ccp] ... logs suitable for log capture.
CCP also auto-detects non-TTY stdin/stderr and behaves the same way. Prefer the env var when running under PTYs (tmux, screen, expect-style wrappers) where TTY detection can falsely report interactive.
Per-command escape hatches still exist — ccp dev --headless, ccp deploy --yes, ccp db destroy --yes — but CCP_HEADLESS=1 covers all of them at once.
Destructive commands run without confirmation in headless mode.
ccp remove,ccp undeploy,ccp db destroy,ccp db backup delete, andccp domain removeall execute immediately. This is intentional for automation; double-check the target ID before invoking.
Authentication
CCP resolves a session token in this order:
CCP_SESSION_TOKENenv var (preferred for headless)- The persisted session store — a
0600file at~/.cluster/ccp-session.jsonon macOS (default), or the OS keyring on Linux and Windows (secret service / Credential Manager), with the file as a fallback. SetCCP_AUTH_STORE=fileor=keychainto override. See Session storage.
The recommended headless flow is to authenticate once on a workstation, export the token, and pass it into the runtime environment:
# On the operator's machine (interactive)
TOKEN=$(ccp auth print-access-token)
# In the headless environment
export CCP_SESSION_TOKEN="$TOKEN"
ccp deploy --prodIf CCP_SESSION_TOKEN is expired, CCP attempts a silent refresh; if refresh fails, it errors with Not logged in and you'll need to re-export a fresh token.
For scripted login when you have an OTP code in hand, pass --email and --code together. --code skips the OTP send step and assumes the code was already issued:
ccp auth login --email me@example.com --code 123456Plain ccp auth login (no --code) will hang or error in a non-TTY context — always pair email and code together when scripting.
Identity Flags
Identity flags name which resource a command operates on. They're orthogonal to headless mode — passing --org-id doesn't enable headless, and CCP_HEADLESS=1 won't pick a resource for you.
| Flag | Used by | Resolved from |
|---|---|---|
--org-id <id> | ccp deploy, ccp link | .cluster/config.json, then CCP_ORG_ID, if omitted |
--function-id <id> | ccp deploy, ccp link, ccp db create | .cluster/config.json if omitted |
--db-id <id> | ccp db exec, migrate, connect, backup *, destroy | .cluster/config.json if omitted |
--token <token> | ccp db exec, migrate, connect | .cluster/config.json if omitted |
--store-id | ccp store * | .cluster/config.json if omitted |
When a required ID is missing and can't be resolved from config, CCP errors clearly rather than guessing. The exception: if exactly one org or store exists on your account, CCP will pick it.
Always commit .cluster/config.json. It's how subsequent invocations (and other VMs) find the function.
Resolving the org with CCP_ORG_ID
Multi-org accounts otherwise have to pass --org-id on every org-scoped command (a single-org account auto-picks its only org). Export CCP_ORG_ID once and every org-scoped command — ccp deploy, ls, link, init, store, and the compute, oidc, and domain subcommands — uses it when no higher-precedence hint is present, so a headless multi-org account needs no per-call flag. It's the org-id counterpart to CCP_SESSION_TOKEN:
export CCP_SESSION_TOKEN="$TOKEN"
export CCP_ORG_ID="$ORG_ID"
ccp deploy --prod # no --org-id, even on a multi-org accountThe org is resolved with this precedence everywhere an org is chosen:
--org-idflag- project config —
.cluster/config.jsonorcluster.toml CCP_ORG_ID- the single org on your account (auto-picked)
- otherwise: an error in headless mode, or the interactive picker in a TTY
Because the project config outranks the env, a repo's pinned org still wins over an ambient CCP_ORG_ID — a cloned project in a CCP_ORG_ID-set dev VM deploys where it declares — and an already-linked deploy ignores the env entirely (the linked function's org drives it).
A CCP_ORG_ID that matches none of your organizations fails loudly and names the variable:
Organization '<id>' (from CCP_ORG_ID) not found — check it or `unset CCP_ORG_ID`so a stale export surfaces as an actionable error instead of a mystery "Organization not found". This loud failure applies even to single-org accounts: a set-but-wrong CCP_ORG_ID errors rather than silently falling through to your only org.
Platform-created dev VMs set it for you. When Cluster provisions a development (or build-machine) VM, it injects CCP_ORG_ID alongside the in-VM session token, pre-set to your default org — the most-recently-created org you own, or one you're a member of if you own none. A multi-org account's in-VM ccp deploy therefore resolves an org with no --org-id and no manual export. As the lowest-precedence hint (precedence 3 above), a project-pinned org or an explicit --org-id still overrides it.
Reference Matrix
With CCP_HEADLESS=1 set:
| Command | Headless-safe | Required input |
|---|---|---|
ccp init <name> | yes | positional <name> |
ccp dev | yes | — |
ccp build | yes | — |
ccp deploy | yes | populated config, or nothing — unlinked, it auto-creates a function (single-org auto-picks; --org-id or CCP_ORG_ID for multi-org) |
ccp link | yes | --org-id (or CCP_ORG_ID) + --function-id |
ccp list / ls | yes | linked project; outside one, --org-id/CCP_ORG_ID or a single-org account |
ccp logs | yes | linked project (or [FUNCTION_ID]) |
ccp remove | yes (destructive) | linked project |
ccp promote <id> | yes | deployment ID |
ccp undeploy <id> | yes (destructive) | deployment ID |
ccp store create | yes | org from config or single-org account |
ccp store put / get / ls / rm | yes | store from config or single-store org |
ccp db create | yes | linked project or --function-id |
ccp db ls / info / exec / migrate | yes | linked project or --db-id |
ccp db destroy <id> | yes (destructive) | DB ID |
ccp db connect | no | use ccp db exec instead |
ccp db backup create / ls | yes | linked project or --db-id |
ccp db backup restore <id> | yes (destructive) | backup ID |
ccp db backup delete <id> | yes (destructive) | backup ID |
ccp domain ls / add / link / unlink | yes | flags as needed |
ccp domain remove <domain> | yes (destructive) | domain |
ccp auth login | yes | --email + --code |
ccp auth print-access-token / export-access-token / logout | yes | logged-in session |
ccp db connect is the only command that's incompatible with headless mode — it opens a TUI psql shell. Use ccp db exec '<SQL>' for one-off statements.
Typical Agent Workflow
# In an ephemeral dev VM (CCP_HEADLESS=1, CCP_SESSION_TOKEN, and CCP_ORG_ID already in env)
cd /workspace
ccp init my-app --template react
cd my-app
# ... agent edits code, runs tests ...
# First deploy auto-creates + links the function (the VM's injected CCP_ORG_ID
# resolves your default org with no flag; pass --org-id to target a different
# org), and writes the IDs to config:
ccp deploy --prod
# Persist the link before VM teardown:
git add .cluster/config.json
git commit -m "link function"
git push
# Later, in a fresh VM with the same repo:
git clone "$REPO" && cd my-app
ccp deploy --prod # reuses .cluster/config.jsonReference for AI Agents
ccp print-skill prints an authoritative reference for the CLI when used by an AI agent. It's embedded in the binary, so it stays in sync with the version of ccp you have installed.
print-skill is progressive — load just the part you need instead of the whole reference:
ccp print-skill # compact overview: headless mode, auth, project shape,
# the reference matrix, and an index of topics
ccp print-skill <topic> # one surface in depth, e.g. `ccp print-skill db`
ccp print-skill all # the complete reference — every topic at onceStart with ccp print-skill, then fetch the one or two topics your task needs. The available topics are deploy, stores, oidc, db, domains, compute, ci, and env; an unknown topic errors and lists the valid ones. The overview always carries the reference matrix and a generated topic index, so an agent can discover every surface even before fetching a deep dive.
Each view is a markdown document covering authentication, project shape, the relevant commands' headless behavior, and common pitfalls. ccp print-skill all reproduces the full reference (the original behavior) — pipe it into an agent's context, or save it as SKILL.md in your project to give a coding agent a complete reference for working with CCP.
Common Pitfalls
- Don't commit:
.env,node_modules/, or.cluster/index.js(build output). - Always commit:
.cluster/config.json— the persistent link to the remote function across VMs. - Token expiry: if
CCP_SESSION_TOKENis expired and silent refresh fails, re-export a fresh token from the operator's machine. - No git integration: CCP doesn't commit or push for you. Persist
.cluster/config.jsonmanually after the first deploy or link. - Build artifacts (
.cluster/index.js, bundled assets) regenerate on eachccp deploy/ccp build— safe to delete from a clean checkout.