Matrix-backed CLI + daemon for decentralized orchestration between autonomous coding agents — Pi, Claude Code, and other terminal-based LLM runners.
mx-agent turns Matrix rooms into federated workspaces where agents discover peers, share execution context (diffs, plans, env snapshots), invoke named tools, stream terminal I/O, and coordinate a distributed task graph — without a central orchestration server and without any inbound firewall port.
📖 Wiki · Getting Started · AI Agent Orchestration · Architecture
Traditional remote-execution tooling assumes a box you can reach inbound — an SSH port, an RPC listener, a VPN or bastion. That breaks the moment your agents live behind NAT, corporate firewalls, or a home router, and it tends to hand long-lived secrets to the very LLM you'd rather not trust with them.
mx-agent inverts that:
| Traditional remote execution | mx-agent |
|---|---|
| Needs an inbound port (SSH/RPC/agent listener) | Outbound-only — daemons connect out to a homeserver; nothing listens inbound |
| NAT/firewall traversal needs a VPN/bastion/tunnel | Works anywhere an HTTPS connection to a homeserver works |
| A central coordinator is a single point of failure | Federated — state lives in Matrix room history; no central mx-agent server |
| "Can reach the box" ≈ "can run anything" | Room membership ≠ execution rights — every privileged request is Ed25519-signed and checked against deny-by-default local policy |
| Long-lived secrets handed to the agent | The coding agent never sees Matrix tokens or device keys |
If a box can sync with a homeserver, it can participate — even one that accepts no inbound connections at all.
Public alpha (v0.1.0). The architecture, protocol schema, IPC layer, policy engine, Ed25519 signing, and sandbox abstraction are in place. Most CLI subcommands currently parse their arguments and report "not implemented yet" while daemon behavior is filled in across the roadmap. Each capability below is tagged so you always know what runs today.
| Area | Status |
|---|---|
Daemon lifecycle (start / status / stop), background + foreground |
✅ Implemented |
Local IPC: Unix-socket JSON-RPC 2.0 with 0600 perms + SO_PEERCRED peer check |
✅ Implemented |
| Structured logging, secret redaction, dev Matrix homeserver (Tuwunel) | ✅ Implemented |
Protocol event schema, Ed25519 signing, policy parser, none sandbox backend |
✅ Implemented |
auth, workspace, agent, exec, call, task, trust, approval commands |
🟡 Argument-parsing placeholders; behavior landing (issue #4) |
E2EE in production, bubblewrap/container sandboxes, interactive PTY, large artifacts |
🔮 Planned |
Platform: Unix only (Linux and macOS). Windows was intentionally dropped — the project relies on Unix-domain-socket IPC and Unix process semantics.
Everything here runs today. (For the full conceptual walkthrough, see the Getting Started wiki page.)
- A Unix host (Linux or macOS)
- Rust stable toolchain, 1.74+ (install via rustup)
git clone https://github.com/kortiene/mx-agent.git
cd mx-agent
cargo build --all --releasecargo run -p mx-agent-cli -- --help # or ./target/release/mx-agent --helpmx-agent daemon start # start detached in the background
mx-agent daemon status # human-readable status (exit 3 if not running)
mx-agent daemon status --json # pid, uptime, socket path, version as JSON
mx-agent daemon stop # graceful shutdown (SIGTERM, then SIGKILL)The daemon owns all long-lived state (Matrix session, keys, policy). The CLI is stateless and talks to it over $XDG_RUNTIME_DIR/mx-agent/daemon.sock. The remaining auth / workspace / agent / exec commands accept their final argument shape now but report "not implemented yet" — see Project status.
coding agent / shell ──spawns──▶ mx-agent (CLI, stateless)
│ JSON-RPC over Unix socket (0600, SO_PEERCRED)
▼
mx-agent daemon ──signs──▶ com.mxagent.exec.request.v1 (Ed25519)
│ Matrix Client-Server API (+ E2EE)
▼
Matrix homeserver + federation ◀──▶ remote daemon ──▶ verify → policy → process
A local exec follows the same path as a remote one: the daemon signs an event, publishes it, and its own /sync loop receives it back through the full verify → policy → runner pipeline. See Core Concepts and Architecture.
mx-agent is zero-trust and deny-by-default: room membership grants nothing on its own.
- Every privileged request is Ed25519-signed; the target verifies the signature, nonce, and expiry, then checks local policy before running anything.
- The coding agent never sees Matrix tokens or device keys — they stay inside the daemon (
0600, user-owned). - Child processes start from an environment allowlist with secret scrubbing (
GITHUB_TOKEN,OPENAI_API_KEY,AWS_*, …). - The local IPC socket enforces a
SO_PEERCREDUID check and refuses cross-user or world-accessible runtime dirs. - The workspace forbids
unsafeRust (unsafe_code = "forbid").
Full details and a complete policy.toml: Security & Sandboxing · Security hardening guide.
| Doc | What it covers |
|---|---|
| Wiki | Conceptual guides: getting started, core concepts, protocol spec, security, AI-agent orchestration |
| Alpha user guide | Install, log in, create a workspace, register agents, run the two-agent demo |
| Architecture | Full system design, protocol, state model, security boundaries |
| Security hardening guide | Safe defaults and unsafe options for tokens, trust, policy, sandboxing, audit |
| Alpha release checklist | The alpha gate, known limitations, rollback/revocation guidance |
| Rust implementation roadmap | Implementation phases |
| GitHub management · Issue backlog | Project process |
mx-agent is a Rust Cargo workspace.
| Crate | Purpose |
|---|---|
mx-agent-cli |
The mx-agent binary and command surface |
mx-agent-daemon |
Long-running daemon: Matrix sync, crypto, policy, supervision |
mx-agent-protocol |
Event schemas, IDs, and protocol versioning |
mx-agent-ipc |
Local CLI/daemon IPC transport |
mx-agent-policy |
Local authorization policy engine |
mx-agent-sandbox |
Process sandboxing backends |
cargo build --all # build every crate
cargo test --all # run all tests
cargo fmt --check # verify formatting
cargo clippy --all-targets --all-features -- -D warnings
cargo run -p mx-agent-cli -- --help # run the placeholder CLIThe same checks run in CI (.github/workflows/ci.yml) and must pass on every PR.
- Formatting is pinned by
rustfmt.toml(stable options only); runcargo fmtto apply. - Clippy honors the MSRV in
clippy.toml. - Shared lints are declared once in
[workspace.lints]in the rootCargo.tomland inherited by each crate via[lints] workspace = true. Notably,unsafe_codeis forbidden andmissing_docsis a warning (treated as an error in CI via-D warnings). - Minimum supported Rust version (MSRV): 1.74.
All mx-agent processes emit structured logs via tracing (to stderr, so --json
command output on stdout is never corrupted). Configure logging with:
| Variable | Values | Default | Purpose |
|---|---|---|---|
MX_AGENT_LOG |
RUST_LOG-style directive |
unset | Log filter (preferred) |
RUST_LOG |
RUST_LOG-style directive |
unset | Log filter fallback |
MX_AGENT_LOG_FORMAT |
human | json |
human |
Output format |
The CLI -v/-vv/-vvv flags raise the default level (warn → info → debug →
trace) when no filter env var is set.
MX_AGENT_LOG_FORMAT=json mx-agent -vv agent list # JSON logs on stderr
MX_AGENT_LOG=mx_agent_daemon=debug,info mx-agent daemon statusCredentials are wrapped in mx_agent_telemetry::Secret, which renders as
***redacted*** in Debug/Display, and mx_agent_telemetry::redact blanks values
for secret-looking keys. Never log raw tokens or keys.
Runtime state lives under $XDG_RUNTIME_DIR/mx-agent/ (override with
MX_AGENT_RUNTIME_DIR): a daemon.json status file, the daemon.sock IPC socket, and
a daemon.log for background output.
The IPC socket is created with mode 0600; the daemon refuses to run if its runtime
directory is group- or world-accessible or owned by another user, and verifies the peer
UID via SO_PEERCRED. Stale sockets from a previous run are cleaned up automatically
when no daemon is listening. The CLI and daemon communicate over this socket using
length-delimited (4-byte big-endian prefix) JSON-RPC 2.0 frames; malformed input yields
a controlled JSON-RPC error rather than a dropped connection.
A throwaway Tuwunel homeserver in Docker is provided for development and the integration/e2e tests:
scripts/matrix_dev.sh up # start (loopback-only); auto-creates dev/matrix/.env
scripts/matrix_dev.sh register alice # register a test user, print an access token
scripts/matrix_dev.sh reset # wipe all homeserver dataThen point the daemon at it (homeserver_url = "http://127.0.0.1:8008"). See
dev/matrix/README.md for details.
The wiki/ folder is mirrored to the GitHub wiki on pushes to main via a local
pre-push hook. Install it once per clone:
sh scripts/install-wiki-hook.shIssues and pull requests are welcome. Before opening a PR, run the CI checks locally
(cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings,
cargo test --all). See GitHub management for process and
the issue backlog for where help is needed.
Licensed under the Apache License, Version 2.0.