# kmq CLI (/operate/kmq-cli)



`kmq&#x60; is KubeMQ's command-line client — a single static Go binary that talks to the
KubeMQ management API on port &#x2A;*`:8080`** (the same API the web dashboard uses). It is
built to be **agent-native**: predictable output, typed exit codes, and a
self-describing command tree make it a first-class tool for AI coding agents as well as
human operators.

## Overview [#overview]

`kmq` exposes every messaging pattern — Queues, Events, Events Store, and RPC
(Commands/Queries) — plus full observability (status, metrics, connections, audit,
connectors, agents) and a set of meta/discovery commands. Four properties make it
suited to automated and agent-driven use:

* **Token-efficient** — a `kmq queue send` is a few hundred tokens versus a multi-round
  exchange over a protocol like MCP.
* **Deterministic** — typed exit codes and machine-readable `json`/`ndjson` output make
  results easy to branch on in a script or an agent loop.
* **Self-describing** — `kmq schema` emits the whole command tree, offline, with no
  network call.
* **Bounded by default** — every stream/subscribe/replay command honors
  `--count`/`--duration`/`--idle`, so an agent can never loop forever waiting on it.

<Callout type="info" title="Scope">
  `kmq` is a *client of* the management API — it does not embed the broker, and it never
  talks to the message broker directly. Every action is an HTTP call to the `:8080`
  management API. `kmq mcp` reads the server's **registered MCP tools**; it does not make
  `kmq` itself an MCP server.
</Callout>

## Architecture [#architecture]

<Mermaid
  chart="graph TD
    classDef client fill:#F1F5F9,stroke:#475569,color:#0F172A;
    classDef broker fill:#EFF6FF,stroke:#2563EB,stroke-width:2px,color:#0F172A;
    classDef data fill:#F5F5F4,stroke:#57534E,color:#0F172A;

    subgraph AGENT[&#x22;Developer / AI agent&#x22;]
        USER[&#x22;Human or AI agent&#x22;]
        SKILL[&#x22;Installed agent skill&#x22;]
    end

    subgraph KMQ[&#x22;kmq binary&#x22;]
        ROOT[&#x22;Cobra command tree&#x22;]
        MSG[&#x22;Messaging<br/>queue / events / estore / command / query&#x22;]
        OBS[&#x22;Observability<br/>status / overview / metrics / conn / audit / connector / agent / mcp&#x22;]
        META[&#x22;Meta / discovery<br/>schema / cheat / docs / skills / whoami / doctor&#x22;]
        CLIENT[&#x22;HTTP client&#x22;]
        CTX[&#x22;Contexts + config<br/>XDG_CONFIG_HOME/kmq&#x22;]
        EMBED[&#x22;Embedded content (offline)&#x22;]
    end

    API[&#x22;Management API :8080<br/>POST /api/request + REST + SSE/WS&#x22;]

    USER --> ROOT
    SKILL -.teaches.-> USER
    ROOT --> MSG
    ROOT --> OBS
    ROOT --> META
    MSG --> CLIENT
    OBS --> CLIENT
    META --> EMBED
    CLIENT --> CTX
    CLIENT -->|Bearer key when auth on| API

    class USER,SKILL client;
    class API broker;
    class CTX,EMBED data;"
/>

`kmq` never talks to the message broker directly. Every action is an HTTP call to the
`:8080` management API: one-shot actions dispatch via `POST /api/request`
(`{type, data}`) or dedicated REST routes, and streaming commands use Server-Sent
Events / WebSocket subscription endpoints. Meta commands (`schema`, `cheat`, `skills`)
serve content **compiled into the binary**, so they work fully offline and never need
the server.

## Installation [#installation]

```sh
# Primary — GitHub Releases (kubemq-io/kmq), no credentials
curl -sSfL https://raw.githubusercontent.com/kubemq-io/kmq/main/install.sh | sh

# Pin a version, or verify the cosign signature strictly
curl -sSfL https://raw.githubusercontent.com/kubemq-io/kmq/main/install.sh | sh -s -- --version v0.3.1
curl -sSfL https://raw.githubusercontent.com/kubemq-io/kmq/main/install.sh | sh -s -- --verify-signature

