mqgov-cli 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +97 -15
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Governed message-broker operations for humans _and_ AI agents.**
6
6
 
7
- One safe command line for **Kafka**, **RabbitMQ**, **Pulsar**, and **RocketMQ** — list, describe, peek, produce, reset offsets, purge, and delete topics without ever fat-fingering production or silently draining a queue.
7
+ One safe command line for **Kafka**, **RabbitMQ**, **Pulsar**, and **RocketMQ** — list, describe, peek, tail, produce, govern DLQs, reset offsets, inspect ACLs and schemas, purge, and delete topics without ever fat-fingering production or silently draining a queue.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/mqgov-cli.svg)](https://www.npmjs.com/package/mqgov-cli)
10
10
  [![CI](https://github.com/JiangHe12/mqgov-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/JiangHe12/mqgov-cli/actions/workflows/ci.yml)
@@ -25,7 +25,7 @@ Message brokers — **Kafka**, **RabbitMQ**, **Pulsar**, **RocketMQ** — are th
25
25
 
26
26
  - 🔎 **Shows you the blast radius first** — `--dry-run` / `--plan` print the exact per-partition impact (how many messages an offset reset will replay or skip) before anything happens.
27
27
  - 🛡️ **Refuses to do something dangerous without explicit sign-off** — risky commands need a confirmation flag, a change ticket, and an explicit `--allow-*` for the operation.
28
- - 👀 **Peeks without consuming** — inspecting messages never advances a consumer's position or drains a queue.
28
+ - 👀 **Peeks/tails without consuming** — inspecting or streaming message fingerprints never advances a consumer's position or drains a queue.
29
29
  - 📜 **Records everything in a tamper-evident audit log** — sha256 fingerprints and counts only, **never your message bodies**.
30
30
  - 🤖 **Is safe to hand to an AI agent** — the agent can read and preview freely, but **cannot** invent the human approvals required for dangerous actions.
31
31
 
@@ -38,10 +38,10 @@ It's built on the shared [`opskit-core`](https://github.com/JiangHe12/opskit-cor
38
38
  | | |
39
39
  |---|---|
40
40
  | 📨 **Four brokers** | **Kafka** (franz-go), **RabbitMQ** (AMQP + management API), **Pulsar** (client + admin REST), **RocketMQ** (rocketmq-client-go/v2). One backend-agnostic governance model; pick per context or override per command. |
41
- | 🧱 **topic / group / message** | topics: list · describe · create · alter · delete · purge. consumer groups: list · lag · reset-offset. messages: non-destructive peek · produce. |
41
+ | 🧱 **topic / group / message / dlq / acl / schema / fleet** | topics: list · describe · create · alter · delete · purge. consumer groups: list · lag · reset-offset. messages: non-destructive peek · tail · produce. DLQs: list · peek · redrive · purge through native broker models. ACLs: list · grant · revoke where supported. Schemas: list · describe · check where native schema registry support exists. Fleet: read-only status and topic inventory across configured contexts. |
42
42
  | 🔐 **R0–R3 governance** | every operation is risk-classified by the fail-closed `mqclass` engine; protected contexts and internal/system topics escalate one tier; AI callers can never self-authorize. |
43
43
  | 🎯 **Real blast-radius preview** | `reset-offset --dry-run` and `purge --dry-run` compute the actual per-partition message delta from the live broker — no guessing. The preview is read-only and never mutates. |
44
- | 👀 **Non-destructive peek** | inspect messages as fingerprints without consuming them or moving any cursor (Pulsar Reader, RabbitMQ get+requeue). Where a broker can't guarantee this, peek fails closed rather than silently consuming. |
44
+ | 👀 **Non-destructive peek/tail** | inspect or stream messages as fingerprints without consuming them or moving any cursor (Kafka direct partition reads, Pulsar Reader, RabbitMQ get+requeue for peek only). Where a broker can't guarantee this, the operation fails closed rather than silently consuming. |
45
45
  | 🧭 **Honest capabilities** | brokers differ — mqgov reports what each one actually supports (`capabilities -o json`) and **fails closed with `NOT_IMPLEMENTED`** for the rest, never faking it. |
46
46
  | 📜 **Tamper-evident audit** | hash-chained log of every action (sha256 fingerprints + counts, **no message bodies/keys/headers**); `audit verify` detects tampering. |
47
47
  | 🩺 **Ops & DX** | backend-bound `ctx` contexts with credstore-backed secrets, `doctor` diagnostics, shell `completion`, OpenTelemetry traces/metrics, JSON output everywhere. |
@@ -54,11 +54,17 @@ It's built on the shared [`opskit-core`](https://github.com/JiangHe12/opskit-cor
54
54
  | topic list / describe / create / delete | ✅ | ✅ | ✅ | ✅ |
55
55
  | produce | ✅ | ✅ | ✅ | ✅ |
56
56
  | **non-destructive peek** | ✅ | ✅ (Reader) | ✅ (get+requeue) | ❌ `NOT_IMPLEMENTED`¹ |
57
+ | **non-destructive tail** | ✅ | ✅ (Reader) | ❌ `NOT_IMPLEMENTED`² | ❌ `NOT_IMPLEMENTED`¹ |
57
58
  | **offset lag / reset** | ✅ | ✅ (cursor) | ❌ (no offsets) | ❌ |
58
59
  | alter partitions | ✅ | ✅ | ❌ | ❌ |
59
60
  | purge | ✅ | ✅ | ✅ | ❌ |
61
+ | **DLQ list / peek / redrive / purge** | list ❌; explicit topic peek/redrive/purge ✅ | ✅ `{topic}-{subscription}-DLQ` | ✅ DLX queues | list ✅ `%DLQ%group`; others ❌ |
62
+ | **ACL list / grant / revoke** | ✅ | ✅ namespace/topic permissions | ✅ user-vhost permissions | ❌ `NOT_IMPLEMENTED`³ |
63
+ | **schema list / describe / check** | ✅ Confluent Schema Registry | ✅ built-in admin schema API | ❌ `NOT_IMPLEMENTED` | ❌ `NOT_IMPLEMENTED` |
60
64
 
61
- ¹ RocketMQ's Go v2 `PullConsumer` enters the consumer-group lifecycle and commits offsets, so it cannot guarantee a non-destructive peek — mqgov fails closed instead of silently advancing offsets. Unsupported operations always return `NOT_IMPLEMENTED` (exit 12), never a fake success.
65
+ ¹ RocketMQ's Go v2 `PullConsumer` enters the consumer-group lifecycle and commits offsets, so it cannot guarantee non-destructive peek/tail — mqgov fails closed instead of silently advancing offsets. ² RabbitMQ has no forward non-destructive tail because reads are consume/requeue oriented. Unsupported operations always return `NOT_IMPLEMENTED` (exit 12), never a fake success.
66
+
67
+ ³ RocketMQ broker ACLs live in broker-side `plain_acl.yml`, but `rocketmq-client-go/v2` does not expose a public, cgo-free admin API for reading or changing that config. mqgov does not shell out to the Java `mqadmin` tool and does not hand-roll remoting commands; manage RocketMQ ACLs out of band with broker configuration or official mqadmin until the Go client exposes a clean API.
62
68
 
63
69
  ---
64
70
 
@@ -100,6 +106,7 @@ mqgov ctx test # ping the broker through the context
100
106
  mqgov topic list -o json
101
107
  mqgov topic describe orders -o json
102
108
  mqgov message peek orders --count 5 -o json # fingerprints only, nothing consumed
109
+ mqgov message tail orders --max-messages 10 -o json
103
110
 
104
111
  # 3. Preview the blast radius of a dangerous op — nothing is changed yet
105
112
  mqgov group reset-offset billing orders --to latest --dry-run -o json # shows per-partition delta
@@ -121,19 +128,19 @@ Every command is sorted into one of four **risk tiers** by the fail-closed `mqcl
121
128
 
122
129
  | Tier | What it covers | What you must provide |
123
130
  |:---:|---|---|
124
- | **R0** | Reads & previews (`topic list/describe`, `group list/lag`, `message peek`, `*-dry-run`, `audit query/verify`, `doctor`) | Nothing — but it's still audited |
131
+ | **R0** | Reads & previews (`topic list/describe`, `group list/lag`, `message peek`, `message tail`, `dlq list/peek`, `acl list`, `schema list/describe/check`, `fleet status/topics`, `*-dry-run`, `audit query/verify`, `doctor`) | Nothing — but it's still audited |
125
132
  | **R1** | Ordinary writes (`message produce`, `topic create`) | `--yes` (or an interactive confirmation) |
126
- | **R2** | Elevated mutations (`topic alter`, `group create/delete`, produce to a **protected** topic) | `--yes` **and** a non-empty `--ticket` |
127
- | **R3** | Destructive / irreversible (`group reset-offset`, `topic purge`, `topic delete`, produce to an **internal/system** topic) | The above **plus** the exact `--allow-*` flag |
133
+ | **R2** | Elevated mutations (`topic alter`, `group create/delete`, `acl grant`, produce to a **protected** topic) | `--yes` **and** a non-empty `--ticket` |
134
+ | **R3** | Destructive / irreversible (`group reset-offset`, `topic purge`, `topic delete`, `dlq redrive`, `dlq purge`, broad `acl grant`, `acl revoke`, produce to an **internal/system** topic) | The above **plus** the exact `--allow-*` flag |
128
135
 
129
- The R3 allow flags: `--allow-offset-reset`, `--allow-topic-purge`, `--allow-topic-delete`, `--allow-internal-produce`.
136
+ The R3 allow flags: `--allow-offset-reset`, `--allow-topic-purge`, `--allow-topic-delete`, `--allow-destructive-acl`, `--allow-internal-produce`.
130
137
 
131
138
  **Protected contexts, protected topics, and internal/system topics raise the tier by one.** For example, producing to `__consumer_offsets` is treated as a destructive R3 operation and needs `--allow-internal-produce`.
132
139
 
133
140
  Three rules keep this safe — especially for automation:
134
141
 
135
142
  1. **Blast radius comes from the tool, not a guess.** Use `--dry-run` / `--plan` to see the exact per-partition impact. Never estimate it by reasoning.
136
- 2. **`mqclass` is fail-closed and structure-aware.** All offset changes, purge, and delete are pinned R3; wildcard/glob targets escalate; an unknown operation fails closed to the highest tier — it never falls to R0.
143
+ 2. **`mqclass` is fail-closed and structure-aware.** All offset changes, purge, topic delete, ACL revoke, and broad ACL grants are pinned R3; wildcard/glob targets escalate; an unknown operation fails closed to the highest tier — it never falls to R0.
137
144
  3. **🤖 AI agents must never invent `--ticket`, `--allow-*`, or a high-risk `--yes`.** Those are *human* authorization inputs. An agent should surface "this needs approval X" to its operator and stop.
138
145
 
139
146
  ---
@@ -179,14 +186,89 @@ Offsets are a Kafka and Pulsar concept. On RabbitMQ and RocketMQ, `group lag` /
179
186
  </details>
180
187
 
181
188
  <details>
182
- <summary><b>message</b> — peek & produce</summary>
189
+ <summary><b>message</b> — peek, tail & produce</summary>
183
190
 
184
191
  ```bash
185
192
  mqgov message peek <topic> [--partition N] [--offset N] [--count N] -o json # R0, non-destructive, fingerprints only
193
+ mqgov message tail <topic> [--partition N] [--from earliest|latest|offset:N] [--follow] [--max-messages N] [--timeout 30s] -o json
186
194
  mqgov message produce <topic> [--key <k>] [--body <text>] --yes # R1 (R3 + --allow-internal-produce for internal topics)
187
195
  ```
188
196
 
189
- `peek` never consumes a message or moves a cursor, and returns only sha256 fingerprints (`keySha256`, `bodySha256`, size) — never the body. On RocketMQ, `peek` fails closed (`NOT_IMPLEMENTED`).
197
+ `peek` and `tail` never consume a message or move a cursor, and return only sha256 fingerprints (`keySha256`, `bodySha256`, size, optional timestamp) — never the body. `tail` is bounded by `--max-messages` and `--timeout`; `--follow` streams new messages only until those bounds or cancellation. Tail is supported by Kafka and Pulsar. On RabbitMQ and RocketMQ, `tail` fails closed (`NOT_IMPLEMENTED`); on RocketMQ, `peek` also fails closed.
198
+ </details>
199
+
200
+ <details>
201
+ <summary><b>dlq</b> — dead-letter queue governance</summary>
202
+
203
+ ```bash
204
+ mqgov dlq list [--topic <source-or-dlq>] [--group <group-or-sub>] [--pattern <name|glob>] -o json # R0
205
+ mqgov dlq peek <dlq> [--topic <source>] [--group <group-or-sub>] [--count N] -o json # R0, fingerprints only
206
+ mqgov dlq redrive <dlq> --target <live-topic> [--count N] --dry-run -o json # R0 preview
207
+ mqgov dlq redrive <dlq> --target <live-topic> [--count N] --yes --ticket <t> --allow-internal-produce # R3
208
+ mqgov dlq purge <dlq> --dry-run -o json # R0 preview
209
+ mqgov dlq purge <dlq> --yes --ticket <t> --allow-topic-purge # R3
210
+ ```
211
+
212
+ DLQ mapping is backend-native and honest: RocketMQ lists `%DLQ%{consumerGroup}` topics only; RabbitMQ treats DLQs as ordinary queues fed by a dead-letter exchange; Kafka has no native DLQ and never auto-discovers one, so use an explicit DLQ topic for peek/redrive/purge; Pulsar uses `{topic}-{subscription}-DLQ`. Unsupported verbs return `NOT_IMPLEMENTED`.
213
+
214
+ Redrive is governed as internal produce: dry-run is a read-only preview and real execution requires `--allow-internal-produce`. Audit remains fingerprint/count-only and never stores message body, key, or headers.
215
+ </details>
216
+
217
+ <details>
218
+ <summary><b>schema</b> — schema registry</summary>
219
+
220
+ ```bash
221
+ mqgov schema list [--pattern <subject>] -o json
222
+ mqgov schema describe <subject-or-topic> [--version latest|N] -o json
223
+ mqgov schema check <subject-or-topic> --schema-file ./next.avsc --schema-type AVRO [--version latest] -o json
224
+ ```
225
+
226
+ `schema list`, `schema describe`, and `schema check` are R0 and audited. `check` uses read-only compatibility endpoints and never registers, deletes, or evolves a schema. Kafka maps to Confluent Schema Registry (`GET /subjects`, `GET /subjects/{subject}/versions`, `GET /subjects/{subject}/versions/{version|latest}`, and `POST /compatibility/subjects/{subject}/versions/{version}`). Pulsar maps to its built-in admin schema endpoints under `/admin/v2/schemas/{tenant}/{namespace}/{topic}`. RabbitMQ and RocketMQ fail closed with `NOT_IMPLEMENTED`. Audit stores only subject/version metadata and schema hashes, never schema text or registry credentials.
227
+ </details>
228
+
229
+ <details>
230
+ <summary><b>fleet</b> — cross-context read-only views</summary>
231
+
232
+ ```bash
233
+ mqgov fleet status --all -o json
234
+ mqgov fleet topics --contexts dev,staging --pattern orders -o json
235
+ ```
236
+
237
+ `fleet status` fans out `Ping`, `Describe`, and `Capabilities` across selected contexts. `fleet topics` fans out topic listing and tags every row with its source context. Select contexts with exactly one of `--all` or `--contexts a,b,c`. Fleet is R0 only: each per-context read still runs through the same R0 classification and authorization path as a single-context command, using that context's own stored credentials. Partial failures are reported per context as `denied`, `unreachable`, or `error` data and the command still exits 0.
238
+ </details>
239
+
240
+ <details>
241
+ <summary><b>acl</b> — broker access control</summary>
242
+
243
+ ```bash
244
+ mqgov acl list [--principal <P>] [--resource-type <T>] [--resource-name <N>] -o json
245
+
246
+ # Kafka broker ACLs
247
+ mqgov acl grant --principal User:svc --resource-type topic --resource-name orders \
248
+ --pattern literal --operation read --permission allow --yes --ticket <t>
249
+
250
+ mqgov acl revoke --principal User:svc --resource-type topic --resource-name orders \
251
+ --pattern literal --operation read --permission allow \
252
+ --yes --ticket <t> --allow-destructive-acl
253
+
254
+ # RabbitMQ native user-vhost permissions
255
+ mqgov acl grant --principal svc --vhost / --resource-type vhost --resource-name '^orders$' \
256
+ --pattern regex --operation read --permission allow --yes --ticket <t>
257
+
258
+ mqgov acl revoke --principal svc --vhost / --resource-type vhost --resource-name '^orders$' \
259
+ --pattern regex --operation read --permission allow \
260
+ --yes --ticket <t> --allow-destructive-acl
261
+
262
+ # Pulsar native namespace/topic permissions
263
+ mqgov acl grant --principal app-role --resource-type namespace --resource-name public/default \
264
+ --pattern literal --operation produce --permission allow --yes --ticket <t>
265
+
266
+ mqgov acl revoke --principal app-role --resource-type topic --resource-name orders \
267
+ --pattern literal --operation consume --permission allow \
268
+ --yes --ticket <t> --allow-destructive-acl
269
+ ```
270
+
271
+ `acl list` is R0 and audited. Normal `acl grant` is R2. Broad grants (Kafka prefixed pattern, wildcard principal, wildcard resource, cluster resource, `all`, `alter`, cluster-action style operations, broad RabbitMQ regexes such as `.*`, `.+`, `.`, and `orders.*`, or Pulsar `functions`/`sources`/`sinks`/`packages`) and every `acl revoke` are R3 and require `--allow-destructive-acl`. Kafka implements broker ACLs with `literal`/`prefixed` patterns. RabbitMQ maps ACLs to native per-user, per-vhost permission regexes (`configure`, `write`, `read`) and only supports `--permission allow` with `--pattern regex`. Pulsar maps ACLs to native role permissions on namespaces or topics with actions `produce`, `consume`, `functions`, `sources`, `sinks`, and `packages`; it is allow-only and uses `--pattern literal`. RocketMQ fails closed with `NOT_IMPLEMENTED`: broker ACLs are managed through broker-side `plain_acl.yml` or the official Java `mqadmin`, because `rocketmq-client-go/v2` exposes no clean public ACL admin API.
190
272
  </details>
191
273
 
192
274
  <details>
@@ -194,12 +276,12 @@ mqgov message produce <topic> [--key <k>] [--body <text>] --yes
194
276
 
195
277
  ```bash
196
278
  # Backend-bound contexts (credentials go through credstore, never plaintext)
197
- mqgov ctx set <name> --backend kafka --brokers <h:p,h:p> [--sasl-mechanism PLAIN] [--tls --ca-cert <f>] [--protected]
279
+ mqgov ctx set <name> --backend kafka --brokers <h:p,h:p> [--sasl-mechanism PLAIN] [--tls --ca-cert <f>] [--schema-registry-url <url>] [--schema-registry-username <u>] [--schema-registry-password <p>] [--protected]
198
280
  mqgov ctx set <name> --backend rabbitmq (--amqp-url <url> | --host <h> --port <p> --vhost </>) --management-url <url>
199
281
  mqgov ctx set <name> --backend pulsar --service-url pulsar://<h:p> --admin-url http://<h:p> [--tenant public] [--pulsar-namespace default]
200
282
  mqgov ctx set <name> --backend rocketmq --nameservers <h:p,h:p> [--broker-addr <h:p>]
201
283
  mqgov ctx use|list|current|delete|test
202
- # secrets: --password <pw|token|secretKey> --credential-backend <encrypted-file|keychain|...> (a non-plain backend is required)
284
+ # secrets: --password <pw|token|secretKey> and --schema-registry-password <pw> go through --credential-backend <encrypted-file|keychain|...> (a non-plain backend is required)
203
285
 
204
286
  # Audit (tamper-evident, fingerprint-only)
205
287
  mqgov audit query [--since 24h] [--type <t>] [--operator <o>] [--status <s>] [--limit 100] -o json
@@ -220,7 +302,7 @@ mqgov version
220
302
 
221
303
  mqgov-cli is designed to be driven by autonomous agents safely:
222
304
 
223
- - Run `mqgov capabilities -o json` first to discover what the bound backend supports — brokers differ, don't assume (e.g. RabbitMQ/RocketMQ have no offsets; RocketMQ has no peek).
305
+ - Run `mqgov capabilities -o json` first to discover what the bound backend supports — brokers differ, don't assume (e.g. Kafka, RabbitMQ, and Pulsar support `acl` with different native models; Kafka and Pulsar support `schema`; RabbitMQ/RocketMQ have no offsets, schema registry, or tail; RocketMQ has no peek). Use `fleet status --all -o json` for a read-only cross-context dashboard.
224
306
  - Use `-o json` everywhere; every command returns a stable, versioned envelope.
225
307
  - Get blast radius from `--dry-run` / `--plan`, never from your own reasoning.
226
308
  - **Never self-fill `--ticket`, `--allow-*`, or a high-risk `--yes`.** Surface the required human approval and stop.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mqgov-cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Governed message-broker operations CLI for AI agents (Kafka, RabbitMQ, Pulsar, RocketMQ)",
5
5
  "bin": {
6
6
  "mqgov": "bin/mqgov-cli.js",