CLI overview
This page is the front door to the platformctl reference. You will learn what the CLI covers, how to install it, how it decides which endpoint to call and which credential to send, where its config file lives, and how it behaves when things go wrong.
What platformctl is
platformctl is the command-line tool for the Crusoe AI Platform. It is one self-contained program written in Go — no runtime, no plugins. It packages a folder of agent code, uploads it, waits for the platform to build and start it, and then lets you chat with the agent, read its logs, set its secrets, and delete it.
It also administers the rest of the platform. Serverless container services, pub/sub topics and subscriptions, memory stores, vector indexes and the points inside them, MCP servers, the project secret store, projects, organizations, platform users, project membership, service accounts, API keys, invitations, quota, and the audit trail all have commands. That is 35 command groups and, not counting the shell completion generators, 196 commands.
Almost everything the web console can do, platformctl can do in a scriptable form — including the one flow you might expect to be browser-only. A new account is issued a temporary password, and until you replace it every management call is refused. You can clear that from the terminal with platformctl change-password; you never have to open the console just to start working. (That is different from platformctl users reset-password, which is an administrator issuing someone else a fresh temporary password.)
Where a specific operation in a group is still console-only, the page documenting that group says so.
Command groups
platformctl --help prints this list. It is the map of the whole surface — every command lives under one of these.
| Group | What it covers |
|---|---|
agents | A deployed agent's life after deploy: revisions and traffic pinning, scaling and resource config, the memory policy and what the agent remembers about each user (agents memory), its operator-written knowledge (agents knowledge), plain environment variables, source files, sessions, end users, live spec, runtime signals, which secret keys are set, redeploy, and the embeddable chat widget. |
audit | The project's audit trail, newest first, one page per run (--limit, --page-token). Every project member can read it. |
change-password | Replace the password of the account you are signed in as. A new account is issued a temporary one, and every management call is refused until it is replaced — so this is usually the first command you run. |
completion | Shell completion scripts for bash, zsh, fish, and powershell. |
config | The CLI's own persisted settings: the saved default project, and where the config file lives. |
crusoe-cloud (alias cc) | The project's Crusoe Cloud connection: connect, show, disconnect, and browse the buckets, container-registry repositories, and managed inference models it reaches. Connecting is a prerequisite for deploying — every image the platform builds is pushed to a repository in your own Crusoe Cloud registry. |
delete | Delete an agent or a function. |
demo | Walk the golden path end to end against the bundled research-buddy example. |
deploy | Package a directory of agent code and deploy it (ADK, LangGraph, or CrewAI). |
functions | Deploy a function directory (Python, Node.js, Go, or Ruby). |
gateway | Publishing to the internet: publish puts an agent, service, function or MCP server at an address you can hand out, and endpoint, key and domain manage what happens after — API keys, IP allow lists, rate limits, and your own domains. Nothing is reachable from outside the project until you publish it. |
iam | Who holds a role on this project: members, service-account grants, and recorded break-glass access. |
inference | The platform's hosted models: models lists the catalog, embed turns text into vectors, rerank scores documents against a query. |
invitations | Invite an email address that has no account yet; list, revoke, preview, and accept. |
invoke | Send a message to an agent and print its response. |
keys | Your own API keys — create (the secret is printed once), list, revoke. |
list | List the agents and functions deployed in the project. |
login | Sign in and cache a session token for management commands. |
logout | Discard the cached session token. |
logs | Print an agent's logs: the running instance's buffer, --follow to tail it, or --history for persisted logs that survive scale-to-zero. |
mcp | Hosted, scale-to-zero MCP tool servers: their tools, the immutable version built by each publish, rollback, and yank/unyank. |
memorize | Write a session's conversation into an agent's long-term memory. |
memorystore | Memory store instances: create, inspect, delete, rotate the credential, and read a live stats snapshot. |
orgs | Organizations and their members. Not project-scoped, so --project is ignored here. |
platform | The platform's own limits and defaults — container sizes, timeout ceilings, the embedding width your indexes must use. Read these rather than hardcoding them. |
profile | The platform-wide default model credential that agents inherit. Write-only, platform admin only. |
projects | Create, list, inspect, rename, and delete projects. |
pubsub | Topics and subscriptions, publishing and pulling messages, direct broker connection details, per-topic usage counters, and the storage quota. |
quota | The project's limits and how much of each is in use, read from the accounting that actually enforces them. |
secrets | Two different things: the versioned project secret store (put, list, show, versions, reveal, delete, issue-token), and an agent's own environment secrets (secrets set). |
search | Find a resource by name across every project you can see, when you know what a thing is called but not where it lives. |
serverless | Scale-to-zero container services: create, update, delete, revisions, traffic splits, logs, metrics, spec, and triggers that fire on a schedule, a topic, or a bucket. |
service-accounts | The project's machine principals and their API keys. Project admin only — and never usable with a service account's own credential. |
status | An agent's full status. |
users | Platform-wide user accounts: create, list, change role, reset password, delete. |
vectordb | Vector indexes and the points in them: create, update, delete, upsert, query, scroll, delete points, and direct-access credentials. |
whoami | The identity the CLI is authenticating as, and which credential it used. |
A few commands deliberately refuse a value flag. platformctl secrets put has no --value; it reads from --value-file or stdin. platformctl crusoe-cloud connect has no --secret-key; it reads from --secret-key-file or stdin. That is a design choice, not an oversight: a flag value lands in your shell history, is visible in the process table for as long as the command runs, and is captured by any shell tracing or audit rule watching argv. A file or a pipe is none of those things.
Install
From the repository root:
( cd cli/platformctl && go build -o ../../bin/platformctl . )
export PATH="$PWD/bin:$PATH"
platformctl --help
The install guide walks through this step by step.
Shell completion
Tab completion means your shell finishes command and flag names for you when you press Tab. platformctl generates the script for it, for four shells: bash, zsh, fish, and powershell.
platformctl completion --help
You should see:
Generate the autocompletion script for platformctl for the specified shell.
See each sub-command's help for details on how to use the generated script.
Usage:
platformctl completion [command]
Available Commands:
bash Generate the autocompletion script for bash
fish Generate the autocompletion script for fish
powershell Generate the autocompletion script for powershell
zsh Generate the autocompletion script for zsh
To turn it on for the shell you are in right now — a good way to try it before committing:
source <(platformctl completion bash) # bash
source <(platformctl completion zsh) # zsh
platformctl completion fish | source # fish
To keep it after you close the terminal, write the script where your shell looks for completions. Run the line for your shell once, then start a new shell:
| Shell | Run once |
|---|---|
| bash (Linux) | platformctl completion bash > /etc/bash_completion.d/platformctl |
| bash (macOS) | platformctl completion bash > $(brew --prefix)/etc/bash_completion.d/platformctl |
| zsh (Linux) | platformctl completion zsh > "${fpath[1]}/_platformctl" |
| zsh (macOS) | platformctl completion zsh > $(brew --prefix)/share/zsh/site-functions/_platformctl |
| fish | platformctl completion fish > ~/.config/fish/completions/platformctl.fish |
| powershell | Add the output of platformctl completion powershell to your PowerShell profile |
Bash needs the bash-completion package installed. Zsh needs completion enabled — if it is not already, run echo "autoload -U compinit; compinit" >> ~/.zshrc once.
Each subcommand also takes --no-descriptions, which drops the short help text that would otherwise appear next to each suggestion. platformctl completion zsh --help prints the same instructions for your shell at any time.
How the CLI reaches the platform
The CLI talks to the public API at https://api.codyhill.dev unless you tell it otherwise. Nothing needs configuring for that to work.
The CLI talks to two families of endpoints:
Control plane (most commands). The endpoint is resolved in this order:
- The
--apiflag. - The
$CAI_APIenvironment variable. - The public API,
https://api.codyhill.dev. This is the default and needs no setup.
There is a fourth, opt-in route: give either the flag or the variable the literal value port-forward and the CLI opens a short-lived private tunnel into the platform instead. That is break-glass for administrators — it needs privileged access to the platform's internals — and it never happens by itself. If the tunnel cannot be opened, you get:
port-forward failed (pass --api <url> or set CAI_API to a URL instead): ...
The fix is almost always to stop asking for the tunnel: unset the variable and let the default take over.
Service groups (serverless, pubsub, memorystore, vectordb). These four command groups deliberately do not take their base URL from --api. Each has its own environment variable, and each falls back to $CAI_API and then to the public API when that variable is unset — one hostname serves all of them, so in normal use you set none:
| Command group | Env override |
|---|---|
platformctl serverless | CAI_SERVERLESS_API |
platformctl pubsub | CAI_PUBSUB_API |
platformctl memorystore | CAI_MEMORYSTORE_API |
platformctl vectordb | CAI_VECTORDB_API |
--api is still used indirectly by these four groups: if --project is a slug or short name rather than a UUID, the CLI resolves it to a UUID through the core platform API, reached via --api, $CAI_API, or the public default. Pass the project's UUID to skip that call entirely.
Global flags
Every command accepts these flags. One of them — -o — is accepted everywhere but honored only by the commands that print a resource; the rule and the exceptions are in output formats and scripting below.
| Flag | Default | What it does |
|---|---|---|
--api <url> | $CAI_API, else https://api.codyhill.dev | Base URL of the core platform API. The four service groups above do not take their base URL from it, but still use it to resolve a slug or short-name --project to a UUID. |
--project <ref> | $CAI_PROJECT, else the saved default, else the server picks your single/default project | Which project to scope requests to. Accepts a project's slug, short id, or full UUID. |
-o, --output <format> | table | Output format: table, json (pretty-printed), or yaml. Validated on every command, ignored by some — see below. |
--json | off | Hidden legacy flag: emits one compact JSON line as the last line of stdout. Kept for existing scripts; use -o json instead. |
Project resolution, most specific first: --project flag, then $CAI_PROJECT, then the default saved by platformctl config set-project, then empty (the server chooses). Note that $CAI_PROJECT beats the saved config default — the same convention as gcloud, aws, and az.
What that reference costs you depends on which of three families the command belongs to:
| Family | Commands | How the project travels | Cost of a slug or short name |
|---|---|---|---|
| Agent routes | deploy, functions deploy, list, status, invoke, memorize, logs, delete, secrets set, and the agents group | ?project=<ref> on the request; the server resolves it | Nothing — slug, short id, and UUID all cost the same |
| Project-scoped control-plane routes | mcp, iam, audit, quota, service-accounts, projects get/update/delete, invitations (except preview and accept), and the project secret store — every secrets subcommand except set | The project's UUID as a path segment | One GET /v1/projects lookup, made on the connection the command already opened. A UUID skips it. |
| The four service groups | serverless, pubsub, memorystore, vectordb | The project's UUID as a path segment, on a different base URL | One lookup against the control plane, over a second connection. Pass a UUID in CI to avoid it. |
The first family can fall back to "the server chooses". The other two cannot — they need a project named somewhere, and with none set they fail before sending anything:
this command needs a project: pass --project, set $CAI_PROJECT, or run 'platformctl config set-project' (use the id or slug shown by 'platformctl projects list')
Some groups are not project-scoped at all, and the CLI deliberately sends no project reference on their requests: orgs sits above projects, users and profile are platform-wide, and keys manages your credentials rather than a project's. invitations preview and invitations accept also send none — the token is the credential. For all of these, --project and $CAI_PROJECT are ignored.
Output formats and scripting
-o table(default): human-readable columns.-o json: the full object(s), pretty-printed.-o yaml: the same, as YAML.--json(hidden, legacy): one compact JSON line as the last stdout line — the contract the platform's own e2e scripts rely on.
An invalid format fails with:
invalid --output "xml": want table, json, or yaml
Which commands honor -o
-o is checked once, centrally, before any command runs — so a bad value is rejected everywhere. A good value, though, only changes the output of the commands that print a resource. The rest print a fixed human sentence and ignore the flag entirely.
The rule: if a command returns an object or a list, -o works. If it prints a confirmation sentence or streams raw text, -o is ignored.
That makes it honored across nearly the whole surface: deploy, functions deploy, list, status, invoke, logs --history, config get-project, config view, and the read, create, and update verbs of every group — agents, audit, iam, invitations, keys, mcp, memorystore, orgs, profile, projects, pubsub, quota, secrets, serverless, service-accounts, users, vectordb. (agents spec and serverless spec count as resource commands: their default output is the YAML document, and -o json/-o yaml return the envelope around it.)
Ignored by these:
| Command | What it prints instead |
|---|---|
login, logout, whoami | A plain identity or session sentence |
demo | The narrated golden-path walkthrough |
memorize, secrets set, config set-project | A one-line confirmation |
Every delete, revoke, and remove verb, in every group — delete, mcp delete, mcp tools delete, serverless delete, serverless triggers delete, vectordb delete, memorystore delete, pubsub topics delete, pubsub subscriptions delete, secrets delete, projects delete, users delete, orgs delete, orgs members remove, iam members remove, iam service-accounts remove, service-accounts delete, service-accounts keys revoke, keys revoke, invitations revoke, agents sessions delete, agents files delete, agents embed disable | A one-line confirmation such as deleted my-agent, exit 0 |
logs (default) and logs --follow | Raw log text |
Note the split inside logs: --history reads stored log records and can render them as JSON or YAML, while the default and --follow modes stream raw log text, which has no object shape to convert.
platformctl whoami -o json prints prose, not JSON, and exits 0. A pipeline like platformctl whoami -o json | jq -r .email fails inside jq, not in the CLI. When scripting, only parse output from a command the rule above says is honored.
Here is the difference, on one command from each side:
platformctl status research-buddy -o json | jq -r .state
platformctl whoami -o json
You should see:
ready
you@example.com role=admin (credential: cached login (/Users/you/Library/Application Support/crusoe-ai/token))
The second line is the plain-text whoami output, unchanged by -o json. To script identity checks today, read the same values from the API instead — see API authentication.
Exit behavior and errors
- Commands exit
0on success and non-zero on any failure, with the error printed to stderr. - Any 4xx or 5xx from the API is wrapped as:
workload-api returned <code>: <body>
- On a
401or403, the CLI also tells you which credential it tried:
workload-api returned 401: ... [platformctl credential: none; run `platformctl login` or set $CAI_TOKEN]
The credential source shown is one of: none, $CAI_TOKEN, cached login (<path>), or $CAI_USER/$CAI_PASSWORD.
Credential resolution
Every request carries at most one credential, sent as Authorization: Bearer <credential>. The CLI picks it in this order, most specific first:
$CAI_TOKEN— any bearer credential: a session token, or an API key in the formatcai_<keyid>_<secret>. Create one withplatformctl keys createfor yourself, orplatformctl service-accounts keys createfor a machine principal; the console has the same under Security → API keys. Either way the secret is printed once and only a hash is kept.- The cached login token written by
platformctl login, stored at<UserConfigDir>/crusoe-ai/token(macOS:~/Library/Application Support/crusoe-ai/token; fallback:$TMPDIR/crusoe-ai-token), owner-only permissions. Tokens last 12 hours; a token expiring within 30 seconds is treated as absent and the CLI falls through to the next source. $CAI_USER+$CAI_PASSWORD— the CLI logs in on the spot viaPOST /v1/auth/login. It caches nothing, and on failure it falls through instead of erroring.
Finding nothing is not an error. Invoking a deployed agent needs no credential by default (the "data plane" is open). Management commands — deploy, list, status, logs, secrets, delete — return a 401 annotated with the credential source that was tried. See API authentication for the full model.
The config file
platformctl config set-project saves your default project to:
<UserConfigDir>/crusoe-ai/config.json
The file has 0600 permissions (only you can read it) and contains {"default_project":"..."}. Remember: $CAI_PROJECT still overrides it. See the config commands for details.
Timeouts
| Operation | Timeout |
|---|---|
| Any API request | 5 minutes (deliberately generous for cold starts) |
| Deploy upload request | 2 minutes |
| Deploy readiness polling | every 2 seconds, up to 5 minutes |
| Opening the CLI's own connection | 15 seconds |
logs --follow | none — tails until you stop it |
How it compares
platformctl | AWS CLI | gcloud | az | |
|---|---|---|---|---|
| Install | Build from source () | Prebuilt installers and packages | Prebuilt installers | Prebuilt installers |
| Sign in | platformctl login, $CAI_TOKEN API keys | Access keys, SSO | Browser OAuth flow | Browser OAuth flow |
| Output | table, json, yaml | json, text, table | default, json, yaml, more | json, table, tsv, more |
| Coverage | The whole platform: agents, functions, four data services, MCP servers, secrets, and platform administration | Hundreds of services | Hundreds of services | Hundreds of services |
Summary: the big clouds' CLIs cover far more services and ship signed binaries. platformctl covers one platform and is built from source during the — no prebuilt binaries and no package manager. What it does cover, it now covers end to end: deploy to invoke in two commands, and every administration area the console exposes.
In this section
- Agents and functions —
deploy,functions deploy,list,status,invoke,memorize,secrets set,logs,delete,demo, and theagentsgroup: revisions, traffic, config, env, files, sessions, users, metrics, spec. - Data and messaging —
serverless,pubsub,memorystore,vectordb, end to end: create, update, and delete as well as read. - MCP and secrets — MCP servers and the project secret store.
- Platform and auth —
login,logout,whoami,config,projects,orgs,users,iam,invitations,service-accounts,keys,profile,quota,audit. - Gateway and inference —
gateway(publish, endpoints, keys, domains) andinference(models,embed,rerank), plusplatform,searchandchange-password.