# Container image (no install)
podman run --rm europe-docker.pkg.dev/kubemq/images/kmq:latest version
```

`install.sh` detects OS (`linux`/`darwin`/`windows`) and architecture (`amd64`/`arm64`),
resolves the version, downloads the archive plus `checksums.txt`, and performs a
**mandatory SHA-256 verification** — the install aborts if it cannot verify the
checksum. An **optional cosign signature check** runs on top of that (best-effort by
default, strict when requested), and the binary installs to the first writable `PATH`
directory.

| Install env var        | Purpose                                           |
| ---------------------- | ------------------------------------------------- |
| `KMQ_VERSION`          | Explicit version to install (e.g. `v0.3.1`)       |
| `KMQ_INSTALL_DIR`      | Target directory for the `kmq` binary             |
| `KMQ_BASE_URL`         | Mirror/staging base URL (switches to mirror mode) |
| `KMQ_PREFIX`           | Object prefix for a mirror (default `kmq`)        |
| `KMQ_VERIFY_SIGNATURE` | Set to `1` to require a valid cosign signature    |

## Contexts & configuration [#contexts--configuration]

A **context** is a named connection profile (API address, token, TLS, defaults).
Contexts live under `$XDG_CONFIG_HOME/kmq/` (`~/.config/kmq/` by default):

```text
$XDG_CONFIG_HOME/kmq/
├── contexts/
│   ├── default.json
│   └── prod.json
└── current-context      # pointer file (active context name)
```

```sh
kmq context create default --api-address http://localhost:8080
kmq context create prod \
  --api-address https://kubemq.example.com:8080 \
  --token kmq_<keyid>_<secret> --tls
kmq context use prod
kmq context list          # (alias: ls) — current-context marked
kmq context current
kmq context edit prod --token kmq_<newkey>_<newsecret>
kmq context delete staging   # (aliases: rm, remove)
```

Configuration is resolved by precedence, highest to lowest:

| Precedence  | Source                                          |
| ----------- | ----------------------------------------------- |
| 1 (highest) | Persistent flags (`--context`, `--api-address`) |
| 2           | Environment variables                           |
| 3           | Active-context file                             |
| 4 (lowest)  | Built-in defaults (`http://127.0.0.1:8080`)     |

| Env var           | Purpose                                                               |
| ----------------- | --------------------------------------------------------------------- |
| `KMQ_TOKEN`       | Service-account Bearer key (`kmq_<keyid>_<secret>`) — preferred in CI |
| `KMQ_CONTEXT`     | Active context name (overrides the pointer file)                      |
| `KMQ_API_ADDRESS` | Management API URL                                                    |

### Authentication & roles [#authentication--roles]

Authentication is opt-in on the server — see [Security](/configure/reference/security)
for the account model and roles. Check whether it is on with `kmq doctor -o json | jq .auth`
(`on`/`off`). When auth is on, supply a service-account key via `KMQ_TOKEN` or the active
context. Service-account roles gate what the CLI can do:

| Role         | Grants                                                                 |
| ------------ | ---------------------------------------------------------------------- |
| `read_only`  | list/inspect, metrics, status, overview, schema, doctor, billing       |
| `read_write` | the above + send/receive/stream/subscribe/purge, channel create/delete |
| `admin`      | the above + audit, account management                                  |

<Callout type="note" title="Auth-exempt commands">
  A handful of commands and routes succeed with **no token**, even when server auth is
  on: `kmq doctor`, `kmq metrics scrape`, `kmq billing` (routes `/ready`, `/health`,
  `/metrics`, `/billing`, `/api/v1/auth/status`). Don't read the role table above as
  universal gating — these are the exceptions.
</Callout>

`config set`/`revert` and account management require the `admin` role. Service accounts
never carry `admin`, so those operations are deliberate non-goals of the CLI.

## Global flags & output discipline [#global-flags--output-discipline]

Persistent flags are inherited by every subcommand:

