CLI Tools

Connect cb-cli, cb-mcp and the maintenance tools to your node with an API key, then inspect and navigate the Store.

Prefer a guided tutorial?

Follow the Inspect the Store with cb-cli hands-on tutorial, then return here for the full reference.

ControlBird ships cb-cli for inspecting and operating the in-memory Store directly. It gives operators and developers a fast, scriptable way to read and write entities, navigate the entity hierarchy, and run filtered queries. Separate maintenance tools read a node's change log, snapshot files, and stored files, and cb-mcp gives an AI assistant the same access. Each tool is its own container image. It runs on your computer and connects to your node over the node's web address, with an API key you create on the node.

The Toolset

ToolImagePurpose
cb-clicontrolbird/cliInteractive REPL and one-shot client for store queries, tree navigation, CRUD, notifications, and pipelines
cb-mcpcontrolbird/mcpModel Context Protocol server that lets an AI assistant query and change the Store
cb-walcontrolbird/walChange-log queries with time, field and type filters, and a follow mode
cb-snapshotcontrolbird/snapshotLists, inspects and validates the node's snapshot files
cb-filescontrolbird/filesVerifies the content of the node's stored files

Every image is tagged with the platform version it belongs to, plus latest. Use the tag that matches the version your node runs. The examples below write it as <version>.

Connect a Tool to Your Node

1. Create an API key

  1. Sign in to your node and open API Keys: on the desktop it is in the user section of the start menu, and on a phone it is in the profile sheet.
  2. Enter a Name that says where the key will be used (for example laptop cb-cli) and choose Expires in (days), from 1 to 365. The default is 90.
  3. Select Create key. The key is shown once. Select Copy key, store it as described in the next step, then select Done. If you lose the key, revoke it and create a new one.

A key acts as you, with exactly your permissions. See Managing API keys for expiry, revocation and limits.

2. Save the key to a private file

The tools read the key from a file, never from the command line or an environment variable, so it does not end up in your shell history or process list. Create a file that only you can read:

mkdir -p ~/.controlbird
touch ~/.controlbird/cb-key
chmod 600 ~/.controlbird/cb-key

Open ~/.controlbird/cb-key in a text editor, paste the key as its only content, and save it. The tools refuse a key file that other users can read, a file that holds anything besides the key, and a symbolic link.

3. Run the tool image

docker run --rm -it --user "$(id -u):$(id -g)" \
  -e CB_NODE_URL=https://node.example.com \
  -e CB_API_KEY_FILE=/run/cb-key \
  -v "$HOME/.controlbird/cb-key:/run/cb-key:ro" \
  controlbird/cli:<version>
SettingValue
CB_NODE_URL The address you open ControlBird at in your browser, such as https://node.example.com, or https://<host>:3443 for a self-hosted node. It must use https://. Plain http:// is accepted only for localhost or a loopback address.
CB_API_KEY_FILEThe path, inside the container, of the mounted key file.
CB_NODE_CA_FILE Optional. The path, inside the container, of a PEM certificate to trust in addition to the public certificate authorities. Use it when the node's certificate is not publicly trusted: give it the node certificate downloaded from the node, or the certificate of your own CA.
--user "$(id -u):$(id -g)" Runs the tool as you, so it can read your private key file. Without it the image runs as an unprivileged user with ID 65532, which cannot read a file only you can read. If you cannot pass --user, give the file to that user instead with sudo chown 65532 ~/.controlbird/cb-key.
-v ...:roMounts the key file read-only at the path named by CB_API_KEY_FILE.

Every tool image takes the same settings; only the image name and the command after it change. To save typing, define a shell alias for each tool you use:

alias cb-cli='docker run --rm -it --user "$(id -u):$(id -g)" -e CB_NODE_URL=https://node.example.com -e CB_API_KEY_FILE=/run/cb-key -v "$HOME/.controlbird/cb-key:/run/cb-key:ro" controlbird/cli:<version>'

The examples on this page are written as if such an alias exists for each tool.

Docker Desktop on Windows

Keep the key file in your WSL home directory and run docker from a WSL shell. Files on a Windows drive appear readable by everyone inside a container, so the tools refuse them.

Connecting to a Community Edition node

A self-hosted node, such as a CE node started as in the CE walkthrough, serves HTTPS on port 3443 with a self-signed certificate it creates on first start. Give the tools that certificate so they trust the node:

  1. Sign in to the node, open Certificate Manager, select the Web Server tab, and select Download node certificate.
  2. Save the file as ~/.controlbird/node.pem, mount it, and point CB_NODE_CA_FILE at it:
docker run --rm -it --user "$(id -u):$(id -g)" \
  -e CB_NODE_URL=https://192.168.1.10:3443 \
  -e CB_API_KEY_FILE=/run/cb-key \
  -e CB_NODE_CA_FILE=/run/node.pem \
  -v "$HOME/.controlbird/cb-key:/run/cb-key:ro" \
  -v "$HOME/.controlbird/node.pem:/run/node.pem:ro" \
  controlbird/cli:<version>

