CLI reference (mqlite)
One binary, two modes — the same commands work against a local SQLite DB (embedded) or a running broker (client), chosen by environment:
- Embedded: set
MQLITE_DB(e.g.file:./mq.db,:memory:,libsql://…). The command opens the DB in-process. - Client: set
MQLITE_ENDPOINT(+MQLITE_TOKEN). The command talks to that broker over HTTP. Client mode wins ifMQLITE_ENDPOINTis set.
mqlite <command> [flags] [args]
Connection (environment)
| Env | Meaning |
|---|---|
MQLITE_DB |
embedded DB DSN: file:./mq.db / :memory: / libsql://<db>.turso.io |
MQLITE_DB_AUTH_TOKEN |
auth token for a remote libSQL/Turso DSN |
MQLITE_ENDPOINT + MQLITE_TOKEN |
client mode: a running broker + its Bearer token |
MQLITE_MONITOR_TOKENS |
optional comma-separated read-only credentials for observe and /metrics; requires administrator auth and credentials distinct from administrators |
MQLITE_TOKENS |
broker (serve) administrator Bearer tokens; unset → a mqk_… token is generated + printed, =off disables auth; additional managed keys live in the database |
MQLITE_METRICS |
authenticated Prometheus /metrics on the API port; on/true/1 enables, off/false/0 disables (default); requires administrator auth |
MQLITE_DIAGNOSTIC_LOG |
optional private rotating JSONL failure/recovery log path for serve; parent must exist; use persistent storage |
MQLITE_SYNC |
NORMAL (default) / FULL / OFF / EXTRA durability (embedded/serve). An unrecognized value is rejected at startup — a typo never silently downgrades to NORMAL. |
MQLITE_DLQ_MAX_AGE · MQLITE_DLQ_MAX_COUNT · MQLITE_DLQ_MAX_BYTES |
broker DLQ retention (serve); on by default, disable with MQLITE_DLQ_RETENTION=off |
The DB string is never compiled in — it comes from the environment. The endpoint comes
from the environment too, or from --endpoint (below), which overrides it.
Global flags (data + admin commands)
| Flag | Meaning |
|---|---|
--endpoint <url> |
client mode: a running broker (overrides MQLITE_ENDPOINT) |
--token <bearer> |
Bearer token for --endpoint (overrides MQLITE_TOKEN) |
--output text|json |
text (default, human) or json (machine-readable, for scripting) |
They apply to the data and admin commands. serve, version and help take none of them
(a broker is not a client), and vacuum is local-only — it rejects --endpoint outright,
because reclaiming pages means opening the DB file directly.
An ambient MQLITE_TOKEN is sent only to the endpoint that environment names. If --endpoint
points at a different broker, the token is withheld (with a warning) rather than leaked to
another host — pass --token to authenticate there. --token= (empty) explicitly sends no
credential at all. Two spellings of the same broker (a trailing slash, mqlite://h vs
http://h:6754) are the same target and keep the token.
--output json uses the wire's field names
Every message the CLI emits in --output json is the HTTP API's message object — the same
struct, so the same field names and encodings (seq_number, body base64, epoch-ms
timestamps). The CLI never invents or renames a key.
Two things are the CLI's own, and are not claims about the HTTP response:
- The envelope.
receive/peekprint a bare JSON array of messages; the HTTP API returns a{"messages": [...]}object. - The lock token. It is emitted only where you need it to settle later —
receive --no-ackandreceive-deferred. A default auto-ackreceivehas already Completed the messages, so it omitslock_token; raw Peek-Lock Receive always returns one.
The CLI also doesn't expose every HTTP operation (batch send, attempt ids). For the complete contract — all routes, all fields — see api-reference.md.
Settling across invocations needs a broker.
receive --no-acklocks a message and prints itslock-token; you settle it later withcomplete/abandon/reject/defer/renew <queue> <seq> <token>. This works against a running broker (client mode), which holds the lock between calls. In embedded mode each invocation opens the DB fresh and reclaims orphaned locks, so a--no-acklock does not survive to the next command — use a broker (mqlite serve+--endpoint) when you need manual settle-later.
Commands
serve — run the broker
MQLITE_DB=file:/data/mq.db MQLITE_TOKENS=mqk_dev mqlite serve --addr :6754
| Flag | Default | |
|---|---|---|
--addr |
:6754 |
listen address |
--diagnostic-log <path> |
MQLITE_DIAGNOSTIC_LOG (otherwise disabled) |
private failure/recovery log; 4 MiB × 4 files; see diagnostics |
--metrics |
false |
enable authenticated /metrics on the API port; overrides MQLITE_METRICS; --metrics=false disables |
--insecure-allow-remote |
false |
with auth disabled, allow a non-loopback bind (otherwise refused) |
The listen address may also come from MQLITE_ADDR (precedence: --addr >
MQLITE_ADDR > :6754; a blank value is rejected). With auth disabled
(MQLITE_TOKENS=off) the broker refuses a non-loopback bind unless
--insecure-allow-remote is passed, and MQLITE_CORS defaults to off.
Serves the RPC API, the open / + /healthz, and — unless MQLITE_UI=off —
the embedded admin console at /ui. Enable Prometheus on the same API port with
one flag:
MQLITE_DB=file:./mq.db MQLITE_TOKENS=mqk_dev mqlite serve \
--addr 127.0.0.1:6754 --metrics
Equivalent environment configuration is MQLITE_METRICS=on. Values
on/true/1 enable metrics; off/false/0 disable it. Values are
case-insensitive; unset or empty disables metrics. Invalid values fail startup.
--metrics=false overrides an environment-provided enabled value.
Administrator auth must remain enabled. Scrapes accept configured administrators,
managed manage keys, or configured monitor credentials; send/listen keys cannot
scrape metrics.
When enabled, the open discovery card includes "metrics":"/metrics", which
resolves against the same origin. When disabled, the field is omitted and the
route returns 404 even with valid credentials. No separate address or URL
configuration is needed. If the API is publicly reachable, so is this route;
keep metrics off public ingress with path filtering or a private-only broker.
key create|list|revoke — manage persistent access keys
Available in v0.3.1 and later. In client mode, use an environment administrator
or a managed manage key as MQLITE_TOKEN. The broker can keep running while keys
are created or revoked. Embedded mode directly manages the configured database
and requires exclusive ownership; it cannot open a running local broker's DB.
# MQLITE_ENDPOINT and MQLITE_TOKEN already select the broker and administrator.
mqlite key create --name producer --permissions send --output json
mqlite key create --name worker --permissions listen --output json
mqlite key create --name processor --permissions send,listen --output json
mqlite key create --name operator --permissions manage --output json
mqlite key list --limit 100 --output json
mqlite key list --sort created_desc --limit 25 --output json
mqlite key revoke --id <public-key-id>
| Subcommand | Flags | Result |
|---|---|---|
create |
required --name, --permissions; optional --id, --expires-at-ms |
key metadata plus its one-time secret |
list |
optional --after-id, --limit (default 100, maximum 1000), --sort id_asc|created_desc (default id_asc) |
metadata and a continuation cursor; no secrets or digests |
revoke |
required --id |
idempotently revokes an existing database key |
--id is a public 32-character lowercase hex ID; when omitted, the CLI generates
it before contacting the broker. Creation failures report that ID so an ambiguous
result can be reconciled without guessing from a name. The secret is a separate
mqk_ token with 64 lowercase hex characters. Save it securely when returned;
it cannot be retrieved again. --expires-at-ms uses epoch milliseconds; zero
(the default) means no expiry. Key names need not be unique.
Lists default to ascending public ID order. --sort created_desc lists the
newest-created keys first across all pages, with descending ID as the tie-breaker
for identical creation timestamps. Pass next_after_id as --after-id and keep
the same --sort on every page; text output includes the full continuation
command. In creation order, the cursor must be an existing key ID (revoked and
expired keys remain valid cursors). ID order also accepts absent predecessor IDs
for reconciling a lost create response.
send permits sending, scheduling and cancelling scheduled messages; listen
permits consumption, settlement, renewal, peek and queue stats; manage includes
both and all administration, including issuing other manage keys. Permissions
apply to the whole broker. A 403 is a permission failure, not an invalid token;
ordinary send/listen keys cannot use key, list, status or the admin console.
Existing configured administrator tokens retain their original format and rights.
To rotate a managed key, create its replacement, move the application to the new token, then revoke the old ID. No broker restart is needed. See the authentication contract and backup/restore implications.
create-queue <name> — create/update a queue
mqlite create-queue orders --lock 30s --max-delivery 5 --dedup 10m --ordering strict_fifo
| Flag | Default | |
|---|---|---|
--lock |
0 → 30s | Peek-Lock duration (0 inherits the engine default, 30s) |
--max-delivery |
0 → 10 | deliveries before a message goes to the DLQ (0 inherits 10) |
--ttl |
0 (unlimited) | default message TTL |
--dedup |
0 (off) | dedup window |
--ordering |
standard |
standard / group_fifo / strict_fifo |
--dlq-max-age |
0 (inherit) | per-queue DLQ retention: drop dead letters older than this |
--dlq-max-count |
0 (inherit) | per-queue: keep at most N dead letters (-1 = unbounded) |
--dlq-max-bytes |
0 (inherit) | per-queue: cap dead-letter body bytes (-1 = unbounded) |
subscribe <topic> <name> — create a subscription
(alias: create-subscription)
mqlite subscribe orders eu-orders --expr 'subject_parts[0]=="orders" && properties["region"]=="eu"'
| Flag | ||
|---|---|---|
--expr |
"" | subscription filter — an expr boolean predicate; empty = match all |
send <queue> <body> — enqueue a message
body of - reads stdin; or use --file.
mqlite send orders '{"id":1}' --group order-1 --message-id m1
echo '{"id":2}' | mqlite send orders -
| Flag | ||
|---|---|---|
--file |
read body from a file | |
--message-id |
dedup / idempotency key | |
--group |
group id (ordering / session key) | |
--subject |
subject (label) | |
--reply-to |
reply-to address | |
--ttl |
per-message TTL |
receive <queue> — consume (Peek-Lock)
By default it receives and auto-Completes. Use --no-ack to leave messages locked and
print each lock-token (settle them later — see settlement commands below), or --delete
for at-most-once.
mqlite receive orders --max 10 --wait 5s
| Flag | Default | |
|---|---|---|
--max |
1 | max messages |
--wait |
0 | long-poll wait (e.g. 5s) |
--no-ack |
false | leave messages locked (don't Complete) |
--delete |
false | receive-and-delete (at-most-once, no lock) |
Auto-ack never acknowledges output you can't see. A message is only Completed after its
body is written to stdout successfully. If the write fails (broken pipe) the message is left
locked and redelivered; if stdout cannot deliver at all (closed at exec, or /dev/null) the
command refuses up front — nothing is claimed. Use --delete to drain explicitly, or
--no-ack if you only want the lock tokens.
peek <queue> — browse without locking
mqlite peek orders --state dead_lettered --max 20
| Flag | Default | |
|---|---|---|
--state |
"" | active/locked/deferred/scheduled/dead_lettered |
--from |
0 | start sequence number |
--max |
16 | max messages |
metrics <queue> — queue counters
Prints a human line by default; use --output json for the JSON object. (Changed in
0.3.0: metrics previously printed JSON unconditionally — scripts that parsed the old
default must add --output json.)
mqlite metrics orders # active / locked / deferred / scheduled / dead_lettered / total
mqlite metrics orders --output json # machine-readable
list — list queues & subscriptions
mqlite list
redrive <queue> — move dead letters back to active
mqlite redrive orders --max 100 # back to source
mqlite redrive orders --to orders-replay # to another queue
| Flag | Default | |
|---|---|---|
--to |
"" | target queue (default: back to source) |
--max |
0 (all) | max messages |
--older-than |
0 | only messages older than this |
purge-dlq <queue> — delete dead letters
Destructive. An unbounded purge requires an explicit --all; otherwise pass a bound
(--max/--older-than). Negative bounds are rejected (they would otherwise mean "no
limit"). Exactly one positional <queue> — a stray argument is an error, not ignored.
mqlite purge-dlq orders --older-than 24h # bounded
mqlite purge-dlq orders --all # delete the entire DLQ (explicit)
| Flag | Default | |
|---|---|---|
--max |
0 | max messages to delete (0 = unbounded → --all required) |
--older-than |
0 | only delete messages older than this (minimum 1ms — a sub-millisecond value would round to "no bound") |
--all |
false | delete the entire DLQ (required to run unbounded) |
vacuum — reclaim free DB pages to the OS
Local maintenance (embedded; stop the broker first — the single-writer lock will
reject it otherwise). New local DBs use auto_vacuum=INCREMENTAL, so the default is a
no-lock PRAGMA incremental_vacuum; --full runs a full VACUUM (rewrites the file,
global lock). Not applicable to a remote Turso/libSQL store.
MQLITE_DB=file:/data/mq.db mqlite vacuum # incremental
MQLITE_DB=file:/data/mq.db mqlite vacuum --full # full rewrite
complete / abandon / reject / defer / renew — settle a message
Settle a message received earlier with receive --no-ack (which printed its
lock-token). Positional args are always <queue> <seq> <lock-token>. Client mode only
in practice (a broker holds the lock between calls — see the note under Global flags).
mqlite complete orders 42 lt_9f3a… # done
mqlite abandon orders 42 lt_9f3a… --delay 60s # release for retry, after a backoff
mqlite reject orders 42 lt_9f3a… --reason bad # dead-letter it (--detail …)
mqlite defer orders 42 lt_9f3a… # set aside for receive-deferred
mqlite renew orders 42 lt_9f3a… # extend the lock lease
schedule <queue> <body> — enqueue for future delivery
mqlite schedule orders '{"id":1}' --at 30m # a duration from now
mqlite schedule orders '{"id":1}' --at 2027-01-02T15:04:05Z # or an absolute RFC3339 time (must be future)
| Flag | ||
|---|---|---|
--at |
required | delivery time: RFC3339 or a duration (30m, 2h) |
--file · --message-id · --group · --subject |
same as send |
Cancel a scheduled message before it activates with cancel <queue> <seq>.
receive-deferred <queue> --seq … — fetch deferred messages by seq
mqlite receive-deferred orders --seq 42,57 # re-locks them and prints tokens to settle
observe — canonical observation
Available in v0.3.2 and later. Remote mode requires a v0.3.2 or later broker; configured monitor credentials are also new in v0.3.2.
mqlite observe --output json
MQLITE_ENDPOINT=http://127.0.0.1:6754 MQLITE_TOKEN="$MONITOR_TOKEN" mqlite observe
Both output modes print the complete canonical JSON snapshot, including collection
availability/freshness, queue gauges, committed message effects, storage operations,
maintenance, filter failures, and HTTP request/authentication measurements. It is the
same data contract as SDK Observe, MCP observe, and the HTTP Observe route;
/metrics serializes these measurements for Prometheus.
Check collection.state before interpreting queue counts. An unavailable collection
has queues: null, not zero messages; process counters remain usable when the
configured credential can authenticate. Durations use seconds, timestamps use epoch
milliseconds, and process counters reset on restart. Embedded mode marks HTTP as
not_applicable. Runtime read_available and db_size_available distinguish unknown
or unsupported measurements from real zeroes. See observability.md.
A configured MQLITE_MONITOR_TOKENS credential can call only observe and /metrics.
It also opens the console's read-only overview/metrics views. manage includes
observation; managed send/listen keys do not gain broker-wide monitoring. Monitor
credentials are configured at startup and cannot be created through key create.
status — backend snapshot
mqlite status # backend, location, schema, ping, size, queue/subscription counts
list-subscriptions — subscriptions with topic + filter
mqlite list-subscriptions
test-filter <expr> — dry-run a subscription filter
Compiles the expression and, with a sample, reports whether it would match (nothing is enqueued).
mqlite test-filter 'subject == "ord.created"' # just validate
mqlite test-filter 'subject == "ord.created"' --subject ord.created # validate + test a sample
| Flag | ||
|---|---|---|
--subject · --group · --prop k=v,k=v · --file |
build the sample message |
version / help
mqlite version
mqlite help
Examples end to end
# embedded: produce + consume against a local file, no broker
export MQLITE_DB=file:./mq.db
mqlite create-queue jobs
mqlite send jobs "hello"
mqlite receive jobs --wait 2s
# client: same commands against a running broker
export MQLITE_ENDPOINT=https://your-mqlite.fly.dev MQLITE_TOKEN=mqk_prod_xxx
mqlite list
mqlite metrics jobs
See also: api-reference.md (HTTP), mcp.md (agents), examples.md (Go SDK).