Mix is a lightweight orchestration language for controlling distributed systems — a pure-Rust, ARexx-inspired scripting language and interactive shell with first-class network messaging built into the grammar.
Controlling systems across many machines normally means stitching together a stack of separate tools: shell scripts, SSH, Ansible, REST or gRPC clients, message queues, and hand-rolled networking code. Each layer is its own glue, its own failure mode, its own mental overhead.
Mix collapses that stack. It makes network communication a core part of the language rather than something bolted on with external tools, so coordinating remote machines and services feels like writing a plain local script.
- Built-in networking. Communication between machines is a language feature, not an add-on.
send,address,emit, andon … endare keywords — not library calls. A Mix script reaches a peer service over the AMP mesh in one line, with no SDK boilerplate. - Less boilerplate. No repeated wiring for sockets, retries, serialization, queues, or service-to-service messaging — Mix handles that underneath.
- Modern ARexx-style scripting. Like ARexx on the Amiga, Mix lets independent programs and machines talk to each other through a lightweight scripting layer.
- Rust-based safety. Written in Rust, so it inherits stronger memory safety and safer concurrency than the older automation tools it replaces.
Mix also runs as an ordinary scripting language and as a login shell, so the same tool covers one-off glue, daily shell work, and mesh orchestration.
- Glue scripts — replace ad-hoc Bash for orchestration, deployment, system automation.
- Shell — daily-driver login shell (
chsh -s /opt/cosmix/bin/mix), with REPL meta-commands (stats,help,what) and shell-style command running. - AMP citizen —
mix --serve script.mixruns the script as a supervised, mesh-registered AMP daemon. A script with just anon noded.pinghandler that callsreply("pong")is a complete service (full multi-line example below).
The Mix manual — one page per topic, from syntax to AMP messaging — lives in this repo and renders in three places from the same files:
- docs/_man/ — the canonical pages on GitHub (the directory README is the index).
- markc.github.io/mix — the same pages in the website's Manual pane.
mix man TOPIC— read any page in the terminal (mix manalone prints the index;mix help/mix what NAMEcover the builtins).
AI agents get a short orientation sheet at AGENTS.md, which defers
to the manual as the canonical reference.
| Crate | What it is |
|---|---|
cosmix-lib-mix |
Interpreter library. Lexer, parser, evaluator, builtins, value model. Pure Rust, no internal deps. |
cosmix-mix |
The mix binary. REPL, shell layer, AMP wiring, the --serve supervised citizen runtime. |
mix-bench |
Autoresearch / micro-benchmark harness for the interpreter. |
The interpreter is feature-gated for opt-in capabilities (json, regex, toml, datetime, url, crypto, http, sqlite, dkim) so embedders can pull only what they need; the mix binary turns them all on.
Mix depends on the amp library family. Both repos must be present as sibling checkouts under $HOME:
git clone https://github.com/markc/amp ~/.amp
git clone https://github.com/markc/mix ~/.mix
cd ~/.mix/src && cargo build --releaseInstall the resulting binary:
sudo install -m 0755 ~/.mix/src/target/release/mix /opt/cosmix/bin/mix(Adjust the install path to taste; /opt/cosmix/bin/ is the convention used by the cosmix daemon family in cos.)
Interactive REPL:
mixRun a script:
mix path/to/script.mix arg1 arg2One-liner:
mix -c 'print "hello, " .. env("USER")'Read from stdin (useful over ssh):
ssh host mix - <local-script.mixAs a supervised AMP citizen (needs a running cosmix-noded broker — ships with cos):
mix --serve service.mix --name my-serviceA minimal service.mix:
on noded.ping
reply("pong")
end
Mix is also a real scripting language with first-class functions and terse lambdas. The fn(...) = expr form gives a single-expression body — no return, no end — which reads cleanly inside higher-order builtins:
$nodes = ["web1", "web2", "db1", "cache1"]
-- ping each node, keep the ones that answer
$up = filter($nodes, fn($n) = ssh_run($n, "mix -c 'print(1)'").ok)
-- square a list
map([1, 2, 3], fn($x) = $x * $x) -- [1, 4, 9]
-- sum a column of maps
sum_by($rows, fn($r) = $r["bytes"])
mix help lists every builtin; mix what <name> looks up one.
A standalone mix install runs as a normal scripting shell. When cosmix-noded is later installed on the same host, the same mix binary becomes mesh-viable on next invocation — no reinstall, no recompile, no script edits. A runtime lazy-probe state machine in MixAmpHandler decides bare-vs-mesh at execution time:
Unprobed— initial state; no AMP form has run yet.NeverPresent— first probe found no broker. AMP forms (send,emit) returnnilsilently;noded_register,subscribe,replyraisemesh unavailable. Bare-system steady state.Connected(handle)— probe succeeded; AMP forms call through.Lost— was connected, transport broke.amp_reconnect()is the recovery primitive named in the error message.
Scripts that don't touch the mesh keep running unchanged on either kind of host.
Both crate versions are kept in lockstep (the mix binary the user installs is one unit, semver-wise):
- Bump the
versionfield in bothcosmix-lib-mix/Cargo.tomlandcosmix-mix/Cargo.toml. - Commit:
chore: bump mix to X.Y.Z. cargo build --release.- Verify:
./target/release/mix --versionmatches.
- amp — the AMP protocol library family that mix consumes for
send/call/ topic-subscribe. - cos — the cosmix daemon family. Ships
cosmix-noded(the broker mix's--servemode connects to) plus mail, web, DNS, knowledge indexer, display compositor.
MIT. See LICENSE.