The tools trust that exact certificate at any address you reach the node by, an IP address included, even when the certificate does not name it. A CA certificate in CB_NODE_CA_FILE is different: it is trusted only for certificates it issued that name the address in CB_NODE_URL.

The node certificate is replaced every 795 days or so

The node's own certificate is valid for 825 days. About 30 days before it expires, the node replaces it with a new one and shows a low-priority alarm for a week. The tools then refuse the node until you download the new certificate and replace node.pem with it. A certificate you imported or chose on the Web Server tab is never replaced by the node. See Web Server in the Certificate Manager guide.

Troubleshooting

MessageCause and fix
remote mode needs both CB_NODE_URL and CB_API_KEY_FILESet both variables.
a node URL must use https:// unless its host is loopbackUse the node's https:// address. For a self-hosted node that is https://<host>:3443; see Connecting to a Community Edition node.
the credential file is not a private regular file: ... has mode 0644; run chmod 600 on itRun chmod 600 on the key file on your computer.
open credential ...: permission deniedThe container cannot read the key file. Add --user "$(id -u):$(id -g)".
the node refused the API keyThe key is wrong, expired, revoked or disabled. Create a new one.
the node does not accept API keys for tools: HTTP 429Too many tool connections are open with this key or for your user. Close some and retry.
certificate signed by unknown authoritySet CB_NODE_CA_FILE to the node certificate downloaded from the node, or to the CA certificate that signed the node's certificate.
the node's certificate is neither pinned in CB_NODE_CA_FILE nor issued for its host by a trusted rootThe node serves a different certificate from the one in CB_NODE_CA_FILE, usually because it replaced its own certificate or an administrator chose another one. Download the node certificate again and replace the file.

Managing API Keys

  • A key acts as the user who created it, with exactly that user's permissions. Changing the user's roles changes what the key can do.
  • Each user can have at most 20 active keys. Key names must be unique on the node.
  • The API Keys window lists your keys with their expiry, when each was last used, and a status of Active, Expired or Disabled. Select Revoke and confirm to delete a key; a tool still connected with it is disconnected within seconds.
  • Changing a user's password or deactivating the user disables every key that user created before, and a key does not work while its user's account is locked.
  • Owners see every user's keys in the same window and can revoke any of them.
  • These keys are created on the node itself. They are separate from the Remote Access keys created for a cloud environment.

cb-cli: Interactive Store Client

cb-cli is the primary tool for reading and writing the store. It runs as an interactive REPL (prompt kernel>) or executes a single command in one-shot mode. The REPL needs a terminal, so keep -it on the docker run command. Use the up arrow to recall earlier commands in a session.

# Launch the REPL
cb-cli

# Read the Name and Status fields from entity 42
GET 42 name status

# Find Device entities matching a CEL filter
FIND Device 'Name == "Motor-1"'

# Subscribe to Status changes on entity 42, fetching parent atomically
LISTEN @42 Status CHANGE parent
POLL 100

The REPL groups its commands by purpose. Key commands include:

GroupCommands
Entity CRUDGET, SET, CREATE, DELETE, EXISTS
Type / field resolutionGETTYPE/RESTYPE, GETFLD/RESFLD
QueriesFIND, FINDPAG, FINDEX, TYPES, TYPEPAG (CEL filters supported)
SchemaGETSCH, GETCSCH, SETSCH
NotificationsLISTEN, UNLISTEN, POLL
Indirection & miscRESOLVE, PIPELINE, NODE, INFO, LOGS, HQUERY, PING
SessionHELP, HISTORY, CLEAR, EXIT/QUIT

Output formatting is controlled with --format human|json|csv, and --eval <file> runs a batch of commands from a file. Mount that file into the container and give its path inside the container.

cb-cli TREE: Entity Hierarchy

cb-cli TREE builds the entity hierarchy starting from the Root entity. In the interactive TUI it opens a navigable tree: Up/Down selects a row, Left/Right collapses or expands branches, Enter opens the selected entity's fields, and Esc returns to the prompt. In the field view, Up/Down selects a field, Enter edits and saves it, and Esc cancels editing or returns to the tree. Each field includes its value, writer, and write time. With -c it prints the same hierarchy as an ASCII tree. The Root is discovered automatically; if more than one Root is found, the tool warns.

# Show the tree two levels deep with entity IDs appended
cb-cli -c "TREE --max-depth 2 --show-ids"
FlagEffect
--max-depthMaximum levels to descend (0 means unlimited)
--show-idsAppend each entity's ID to its name
--show-typesShow the entity type alongside each node
--verboseShow additional detail per node

Deep hierarchies can be slow

With --max-depth 0 there is no depth cap. Trees with very deep nesting or many children at each level may take time to build and render. Set a depth limit when you only need the top of the tree.

