A standalone test orchestration tool for running distributed peer integration tests across multiple hosts. Manages SSH connections, Docker image distribution, container lifecycle, Redis coordination, and provides both TUI and console interfaces for monitoring test execution.
cargo build --releaseThe binary is target/release/tm.
tm <config.yaml> # console mode (default)
tm <config.yaml> --tui # ratatui TUI mode
tm <config.yaml> --verbose # console mode with tracing output on stderr
tm <config.yaml> --redis-url <url> # use an external Redis instanceTests are defined in YAML. See docs/test-schema.md for the full schema reference and examples/smoke-test-5peer.yaml for a working example.
- Starts a local Redis container for peer coordination (or connects to an
external instance via
--redis-url) - Enables Redis keyspace notifications (
CONFIG SET notify-keyspace-events K$) so the orchestrator reacts instantly to peer status changes via pub/sub - Connects to remote hosts via SSH
- Distributes Docker test images to all hosts (skips if already present)
- Starts peer containers with environment variables for Redis, listen address, etc.
- Monitors each peer via two channels:
- Status: subscribes to
__keyspace@0__:{peer}_statusfor instant notification when a peer SETs its status key (no polling) - Logs: drains
{peer}_logviaBLPOPwith timeout 0 (truly blocking)
- Status: subscribes to
- Executes a timed command sequence (connect, push, pull, rotate-key, etc.)
- Shuts down all peers and cleans up containers
Each peer monitor task creates three dedicated Redis connections:
| Connection | Purpose |
|---|---|
| PubSub | Subscribes to __keyspace@0__:{peer}_status keyspace notifications |
| GET | Fetches the status value when a notification fires |
| BLPOP | Drains {peer}_log entries (blocking, timeout 0) |
The GET and BLPOP connections are separate MultiplexedConnection instances
(separate TCP sockets) so that a blocking BLPOP cannot starve status GET
requests.
SIGINT(Ctrl+C) andSIGTERMtrigger graceful shutdown — peers are told to stop and the tool waits for them to report "stopped" before cleaning up.
Peer applications communicate with the orchestrator using one of the provided client libraries:
- Rust —
the_mulecrate with async Stream-based API - Python —
the_mulepackage with async iterator API - Go —
the_mulepackage with channel-based API
See the client libraries README for the full Redis protocol specification, and the per-language guides:
See QUICKSTART.md for a step-by-step guide to running your first test on localhost.
Apache 2.0 — see LICENSE.