| Flag            | Default   | Purpose                                                     |
| --------------- | --------- | ----------------------------------------------------------- |
| `-o, --output`  | `json`    | Output format: `json` \| `ndjson` \| `yaml` \| `table`      |
| `--context`     | —         | Use a specific context (overrides current-context)          |
| `--api-address` | —         | Target `:8080` endpoint (overrides context)                 |
| `--no-color`    | `false`   | Disable color in table output                               |
| `--verbose`     | `false`   | Request timing to stderr (token redacted)                   |
| `--yes`         | `false`   | Confirm destructive operations without prompting            |
| `--dry-run`     | `false`   | Render the action without executing                         |
| `--fields`      | —         | Project output to these camelCase wire fields               |
| `--detail`      | `summary` | `summary` \| `full` verbosity, for commands that support it |

Output discipline:

* **Data → stdout**, warnings/errors/diagnostics → **stderr** — safe to pipe.
* One-shot commands default to compact `json`; **streaming** commands (`queue stream`,
  `*subscribe`, `*replay`, `conn watch`, `command/query receive&#x60;) default to
  &#x2A;*`ndjson`** (one record per line, flushed per record).
* `metrics scrape` emits raw Prometheus text; `cheat` and `skills get` emit raw
  markdown — for those, `-o` is ignored.

```sh
kmq queue receive orders --count 10 -o ndjson | jq .body      # stream, per-line
kmq estore replay telemetry --from-first --count 100 -o json # buffer then array
kmq status --fields is_healthy,channels,clients
```

## Exit codes [#exit-codes]

`kmq` returns typed exit codes so scripts and agents can branch deterministically. On
error it also writes a JSON envelope to stderr: `{"error":{"code":"...","message":"...","retryable":bool}}`.

| Code | Name      | Meaning                                 | Retry?                   |
| ---- | --------- | --------------------------------------- | ------------------------ |
| 0    | OK        | Success                                 | —                        |
| 1    | Generic   | Unclassified error                      | No                       |
| 2    | Usage     | Bad flags / usage                       | No                       |
| 3    | NotFound  | Resource not found                      | No                       |
| 4    | Auth      | Auth required / failed / forbidden      | No                       |
| 5    | Conn      | Server unreachable                      | Yes (server down?)       |
| 6    | Timeout   | Request timed out                       | Yes                      |
| 7    | Partial   | Partial success                         | Case-by-case             |
| 8    | Retryable | Server initializing / too many attempts | Yes — `kmq` auto-retries |

Server wire codes map onto this table as follows: `auth_required`, `auth_failed`,
`forbidden`, `must_change`, and `tls_required` all map to exit code **4**;
`auth_initializing` and `too_many_attempts` map to exit code **8** and are
auto-retried; `seed_read_only`, `duplicate`, and `service_admin_forbidden` map to exit
code **2**; any empty or unknown wire code maps to exit code **1**.

## Command reference [#command-reference]

Every send command reads its body from (in priority) a positional argument →
`--body-base64` → `-f/--file` → stdin, and accepts `--message-id`/`--client-id`/
`--metadata`/`-M`/`--tag k:v` (repeatable). Every stream/subscribe/replay/receive
command accepts the bounding flags `--count N`, `--duration 5m`, `--idle 30s` —
whichever fires first stops it; Ctrl-C also exits cleanly with code 0.

### Messaging [#messaging]

| Pattern                             | Send                                        | Receive / Subscribe                                                | Notes                                                                                                                                                                         |
| ----------------------------------- | ------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Queue](/learn/queues)              | `kmq queue send <ch> <body>`                | `kmq queue receive <ch> [--count N]`                               | persistent, at-least-once                                                                                                                                                     |
| Queue (peek)                        | —                                           | `kmq queue peek <ch> [--count N]`                                  | non-consuming                                                                                                                                                                 |
| Queue (interactive)                 | —                                           | `kmq queue stream <ch> [--visibility 60] [--wait 5] [--auto-ack]`  | WS poll/ack/reject session                                                                                                                                                    |
| Queue (drain)                       | —                                           | `kmq queue purge <ch> --yes`                                       | destructive                                                                                                                                                                   |
| [Events](/learn/events)             | `kmq events send <ch> <body>`               | `kmq events subscribe <ch> [--group g]`                            | fire-and-forget pub/sub                                                                                                                                                       |
| [Events Store](/learn/events-store) | `kmq estore send <ch> <body>`               | `kmq estore subscribe <ch>`                                        | persistent + replayable                                                                                                                                                       |
| Events Store (replay)               | —                                           | `kmq estore replay <ch> <offset-mode>`                             | historical replay — one offset mode: `--new-only` (default), `--from-first`, `--from-last`, `--from-sequence N`, `--from-time <RFC3339>`, `--since-seconds N`, plus `--group` |
| [Command](/learn/rpc) (RPC)         | `kmq command send <ch> <body> --timeout 30` | `kmq command receive <ch> [--respond-body '{}' \| --command 'sh']` | request/ack                                                                                                                                                                   |
| [Query](/learn/rpc) (RPC)           | `kmq query send <ch> <body> --timeout 30`   | `kmq query receive <ch> [--respond-body '{}' \| --command 'sh']`   | request/data                                                                                                                                                                  |

**Queue send** extras: `--max-receive-count`, `--dead-letter <ch>`,
`--expiration-seconds`, `--delay-seconds`.

**RPC responders**: `receive` with `--respond-body '<json>'` echoes a static reply;
with `--command '<shell>'` the inbound body is piped to the command's stdin and its
stdout becomes the reply; with neither, requests are printed for manual handling.
`--respond-body` and `--command` are mutually exclusive.

### Channels [#channels]

```sh
kmq channel create <name> --type queues|events|events_store|commands|queries
kmq channel delete <name> --type queues --yes
kmq channel list [--type <t>]
kmq channel inspect <type> <name>          # full detail: clients, rates, totals
# per-family shortcuts also exist: kmq queue list / kmq queue inspect <name>
```

### Cluster [#cluster]

```sh
kmq cluster info [--node]      # (alias: snapshot) cluster-merged, or --node for local
kmq cluster health            # /ready — leadership role, ready/healthy
kmq cluster nodes             # topology: node, type, unavailable nodes
```

### Observability [#observability]

```sh
kmq status                    # composite digest: /ready + snapshot, one-line health
kmq overview [--detail full]  # per-pattern rollups (queues / pubsub / request-reply / totals)
kmq metrics scrape            # raw Prometheus /metrics (auth-exempt)
kmq metrics history [--metric message_rate|volume_rate|error_rate|messages|bytes]
kmq conn list                 # active connections (first SSE snapshot)
kmq conn inspect <id>
kmq conn watch                # live SSE stream of connection events
kmq audit query  [--from ..] [--to ..] [--event-type queue.send] [--limit N] ...
kmq audit stats  [--group-by event_type|client_id|category|channel|transport]
kmq connector list            # aws / gcp / amqp / amqp10 / stomp / mqtt / ce
kmq connector inspect <name> [--operation <service/op>]
kmq agent list [--limit N] [--offset N]     # A2A agents
kmq agent inspect <id>
kmq mcp list                  # server's registered MCP tools
kmq mcp inspect <tool>
```

### Kafka [#kafka]

```sh
kmq kafka probe --bootstrap host:port[,host:port]   # dial a source cluster, read-only
kmq kafka probe -b host:port --dial-timeout 30s   # override the 15s default
kmq kafka probe -b host:port \
  --aws-access-key <key> --aws-secret-key <secret> --aws-session-token <token>  # MSK IAM
```

`kmq kafka probe` dials a **source** Apache Kafka / MSK / Confluent cluster over the
Kafka wire protocol and reports back the brokers plus their `ApiVersions`. It is
**read-only** — it never produces, commits, or auto-creates topics — so it's safe to
point at a production cluster. Flags: `-b/--bootstrap host:port[,host:port...]`,
`--dial-timeout` (default `15s`), and AWS MSK-IAM auth via
`--aws-access-key`/`--aws-secret-key`/`--aws-session-token`. Run it as a first
connectivity check ahead of the [Migration](#migration) family below, or see
[Migrate from Kafka](/connectors/kafka/how-to/migrate-from-kafka) for the full
workflow.

### Migration [#migration]

| Command                                             | Flags                                      | Notes                                                                                                                                         |
| --------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `kmq assess kafka`                                  | `--bootstrap`, `--tls`, `--sasl-mechanism` | Read-only fit assessment of an external Kafka cluster → per-topic READY/CAVEAT/UNKNOWN/BLOCKED + a T1–T4 verdict. Never writes to the source. |
| `kmq migrate assess\|replicate\|translate\|cutover` | `--state`, `--dry-run`, `--force`          | **Beta** four-phase migration (assess → replicate → translate → cutover) from a Kafka cluster to KubeMQ.                                      |

For the full narrative (per-source auth, MirrorMaker 2 hybrid, rollback, staged dry-run) see
[Migrate from Kafka](/connectors/kafka/how-to/migrate-from-kafka).

### Meta & discovery [#meta--discovery]

```sh
kmq version                   # binary version
kmq whoami                    # identity + role (auth on), or {"authenticated":false,"auth":"disabled"} (auth off)
kmq doctor                    # connectivity + auth check (no auth required)
kmq config get [--fields ..]  # server config (read-only, server-redacted)
kmq billing                   # usage/license (auth-exempt)
kmq schema -o json            # full command tree + roles + exit codes (offline)
kmq cheat [topic]             # embedded recipes (offline)
kmq docs [topic] [--open]     # signpost to the online docs + LLM corpus
kmq skills ...                # serve/install the agent skill (see next section)
```

`kmq cheat` topics (embedded, offline): `queue`, `events`, `estore`, `rpc`, `drain`,
`subscribe`, `health`, `auth`, `context`, `output`.

`kmq schema -o json` is the machine-readable contract — the full command tree (path,
description, required role, flags), the exit-code table, the server wire error-code
catalogue, and the CLI/server versions — with no network and no auth. It is the
recommended way for an agent to introspect the CLI.

## Agent-skill distribution [#agent-skill-distribution]

Any coding agent can be taught to drive `kmq` with one command, and the instructions
always match the installed binary version — the skill content is served straight from
the binary, so it can never go stale between releases.

```sh
kmq skills                    # list skills (alias: kmq skills list)   → 'core'
kmq skills get core           # print the core skill (raw markdown)
kmq skills get core --full    # + the full command reference
kmq skills get --all          # every skill
kmq skills path [name]        # skills source dir, or '(embedded)'
kmq skills install [--global] [--force]   # install the stub locally (zero-Node)
```

| Install path        | Command                                       | Reach                                                                  |
| ------------------- | --------------------------------------------- | ---------------------------------------------------------------------- |
| Universal installer | `npx skills add kubemq-io/kmq`                | 70+ agents (Claude Code, Cursor, Codex, Gemini CLI, Windsurf, …)       |
| Claude Code plugin  | `claude plugin marketplace add kubemq-io/kmq` | Claude Code                                                            |
| Zero-Node fallback  | `kmq skills install`                          | Local Claude Code install; `--global` installs it for the current user |

## Server communication [#server-communication]

`kmq` speaks to the management API over HTTP. One-shot actions dispatch as
`POST /api/request` with `{type, data}` (or dedicated REST routes); the server replies
HTTP 200 with an envelope:

```json
{ "error": false, "error_string": "", "code": "", "data": {} }
```

`error: false` decodes `data` into the requested output; `error: true` maps the `code`
field to an exit code via the wire-code table above. A handful of routes are auth-exempt
(no Bearer needed) — the same ones called out in the **Auth-exempt commands** note above.

Retryable server states (`auth_initializing`, `too_many_attempts`) are auto-retried
with exponential backoff, cancellable by SIGINT/SIGTERM. Streaming uses SSE/WebSocket
subscription endpoints and stops on the first of `--count`/`--duration`/`--idle` or a
signal. SIGINT/SIGTERM cancel the root context — streaming/waiting commands close
gracefully and exit 0 on clean cancellation.

## Related [#related]

<Cards>
  <Card title="Web Dashboard" href="/operate/web-dashboard" description="Same management API, different surface — a visual view over cluster health, channels, and clients." />

  <Card title="Management API" href="/operate/observability/api-reference" description="The HTTP and WebSocket management API on port 8080 behind every kmq command." />

  <Card title="Security" href="/configure/reference/security" description="Control-plane authentication, roles, and the account model kmq's service-account tokens use." />

  <Card title="KubeMQ Aiway" href="/aiway" description="The A2A and MCP agents platform that kmq agent/mcp commands observe, and the agent skill teaches coding agents to drive." />
</Cards>