cb-cli SELECT: Filtered Queries

cb-cli SELECT queries entities of a given type, optionally filtered by a CEL expression, and prints selected fields in your chosen format. It is the right tool for reporting and bulk inspection. By default it returns the Name field if you do not specify --fields.

# Active devices, CSV output with IDs, showing Name and Status
cb-cli -c "SELECT Device --filter 'IsActive == true' --fields Name,Status --format csv --show-ids"
FlagEffect
entity_typeEntity type to query (required positional argument)
--filterCEL expression; supports indirection, e.g. "Parent->Name"
--exactMatch the exact type only (no subtypes)
--fieldsComma-separated field list with -> indirection support
--limit / --page-sizeCap results (0 = unlimited) and control pagination
--formatOne of table, json, csv, ids, count
--show-ids / --show-typesInclude the entity ID / type columns
--exportWrite results to a JSON file
--metricsReport per-page and per-field timing and throughput

Field indirection like Parent->Name is resolved recursively. Note that per-field resolution happens sequentially across entities, so queries returning thousands of entities with many fields can be slow.

cb-wal: Change Log

The change log records every store mutation. cb-wal asks the node to read its change log and prints each recorded change, one per line.

# Changes to the Status field of Device entities since a given time, then keep watching
cb-wal query --entity-type Device --field Status --start-time 2026-09-28T09:00:00Z --follow

# Everything written to entity 42, as JSON
cb-wal query --entity 42 --format json

# The node's change-log files, their size, record count and time range
cb-wal info
query flagEffect
--start-time / --end-timeOnly entries written at or after / at or before this RFC3339 time
--fieldOnly entries for this field, by name
--entity-typeOnly entries whose entity is of this type, by name
--entityOnly entries for this entity ID
--writerOnly entries written by this writer's entity ID
--kindOnly entries of this kind: field_update, create_entity or delete_entity. Repeat it for several kinds.
--limitStop after this many entries
--formatcompact (the default) or json
--followKeep checking the node for new entries, about once a second, until you press Ctrl+C

cb-wal info takes --format as well. Entries for fields you are not allowed to read are left out, and Secret values are shown as ***.

cb-snapshot: Snapshot Files

A node keeps point-in-time snapshot files of its data. cb-snapshot asks the node to examine them and prints the results. Secret values are never shown.

CommandWhat it does
cb-snapshot listLists the node's snapshot files with their sequence number, size and creation time.
cb-snapshot inspect <name>Reports what a snapshot file holds: its format, schema and entity counts, ID counters, and how many File and Secret fields it records.
cb-snapshot validate <name>Checks that a snapshot file is complete and readable. It exits with a non-zero status when the file is empty or cannot be decoded.

<name> is a file name exactly as list prints it, such as snapshot_0000000042.bin.

Backing up your configuration

To back up, compare, and restore your configuration, use the Config Manager app. It keeps a history of snapshots, restores with a dry run and undo, and can push to a Git repository.

cb-files: Stored Files

cb-files verify asks the node to recheck the content of every stored file and reports each one whose content no longer matches what was stored. It exits with a non-zero status when any file fails. Only one verification runs on a node at a time.

cb-files verify

Permission for the maintenance tools

cb-snapshot, cb-files verify, cb-wal info and a cb-wal query without --entity need the app.wal permission, which the Engineer, Administrator and Owner roles include. A cb-wal query --entity needs only read access to that entity's fields.

cb-mcp: The Store for AI Assistants

cb-mcp is a Model Context Protocol server. An AI assistant that supports MCP starts it and talks to it over standard input and output, so run it with -i and without -t. It offers the assistant tools to read, find, create, change and delete entities, walk the tree, read schemas, and query history, the change log and service logs, all with the permissions of the key's user.

Add it to your assistant's MCP configuration. Most assistants accept an entry like this; replace 1000:1000 with the output of id -u and id -g, and the key path with your own:

{
  "mcpServers": {
    "controlbird": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i", "--user", "1000:1000",
        "-e", "CB_NODE_URL=https://node.example.com",
        "-e", "CB_API_KEY_FILE=/run/cb-key",
        "-v", "/home/you/.controlbird/cb-key:/run/cb-key:ro",
        "controlbird/mcp:<version>"
      ]
    }
  }
}

Create a separate key for the assistant, so you can revoke it without affecting your other tools.

Limitations

  • TREE --max-depth 0 and SELECT ... --limit 0 are unlimited and can be slow or memory-heavy on large data.
  • SELECT resolves fields one at a time, with no batching across entities.
  • cb-wal --follow checks for new entries about once a second, so it is near-realtime.
  • cb-cli cannot take a snapshot (SNAP) or upload a file (SEND --upload) on a remote node.
  • cb-cli's --host and --port cannot be combined with CB_NODE_URL.
  • A key can hold 4 tool connections at once, and a user 16 across all their keys.

To secure the node address the tools connect to, see the certificates guide.