CLI command vocabulary
This page tells an operator or agent how to name a CLI command. It lists the verbs, how ownership selects a verb pair, the actions that only some families have, and the commands that end in a noun. The CLI lives inapps/cli. The CLI design standard covers input and output.
Each command is one noun family and one last segment. The last segment is a verb from the pairs below, an action that its family lists, or a noun-ending command. The vocabulary also allows one hidden internal command, internal:database-local. Command-specific safety options do not create a new family action: for example, gateway:add --accept-ca-change explicitly accepts a replacement pinned root CA; see Gateway trust.
process:create --user selects the account for a Node systemd Process; it does not add a command or family action. Processes lists the accepted combinations.
CommandVocabulary in apps/cli holds these lists. CommandSurfaceTest fails when a registered command, or a Gateway route that serves one, breaks them. It also fails when this page and the lists differ. Keep this test’s command count and family names aligned when commands are removed as part of legacy cleanup.
gateway:release:rollback switches Gateway code to a retained release. It leaves the database in place; Gateway recovery describes database restoration.
Verb pairs
The CLI uses one pair for each kind of change.Ownership
Usecreate and destroy when the Gateway owns the resource lifecycle. Use add and remove when the command records an association between things that already exist.
route:create takes an Instance ID and domain for an app Route. It does not take a Project ID or an explicit Node or Cluster scope. A custom proxy Route instead takes a domain, serving Node, and upstream or Process. See Route creation.
cluster:router and route:target use set and unset, because each holds one slot. extension uses enable and disable for the Gateway-owned switch; instance:analytics, metrics, and metrics:exporter use enable and disable. proxycli uses setup and teardown for its fleet resources, while proxycli:status and proxycli:models remain family-specific reads. vault uses setup and teardown for the Gateway’s Vaultwarden account, status for its state, and sync to publish at once. tasks uses create for tasks and status for assistance; its extension switch is extension:enable tasks or extension:disable tasks. schedule:enable turns a Schedule on. A Project target selects a definition and never creates a Process or Schedule.
Project Documents use project:document:<verb>. Content operations use read, write, upload, and download; browsing adds search and versions; lifecycle operations add archive, restore, and restore-version. update renames or moves an entry. Permanent removal uses CLI remove and HTTP operation project-document-destroy. HTTP project-document-version-list maps to CLI versions. These are transport names for the same operations, not extra endpoints.
The Gateway names its Project Documents storage routes project:document-storage:show and project:document-storage:update. Storage belongs to the Gateway, so neither operation takes a Project selector. update accepts partial changes after initial configuration and can rotate credentials; there is no destroy, disable, or unset operation for this configuration.
Family-specific actions
Some families have actions outside the pairs above. Each action belongs only to the family that lists it.doctor, profile, and self-update are one-segment commands. Each family name is the command. Project display names, source defaults, and task settings use project:update; create and destroy remain the lifecycle commands.
tool:scan reads supported package-manager inventory without creating Tool intent. Its Gateway route is GET /api/v1/tool-inventory. tool:adopt establishes intent for one existing package without installing it. Both use the Tool contract.
Noun-ending commands
Five commands end in a noun.Gateway route names
The Gateway lives inapps/gateway, and it records each route name as the Activity command. A named route must carry the name of its CLI command when two conditions hold. Its name without the last segment is the prefix of a CLI command, and its last segment is allowed for that family. CommandSurfaceTest checks every named route in routes/api.php.
Some routes have no CLI command. instance:dependencies:show reads the stored dependency inventory for the API and SDK. The CLI has instance:dependencies:scan and instance:dependencies:update, as Instance dependencies describes. tasks:agent-stream is a server-sent event stream for the web task board. The conn: routes serve Conn devices and T3 servers, as Conn layer describes, and have no CLI commands.
tasks:definition:list, tasks:definition:show, tasks:definition:create, tasks:definition:update, and tasks:definition:destroy read and write task definitions on the API, the CLI, and MCP.
node:wireguard-key uses a noun-ending command, like node:settings, because it sets one specific Node property rather than performing a generic update or managing a child resource. Its endpoint is PATCH /api/v1/nodes/{node}/wireguard-key.
Why it works this way
These reasons explain the design. Check them before you propose a change.Two pairs split by ownership
An agent can derive a command from its family and the ownership of its resource, without a command list.add and remove for every family is a rejected alternative, because add would name the creation of an Instance, a Cluster, or a Route. create and destroy for every family is also rejected, because destroy would name the removal of a Node, which leaves the machine intact. node:create and node:destroy stay free for a future command that creates and destroys machines at a hosting provider.
No aliases
The CLI keeps no alias for a renamed command. The consumers of the CLI are this repository’s agents, harness, and operator. An alias doubles the surface that the command-surface test must cover.Definitions in the process and schedule families
A Project definition has the same fields as the Process or Schedule it produces. So theprocess and schedule families serve it with --project. process:update without --project takes an installed Process ID and reconverges it in place; with --project, it takes a definition name and changes later copies only. A separate definition command under the project family is a rejected alternative, because it would hide four verbs behind flags.