Kalam CLI Command Reference
The Kalam CLI (kalam) is the SQL-first terminal client for KalamDB. This guide documents every CLI flag and interactive command currently implemented in ../KalamDB/cli, including live subscriptions, topic consumers, credential workflows, and schema watch automation.
Install and verify
Choose one of these installation options:
Option 1: install.sh (recommended)
curl -fsSL https://kalamdb.org/install.sh | bashOption 2: GitHub release binaries
Use prebuilt binaries from releases if you do not want a Rust toolchain:
Option 3: Build from source
cd clicargo build --release./target/release/kalam --helpAfter installation, verify your CLI:
kalam --helpkalam --versionExecution modes and precedence
kalam supports four execution modes:
--watch-schema: pollsystem.tablesand run a local command when schema metadata changes.--subscribe/--list-subscriptions: subscription management flow.--consume --topic ...: topic consumer mode.- SQL execution mode:
--file <path>executes file and exits--command <sql>executes one statement and exits- no
--file/--commandstarts interactive shell
Important behavior:
--watch-schemais a standalone mode and requires--run.--consumetakes precedence over--fileand--command.--fileand--commandtogether are invalid.
Connection and authentication options
| Option | Description |
|---|---|
-u, --url <URL> | Full server URL or bare host. Bare loopback inputs default to http://; other bare hosts default to https://. |
-H, --host <HOST> | Host-only alternative to --url; combines with --port. |
-p, --port <PORT> | Port used with --host (default 3000). |
--token <JWT> | JWT auth token. |
--user <USER> | User/password login identifier. |
--password [PASS] | Basic auth password. If passed with no value in interactive mode, CLI prompts. |
--instance <NAME> | Credential profile name (default local). |
URL defaults and resolution:
- If
--urlis set, that value is normalized and used. Bare hosts are accepted. - If
--hostis set, URL becomeshttp://<host>:<port>. - Otherwise CLI uses stored credentials URL for the selected
--instancewhen present. - Final fallback is
http://localhost:2900. - Bare
--urlinputs likelocalhost:2900,127.0.0.1:2900, and[::1]:2900default tohttp://...; other bare hosts likekalam.masky.appdefault tohttps://.... --urlrejects embedded credentials, query parameters, and fragments.
Query execution and output options
| Option | Description |
|---|---|
-c, --command <SQL> | Execute a single SQL statement and exit. |
-f, --file <PATH> | Execute SQL from file and exit. |
--format <table|json|csv> | Output format (default table). |
--json | Shorthand for --format json. |
--csv | Shorthand for --format csv. |
--no-color | Disable ANSI colors. |
--no-spinner | Disable spinner animations. |
--loading-threshold-ms <MS> | Spinner threshold in milliseconds. |
Examples:
kalam --command "SELECT * FROM system.tables LIMIT 5;"kalam --command "SELECT * FROM system.tables LIMIT 5;" --jsonkalam --file ./setup.sql --csvkalam --url kalam.masky.app --command "SELECT 1;"Running SQL from the CLI
The CLI is SQL-first: if a statement works through /v1/api/sql, you can send
it unchanged through kalam --command, kalam --file, or the interactive
shell.
Common patterns:
# Read system metadatakalam -c "SELECT * FROM system.tables ORDER BY namespace_id, table_name LIMIT 20;" # DDL / DMLkalam -c "CREATE NAMESPACE chat;"kalam -c "CREATE TABLE chat.messages (id BIGINT PRIMARY KEY, body TEXT) WITH (TYPE='USER');"kalam -c "INSERT INTO chat.messages (id, body) VALUES (1, 'hello');" # Run a whole SQL filekalam -f ./migrations/001-init.sqlNamespace state in interactive mode
Namespace switching is a SQL statement, not a backslash command:
USE NAMESPACE chat;SELECT * FROM messages ORDER BY id DESC LIMIT 20;\flush table messagesAfter a successful USE NAMESPACE chat, SET NAMESPACE chat, or USE chat,
the CLI stores chat locally and sends it as namespace_id on later requests.
That means later unqualified table names resolve to chat.<table> until you
switch again. The prompt also shows the active namespace as ns:<name>.
Table footer metadata
When output format is table, query results end with row count and footer metadata:
(50 rows)As: aliceTook: 2.146 msAs:appears when the server reports the effective user for the statement. This includesEXECUTE AS '<user_id>'and the CLI\asshortcut.Took:is the server-reported execution time in milliseconds.
Interactive meta-commands
In interactive mode, type SQL directly or use backslash meta-commands for CLI-managed operations. --command also accepts these meta-commands, so cluster inspection and administration can stay on the CLI surface instead of requiring backend-rendered strings.
Cluster meta-commands
Core cluster commands:
| Command | Description |
|---|---|
\cluster list / \cluster ls | Render node overview from system.cluster and system.cluster_groups. |
\cluster list groups | Render Raft group details. |
\cluster snapshot | Trigger cluster snapshots and print per-group results. |
\cluster purge --upto <index> | Purge Raft logs up to index. |
\cluster trigger-election | Trigger elections across groups. |
\cluster transfer-leader <node_id> | Request leader transfer to a node. |
\cluster rebalance | Rebalance data-group leaders. |
\cluster stepdown | Request leader stepdown across groups. |
\cluster clear | Clear older snapshot files. |
\cluster join <node_id> <rpc_addr> <api_addr> | Add a node at runtime. |
Examples:
kalam --command "\\cluster list"kalam --command "\\cluster list groups"kalam --command "\\cluster rebalance"kalam --command "\\cluster join 2 10.0.0.2:2910 http://10.0.0.2:2900"How follower writes are forwarded
KalamDB uses Multi-Raft groups, so a client can send a write to any reachable node.
Example:
kalam --url http://node-2:2900 --command "INSERT INTO app.messages (id, body) VALUES (101, 'hello')"Assume the authenticated or effective user is user-42, and that user hashes to DataUserShard(7).
- The request can land on node 2 even if node 2 is only a follower for
DataUserShard(7). - KalamDB prepares and classifies the SQL once, then derives the target Raft group from the table type and current
user_id. - For user and stream tables, the target group is selected by hashing
user_idinto one ofcluster.user_shardsgroups. - If the receiving node is not leader for that target group, it forwards the original SQL, params, auth header, and request id over gRPC to the leader of that group.
- The leader executes the write, appends it to that group’s Raft log, replicates it to followers, commits it, and returns the result.
- The follower returns that leader-built response to the client.
This means the client does not need to discover the right leader first.
Multi-Raft routing today
- KalamDB runs one metadata Raft group plus multiple user data groups.
- User and stream data are routed by
user_id, so all data for one user is coordinated by the same user-data group leader at a given time instead of scattering that user’s active writes across many leaders. - That locality keeps the cluster faster by reducing cross-group coordination and improving write-path locality.
- Shared tables are not meaningfully sharded yet. Today they route to a single shared data group.
- Better shared-table partitioning is a work in progress. The planned direction is partition-by-key so each shared table can define how rows are partitioned and where they belong.
Credential and instance management
| Option | Description |
|---|---|
--list-instances | List stored credential instances. |
--show-credentials | Show stored credentials for --instance. |
--update-credentials | Login and refresh stored JWT/refresh token for --instance. |
--delete-credentials | Delete credentials for --instance. |
--save-credentials | Save credentials after successful login when using user/password flow. |
Examples:
kalam --list-instanceskalam --show-credentials --instance devkalam --update-credentials --instance dev --url http://localhost:2900 --user adminkalam --delete-credentials --instance devLive query subscription options
| Option | Description |
|---|---|
--subscribe <SQL> | Run a live query subscription. |
--subscription-timeout <SECONDS> | Idle timeout after initial data (0 means no timeout). |
--initial-data-timeout <SECONDS> | Maximum wait for initial data batch (0 means no timeout). |
--list-subscriptions | Print current subscription model/capabilities. |
The CLI live-query surface is \live <SELECT ...> in interactive mode and --subscribe "<SELECT ...>" in non-interactive mode. Both routes send a SELECT plus an optional trailing OPTIONS (...) clause to the WebSocket subscription path.
Supported CLI subscription options today:
| Clause | Effect |
|---|---|
OPTIONS (batch_size=<n>) | Limits each initial snapshot batch to at most n rows. If more rows match, the CLI keeps requesting the next batch until startup loading completes. |
OPTIONS (last_rows=<n>) | Rewinds the newest n rows as a single startup batch before live changes begin. |
OPTIONS (from=<seq_id>) | Starts live delivery after a known sequence id. from_seq_id is accepted as an alias. |
Options can be combined in one clause, for example OPTIONS (last_rows=20, batch_size=5, from=1234).
Examples:
kalam --subscribe "SELECT * FROM app.messages WHERE conversation_id = 7 OPTIONS (batch_size=5)"kalam --subscribe "SELECT * FROM app.messages OPTIONS (last_rows=20)" --subscription-timeout 15kalam --subscribe "SELECT * FROM app.messages OPTIONS (last_rows=20, batch_size=5, from=1234)"Interactive examples:
kalam> \live SELECT * FROM app.messages OPTIONS (batch_size=5);kalam> \subscribe SELECT * FROM app.messages OPTIONS (last_rows=20);kalam> \live SELECT * FROM app.messages OPTIONS (last_rows=20, batch_size=5, from_seq_id=1234);How to verify batching from the CLI output:
- The CLI prints a
BATCH <n>line for each startup snapshot page. - With
OPTIONS (batch_size=5)and 20 matching rows, you should see four startup batches of 5 rows each. - Startup batches are delivered from oldest to newest in normal batch mode.
last_rowsis different: it is a single-batch rewind of the newest rows, not paginated history replay.
Example output:
[12:00:00.123] ✓ SUBSCRIBED [sub_...] 0 total rows, batch 1 (loading...), 3 columns[12:00:00.124] BATCH 1 [sub_...] 5 rows (more pending) {"id":1,"message":"hello 1"} {"id":2,"message":"hello 2"}[12:00:00.130] BATCH 2 [sub_...] 5 rows (more pending)...[12:00:00.145] BATCH 4 [sub_...] 5 rows (complete)Topic consumer options (--consume)
Use this mode to consume topic records directly from a terminal.
| Option | Description |
|---|---|
--consume | Enable topic consumer mode. |
--topic <TOPIC> | Topic name (required with --consume). |
--group <GROUP_ID> | Consumer group (offsets committed when set). |
--from <earliest|latest|OFFSET> | Start position. Supports numeric offset. |
--consume-limit <N> | Maximum number of messages before exit. |
--consume-timeout <SECONDS> | Stop after idle runtime window. |
Examples:
# Read newest events and keep runningkalam --consume --topic blog.summarizer --from latest # Replay from earliest with bounded runkalam --consume --topic blog.summarizer --group summarizer-agent --from earliest --consume-limit 50 --consume-timeout 60Schema watch options (--watch-schema)
Use this mode when your app has a local schema-generation step such as npm run schema:gen and you want the CLI to rerun it after DDL changes.
| Option | Description |
|---|---|
--watch-schema | Start schema watch mode. |
--namespace <NAME> | Limit changes to one or more namespaces. Repeat it to watch multiple namespaces. |
--table <namespace.table> | Limit changes to one or more specific tables. Repeat it to watch multiple tables. |
--run <COMMAND> | Shell command to execute after schema changes are detected. |
--run-on-start | Run the command once immediately before polling. |
--interval <DURATION> | Poll interval. Default 5s; accepts values like 500ms, 2s, or 1m. |
Examples:
# Watch all non-system schemaskalam --watch-schema --run "npm run schema:gen" # Watch one namespacekalam --watch-schema --namespace chat --run "npm run schema:gen" # Watch multiple namespaceskalam --watch-schema --namespace chat --namespace billing --run "npm run schema:gen" # Watch a specific tablekalam --watch-schema --table chat.messages --run "npm run schema:gen" # Run once immediately, then poll every 5skalam --watch-schema --namespace chat --run "npm run schema:gen" --run-on-start # Faster pollingkalam --watch-schema --namespace chat --run "npm run schema:gen" --interval 2sInternally the CLI polls system.tables and checks for rows whose updated_at is newer than the last observed watch timestamp. Repeated namespaces are combined with OR, and repeated tables are added as extra OR branches.
Timeout and runtime tuning options
| Option | Description |
|---|---|
--timeout <SECONDS> | HTTP request timeout (default 30). |
--connection-timeout <SECONDS> | Connection/TLS handshake timeout (default 10). |
--receive-timeout <SECONDS> | Receive timeout (default 30). |
--auth-timeout <SECONDS> | WebSocket auth timeout (default 5). |
--fast-timeouts | Preset tuned for local development. |
--relaxed-timeouts | Preset tuned for high-latency networks. |
--config <PATH> | Config file path (default ~/.kalam/config.toml). |
-v, --verbose | Verbose logging. |
Global utility options
| Option | Description |
|---|---|
-h, --help | Print command help. |
-V, --version | Print CLI version, commit, branch, and build timestamp. |
Interactive shell commands by category
After launching interactive mode (kalam with no --command/--file), these backslash commands are available.
Core shell and inspection
| Command | Description |
|---|---|
\help, \? | Show help. |
\quit, \q | Exit CLI. |
\info, \session | Show current CLI session, resolved server, health probe status, server version, cluster, and config details. |
\sessions | Show active PostgreSQL gRPC bridge sessions from system.sessions. |
\history, \h | Open command history menu. |
\health | Run unauthenticated public health probes. |
\stats, \metrics | Query system stats metrics. |
Session introspection
Use \session (or \info) to inspect the current CLI process and server connection: resolved URL, user, connectivity, health probe status, server version, API version, build date, cluster mode, config file, local history, and build metadata.
\health uses the same public /health and /v1/api/healthcheck endpoints documented in the HTTP reference. It does not fall back to authenticated SQL. On remote deployments those endpoints may be localhost-only; in that case the CLI reports the restriction, while \session can still show version/build details learned from cluster metadata.
Use \sessions when you want server-side observability for PostgreSQL bridge traffic. It executes:
SELECT *FROM system.sessionsORDER BY last_seen_at DESC, session_id;That view is focused on live pg_kalam gRPC sessions only. To inspect active explicit transactions across both pg_kalam and native /v1/api/sql requests, query:
SELECT transaction_id, owner_id, origin, state, write_countFROM system.transactionsORDER BY origin, transaction_id;See System Views for the view columns and query patterns.
Namespace, schema, impersonation, and formatting
| Command | Description |
|---|---|
\dt, \tables | List tables. |
\d <table>, \describe <table> | Describe table columns. Accepts <table> or <namespace.table> and ignores a trailing ;. |
\as <user_id> <SQL> | Run one statement as a convenience wrapper for EXECUTE AS '<user_id>'. |
\format table|json|csv | Change output format for current session. |
\refresh-tables, \refresh | Refresh autocomplete table metadata. |
Example:
USE NAMESPACE chat;SELECT * FROM messages WHERE conversation_id = 42;\flush table messages\as user_123 SELECT * FROM app.messages WHERE conversation_id = 42;\flush is a shortcut for STORAGE FLUSH:
\flushor\flush allrunsSTORAGE FLUSH ALLusing the current namespace context.\flush table messagesrunsSTORAGE FLUSH TABLE chat.messageswhen the current namespace ischat.\flush table billing.invoicespreserves the explicit namespace you typed.
The CLI rewrites that shortcut to the canonical SQL wrapper:
EXECUTE AS 'user_123' ( SELECT * FROM app.messages WHERE conversation_id = 42)See Execute As for the delegation rules and role matrix.
Credentials in interactive mode
| Command | Description |
|---|---|
\show-credentials, \credentials | Show stored credentials for current instance. |
\update-credentials <user> <pass> | Update credentials for current instance. |
\delete-credentials | Delete credentials for current instance. |
Live query commands
| Command | Description |
|---|---|
\live <SQL>, \subscribe <SQL> | Start live query subscription (\subscribe is an alias). |
Topic consumer command (interactive)
| Command | Description |
|---|---|
\consume <topic> [--group NAME] [--from earliest|latest|OFFSET] [--limit N] [--timeout SECONDS] | Consume topic messages directly from interactive shell. |
Example:
\consume blog.summarizer --group summarizer-agent --from earliest --limit 25 --timeout 45Cluster and ingest control commands
The \cluster meta-command renders cluster operations directly from the CLI surface.
| Command | Description |
|---|---|
\flush [all|table <table>] | Run STORAGE FLUSH using the current namespace context. |
\cluster snapshot | Trigger cluster snapshot. |
\cluster purge --upto <index> or \cluster purge <index> | Purge cluster logs up to index. |
\cluster trigger-election or \cluster trigger election | Trigger election. |
\cluster transfer-leader <node_id> or \cluster transfer leader <node_id> | Request leadership transfer to node id; some builds may report it unsupported at runtime. |
\cluster rebalance | Rebalance leaders across data groups. |
\cluster stepdown or \cluster step-down | Step down current leader. |
\cluster clear | Clear older snapshot files while keeping recent ones. |
\cluster list or \cluster ls | Show cluster nodes (system.cluster). |
\cluster list groups | Show cluster groups (system.cluster_groups). |
\cluster join <node_id> <rpc_addr> <api_addr> | Add a node at runtime. |
SQL workflows the CLI should cover
The backslash commands are only part of the surface. Many important CLI
workflows are plain SQL entered through -c, -f, or the interactive shell.
Impersonation
You can use either the \as shortcut or the raw SQL form:
EXECUTE AS 'user_123' ( SELECT * FROM app.messages ORDER BY id DESC LIMIT 20);Backup and user export
Run backup and export workflows as normal SQL:
BACKUP DATABASE TO '/tmp/kalamdb-backup.tar.gz';EXPORT USER DATA;SHOW EXPORT;Live queries
The CLI live-query commands take a normal SELECT and run it over the live
subscription channel:
kalam --subscribe "SELECT * FROM app.messages WHERE conversation_id = 7"\subscribe SELECT * FROM app.messages WHERE conversation_id = 7There is no separate live-query SQL keyword to learn for the CLI path; use the
same SELECT you would run normally.
Export user data
EXPORT USER DATA;SHOW EXPORT;SHOW EXPORT returns job rows with a download_url URI path such as
/v1/exports/<user_id>/<export_id>. Prefix that path with the same server base
URL you used for the CLI when downloading the finished ZIP.
Backup and restore
BACKUP DATABASE TO '/var/backups/kalamdb-nightly.tar.gz';RESTORE DATABASE FROM '/var/backups/kalamdb-nightly.tar.gz';Backup and restore paths are resolved on the server filesystem, not on the machine running the CLI.
- If
BACKUP DATABASE TOends with.tar.gzor.tgz, KalamDB writes a single archive file. - If it does not, KalamDB writes the backup directory layout under that path.
RESTORE DATABASE FROMaccepts either the directory layout or a.tar.gz/.tgzarchive.- Backup and restore require a DBA or System role.
- There is no separate backup download endpoint today; the
TO '<path>'value is the server-side artifact location. - To track the status of the backup you can query the jobs table: select * from system.jobs where job_id = ‘backupjob’;
Topic pub/sub SQL
Use SQL for topic definition, CDC wiring, and explicit offset control:
CREATE TOPIC app.new_messages PARTITIONS 4;ALTER TOPIC app.new_messages ADD SOURCE app.messages ON INSERT WITH (payload = 'full');CONSUME FROM app.new_messages GROUP 'worker-1' FROM EARLIEST LIMIT 100;ACK app.new_messages GROUP 'worker-1' UPTO OFFSET 99;DROP TOPIC app.new_messages;The --consume flag and \consume command are convenience shells around topic
consumption. Use raw CONSUME FROM and ACK when you want explicit SQL-based
control or when you are scripting the workflow.
Quick command map
# Interactive shellkalam # One-shot SQLkalam -c "SELECT * FROM system.tables LIMIT 5;" # File executionkalam -f ./queries.sql # Live subscriptionkalam --subscribe "SELECT * FROM app.messages" # Start in one namespace, then use unqualified table names interactivelykalam# inside the shell:# USE NAMESPACE chat;# SELECT * FROM messages;# \flush table messages # Export user data and inspect download pathskalam -c "EXPORT USER DATA;"kalam -c "SHOW EXPORT;" --json # Server-side backup archivekalam -c "BACKUP DATABASE TO '/var/backups/kalamdb-nightly.tar.gz'" # Restore from a server-side archivekalam -c "RESTORE DATABASE FROM '/var/backups/kalamdb-nightly.tar.gz'" # Topic consumerkalam --consume --topic blog.summarizer --group summarizer-agent --from latest # Regenerate local Drizzle schema on DDL changeskalam --watch-schema --namespace app --run "npm run schema:gen" --run-on-start