Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Capture envelopes and query them

How-to guide · for anyone who wants to ask a running mesh what went through it, without watching a live stream

The daemon can capture every published envelope into a SQLite file, the basket, and three tools query it: the pms-query command, the MCP mesh_query tool, and any SQLite client, because the schema is one table.

1. Start the daemon with a basket

mesh-hub --basket-path ~/.local/share/pms/basket.db

or pms start --basket-path <file>. A retention sweep runs every 60 seconds (--basket-retention-secs) and keeps the file to 100 MB or 24 hours of envelopes, whichever is smaller.

The daemon writes the basket on a thread of its own, so a send never waits for SQLite. Captures wait for that thread in a queue of 1,024 envelopes and 64 MiB of payload; when the disk falls that far behind, a capture is dropped, the envelope is still delivered, and the daemon logs the count at the first drop and at each doubling. Stopping the daemon writes every capture still queued.

2. Query from the command line

pms-query is a small Python package under tools/pms-query; install it with pip install -e tools/pms-query. It reads the file at PMS_BASKET_DB or ~/.local/share/pms/basket.db unless --db says otherwise.

pms-query rows --channel 'jobs/*' --since 5min --limit 20
pms-query rows --aggregate count-by-channel --since 1h
pms-query rows --aggregate time-bucket --bucket-secs 60 --since 1h
pms-query rows --origin 'worker@3f9a' --type-name event.fired
pms-query get cb05c6b8f4b696551a0b7245b6f104a2

rows takes filters that AND-combine, --channel, --type-id, --type-name, --origin, --since and --until (durations such as 5min, 1h, 1d), and --limit (50 by default). --aggregate is one of count, count-by-channel, distinct-channels, distinct-types and time-bucket (with --bucket-secs). Output is JSON: rows with the envelope id, capture time, channel, type, origin and payload hash, or the aggregate, plus next_before for paging. get fetches one envelope by identifier with its payload base64-encoded.

3. Query from an agent

The MCP server’s mesh_query tool takes the same filters and aggregations and answers with the same rows; mesh_replay republishes a captured range. See Use the MCP server from an agent.

4. Query with SQLite

One table, envelopes, with the columns id, captured_at_unix_nanos, channel, envelope_type, type_name, origin, payload_hash and payload_bytes. sqlite3 basket.db 'select channel, count(*) from envelopes group by channel' is a valid query. The mesh-basket crate owns the schema; the Python tool’s tests assert the column list.

What the basket is not

It is not the envelope store: the store is shared memory beside the ring, holds one copy per distinct envelope, and is unlinked when the daemon exits; the basket is a file, holds one row per publication, and persists. A Pulse’s envelope_ref resolves against the store, never the basket.