Shell & CLI Tools
Use cb-cli, cb-tree, cb-select and the browser Shell to inspect the Store.
Prefer a guided tutorial?
Follow the Inspect the Store from the Shell hands-on tutorial, then return here for the full reference.
ControlBird ships a family of command-line tools for inspecting and operating the in-memory Store directly, plus a browser-based Shell app that runs them inside the platform UI. Together they give operators and developers a fast, scriptable way to read and write entities, visualize the entity hierarchy, run filtered queries, and inspect logs and snapshots, all without leaving the running system.
Every tool connects to the kernel the same way as a service: through the Store client pointed at KERNEL_ADDRESS. When you launch a tool from the Shell app, the kernel address, node identity, data directory, and user attribution are all pre-configured for you, so the tools are immediately usable with no flags.
The Toolset
| Tool | Purpose |
|---|---|
cb-cli | Interactive REPL and one-shot client for store queries (read/write/create/delete/find/notifications/pipelines) |
cb-tree | Hierarchical entity tree visualization with ASCII art and depth control |
cb-select | CEL-filtered entity queries with pagination, field selection, and multiple output formats |
cb-wal | Change-log inspection with timestamp filtering and follow mode |
cb-snapshot | Snapshot capture, restore, validation, diff reporting, and export |
The Browser Shell
The Shell app opens a real terminal in your browser and relays input and output over the existing WebSocket connection. Access requires the app.shell permission, which is granted to the Owner role.
How Sessions Work
- Access enforced continuously: the
app.shellpermission is required to open a session, and revoking a user's access takes effect immediately. - Sandboxed: terminal sessions run with restricted privileges so terminal access cannot reach beyond what the tools need.
- UTF-8 ready: the terminal supports 256-color, UTF-8 output so box-drawing glyphs in text-based apps render correctly.
Environment Variables
Every Shell session and every tool it launches inherits a pre-configured environment so the tools find the kernel and identify the writer automatically.
| Variable | Purpose |
|---|---|
KERNEL_ADDRESS | Kernel connection used as a fallback when no explicit host/port is given |
NODE_ID | Node identifier (used by cb-wal and cb-snapshot to locate data) |
DATA_DIR | Root data directory for log files and snapshots |
RUST_LOG | Log level for the tools |
CB_USER_ID | Current user's entity ID, used for writer attribution in the audit trail |
CB_USER_NAME | Current user's name for the audit trail |
CB_BIN_DIR | Directory of the cb-* binaries, prepended to PATH |
CB_BIN_DIR is added to PATH so that cb-cli, cb-tree, cb-select, and cb-wal are immediately discoverable in a Shell session.
# Inside a Shell session, the environment is already wired up
echo $KERNEL_ADDRESS # prints the kernel address
echo $CB_USER_ID # your user entity ID, used for write attribution
cb-cli # launches the REPL with no flags neededSession limits
Each connection supports up to 4 concurrent terminal sessions, with up to 20 sessions in total across all users. These limits keep terminal usage from exhausting platform resources.
AI Tool Sessions
Beyond a plain bash shell, the Shell app can launch AI coding tools (Claude Code, Codex, OpenCode) in dedicated sessions. These run non-interactively for safety. An AI tool session replaces the shell context for that session rather than nesting inside an existing one.
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. Interactive history is stored in ~/.kernel_history.
# Launch the REPL (explicit host/port, or rely on KERNEL_ADDRESS)
cb-cli --host localhost --port 9100
# 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 100The REPL groups its commands by purpose. Key commands include:
| Group | Commands |
|---|---|
| Entity CRUD | GET, SET, CREATE, DELETE, EXISTS |
| Type / field resolution | GETTYPE/RESTYPE, GETFLD/RESFLD |
| Queries | FIND, FINDPAG, FINDEX, TYPES, TYPEPAG (CEL filters supported) |
| Schema | GETSCH, GETCSCH, SETSCH |
| Notifications | LISTEN, UNLISTEN, POLL |
| Indirection & misc | RESOLVE, PIPELINE, SNAP, NODE, INFO, LOGS, HQUERY, PING |
| Session | HELP, HISTORY, CLEAR, EXIT/QUIT |
Output formatting is controlled with --format human|json|csv. The --raw flag prints the raw, unformatted response for debugging, and --eval <file> runs a batch of commands from a file.
cb-tree: Entity Hierarchy
cb-tree builds and prints the entity hierarchy starting from the Root entity. It walks each entity's children recursively and renders the result 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-tree --max-depth 2 --show-ids| Flag | Effect |
|---|---|
--max-depth | Maximum levels to descend (0 means unlimited) |
--show-ids | Append each entity's ID to its name |
--show-types | Show the entity type alongside each node |
--verbose | Show 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-select: Filtered Queries
cb-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-select --entity-type Device \
--filter 'IsActive == true' \
--fields Name,Status \
--format csv --show-ids| Flag | Effect |
|---|---|
--entity-type | Entity type to query (required) |
--filter | CEL expression; supports indirection, e.g. "Parent->Name" |
--exact | Match the exact type only (no subtypes) |
--fields | Comma-separated field list with -> indirection support |
--limit / --page-size | Cap results (0 = unlimited) and control pagination |
--format | One of table, json, csv, ids, count |
--show-ids / --show-types | Include the entity ID / type columns |
--export | Write results to a JSON file |
--metrics | Report 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 reads the log files in data/<node>/wal/ and prints each recorded change. Use --follow to stream new entries as they are written.
# Stream change-log entries from node-a in real time
cb-wal --node node-a --follow --format compact| Flag | Effect |
|---|---|
--node | Node whose log directory to read |
--follow | Watch the log for growth and stream new entries |
--start-time / --end-time | RFC3339 timestamp filters |
--format | compact or json output |
--info | Show log file metadata |
Follow granularity
--follow checks for new entries frequently, so it is near-realtime. A corrupted entry stops parsing at that point, and the rest of the file is not read.
cb-snapshot: Point-in-Time State
cb-snapshot captures and restores the entire store as a single snapshot file, and can validate, diff, and export snapshots. Snapshots are the foundation of backup and disaster recovery for a node.
# Capture the live store to a snapshot file
cb-snapshot take --output snapshot.db --core-url localhost:9100
# Compare the live store against a snapshot and write a diff report
cb-snapshot report --input snapshot.db --core-url localhost:9100 \
--output diff.txt --format text| Subcommand | Purpose |
|---|---|
take | Capture the current store state to a .db snapshot file |
factory-restore | Restore by recreating the data directory and node (requires --force if targets exist) |
normal-restore | Apply a snapshot to a live store |
report | Generate a diff report (text or JSON) against the live store |
validate | Check snapshot integrity |
export | Export the snapshot to SQL dumps |
Restore is not atomic
Snapshot and factory-restore operations are not atomic. A partial failure can leave the data directory and node structure in an inconsistent state. Verify backups with validate and treat restores as maintenance operations.
Connection & Attribution
All tools share the same connection logic. They read KERNEL_ADDRESS as a comma-separated failover list, connect to the kernel, resolve entity and field types, and enable pagination and CEL filtering. Writes are attributed to CB_USER_ID, so changes made from a Shell session are recorded against the user who made them in the audit trail.
Limitations
- Up to 4 terminal sessions per connection, and up to 20 in total across all users.
- Terminal input is sent in frames of up to 64KB; split large pastes or pipe via file redirects.
- Terminal dimensions range from 4 to 500 rows and columns.
cb-tree --max-depth 0andcb-select --limit 0are unlimited and can be slow or memory-heavy on large data.cb-selectresolves fields one at a time, with no batching across entities.cb-wal --followis near-realtime; a corrupted entry halts parsing at that point.- Restore operations are not atomic; partial failures can leave inconsistent state.
- CLI tools cannot be nested inside other sessions; AI tool spawns replace the shell context.
For deploying a node where these tools run, see the configuration walkthrough. To secure the endpoints the tools connect through, see the certificates guide.