01 · foundation

Getting started

Install, init, first inference. Then the checks, a first dispatch, and the viewer. Every output below is a capture from a fresh machine, so what you see should match.

Prerequisites

Two things, and a third only for dispatch.

RequiredWhy
HomebrewHow darkmux installs.
LM Studio, with at least one model downloaded and its command-line tool enabled (~/.lmstudio/bin/lms bootstrap)darkmux loads and unloads models through lms. init picks your first worker model from what you have downloaded.
Docker, runningOnly for dispatch, lab run, and missions: each dispatch runs in a small darkmux-runtime container, pulled on first use. radio, doctor, and the viewer need no Docker.

darkmux is developed and tested on Apple Silicon. Everything below was run on one.

Install and init

One line installs; init writes your config and profile registry, picks a worker model from what LM Studio has, and installs the agent skills.

$ brew install kstrat2001/tap/darkmux
$ darkmux init
profile registry: created at ~/.darkmux/profiles.json worker model: qwen3.6-35b-a3b-turboquant-mlx (LM Studio has it; every worker profile now names it, edit ~/.darkmux/profiles.json to change) utility model: qwen3-4b-instruct-2507 (the registry named one LM Studio does not have; this is the closest downloaded match, edit ~/.darkmux/profiles.json to change) config: created at ~/.darkmux/config.json (machine_id seeded — edit to set Redis, dirs, runtime knobs) skills targets: ~/.claude/skills installed (15): darkmux-bootstrap, darkmux-mission-debrief, darkmux-enable-audit, darkmux-add-machine, darkmux-status, darkmux-list-stacks, darkmux-list-workloads, darkmux-lab-run, darkmux-list-runs, darkmux-analyze-run, darkmux-compare-runs, darkmux-design-profile, darkmux-scan-and-suggest, darkmux-qa-review, darkmux-escalation-handler Next steps: 1. First answer, no Docker needed: darkmux radio "do you have a brain?" 2. Check the setup: darkmux doctor 3. With Docker running, a first dispatch: darkmux dispatch code-reviewer "What do you do?" (the runtime image is pulled on first use)

Verbatim, captured 2026-08-28 on an M5 Max from a home directory with no ~/.darkmux, LM Studio installed with a few models downloaded.

If Homebrew refuses the tap as untrusted, run brew trust kstrat2001/tap once and install again. Update later with brew upgrade darkmux. init is safe to re-run: it refreshes a skill it can prove it wrote itself, leaves an edited one alone, and never overwrites a registry or config you have edited. A skill installed by an older darkmux carries no such proof, so the first re-run after upgrading reports every skill as kept rather than refreshed — darkmux init --force takes the new versions and discards any edits.

First inference

No Docker, no setup beyond the model: radio is darkmux in plain text, and it is the fastest proof the install works. Ask it anything about darkmux; it answers from the command tree and your machine’s state.

$ darkmux radio "do you have a brain?"
radio: No biological hardware. I run on local silicon, routed through your LM Studio instance at localhost:1234. I process text, match it against darkmux commands, and execute what's configured. If you want to see what models are actually loaded on your machine, run darkmux machine status. Otherwise, I'm just a well-wired console with a calibrated humor dial and zero pulse.

The radio page has the rest: the humor dial, the editor integration, and how to advertise your own commands.

Check the setup

doctor asks "is my setup correct": registry, models, RAM, daemon, flow sinks, skills. Pass, warn, or fail per check, with the fix under each warning; exit 1 only on a fail. machine status asks the other question, "what is loaded right now".

$ darkmux doctor
darkmux doctor — 56 checks ● needs attention — flow sink health: LocalFile · warnings: flows_dir_absent ✓ build darkmux 3.13.0 (2c3ee0fd) · flow schema 1.56.0 ✓ daemon reachable reachable · viewer http://127.0.0.1:8765/ · phone http://<tailnet-hostname>/ ✓ 51 more checks passed — -v for detail ⚠ flow sink health LocalFile · warnings: flows_dir_absent → Run darkmux flow status for full detail. Common fixes: start Redis (`brew services start redis) if redis_unreachable; raise DARKMUX_REDIS_MAXLEN` if redis_stream_near_maxlen; upgrade the lagging writer in the fleet if schema_skew_detected. ⚠ audit integrity no audit files under ~/.darkmux/audit → AuditFileSink is opt-in: set DARKMUX_AUDIT_DIR to enable a BLAKE3 hash-chained audit log whose edits darkmux flow integrity-check detects (absent a full re-chain — the chain is un-anchored), alongside the casual LocalFile sink. ⚠ utility model utility model qwen3-4b-instruct-2507 registered but NOT loaded → No verb needs this loaded first — the binding's only job is to name the compactor, and every dispatch path self-loads it at its declared n_ctx under the darkmux: namespace (#1616). Loading it by hand just pays that cost now instead of during the first dispatch; if you do, keep the namespace and the context — `lms load <id> --context-length <n> --identifier darkmux:<id> — since a bare lms load` creates a resident darkmux won't reuse and machine eject can't reclaim. (#590, #1616, #1675) 53 pass, 3 warn — workable but worth a look
$ darkmux machine status
registry: ~/.darkmux/profiles.json darkmux-managed (1): darkmux:qwen3.6-35b-a3b-turboquant-mlx ctx=262144 18.45 GB idle user state (0): (none — LMStudio is exclusively darkmux's right now) matches no registered profile

Three warnings on a fresh install are normal: no flow files yet, the audit log is opt-in, and the utility model loads itself on the first dispatch. One thing is redacted: the machine’s tailnet hostname on the daemon line.

Profiles

A profile is a named loadout: which model at which context length. init wrote three worker profiles around the model it picked (fast at 32K, balanced at 100K, the default, and deep at 200K) plus a few reference profiles for the review pipeline. Nothing loads by hand: a dispatch loads what its profile declares, under the RAM budget, and never touches a model you loaded yourself.

$ darkmux profile list
$ darkmux profile scan

To change the worker, edit ~/.darkmux/profiles.json; profile scan lists downloaded models no profile covers yet. Profile models are workers. The utility model that compacts context for them is declared once, in internal.utility.

First dispatch

A dispatch hands one role one message and lets it work with real tools. This is what Docker is for: each dispatch runs in a fresh darkmux-runtime container with only its workspace mounted, so the model can read, write, and run things in that directory and reach nothing else on your Mac, and the container is gone when the dispatch ends. The workspace itself is the model’s to change, which is why coding missions stop at a sign-off gate before anything ships. The first dispatch pulls the image; after that it is seconds. darkmux lab run quick-q runs the same path as a recorded, timed run.

$ darkmux dispatch code-reviewer "In two sentences, what do you do and what do you never do?"
dispatching to model: qwen3.6-35b-a3b-turboquant-mlx --- final assistant message --- I analyze diffs to identify correctness, security, and testing issues while reporting findings classified as MUST FIX or CONSIDER. I never modify implementation code, create commits or PRs, run remote git operations, or attempt to install project toolchains. --- metrics --- turns: 1 compactions: 0 prompt tokens: 3527 completion tokens: 277 total messages: 3 wall: 2933ms darkmux-runtime: trajectory → /darkmux-out/.darkmux-runtime/trajectory.jsonl darkmux-runtime: metrics → /darkmux-out/.darkmux-runtime/metrics.json

Every dispatch writes a flow record and a trajectory. The message is positional; git diff | darkmux dispatch pr-reviewer pipes one in. With no --workdir, the workspace is a fresh temporary directory, as above.

Watch it work

The daemon serves your flow records to a viewer in your browser: one drillable view, fleet to machine to run to tool call. It runs as a Homebrew service and comes back at login.

$ brew services start darkmux
$ open http://127.0.0.1:8765/

Nothing leaves the machine; the page is served by your own daemon. If the smoke test above ran with the daemon up, it is already on the timeline. The demo is the same viewer on recorded data, and Observability is the deeper tour.

Your first mission

A dispatch runs one role once. A mission runs a task graph: a crew of roles works the phases and the graph is live in the viewer as it goes. The built-in coder-phase is the dispatch-to-PR loop: a worktree, a coder, a QA review, then a stop at a sign-off gate before anything ships.

$ darkmux mission launch coder-phase --param workdir=/tmp/darkmux-first-mission --param base=main --param branch=darkmux/first-mission --param mission_id=first-mission

A mission drives real dispatches for minutes, so its output is yours to generate; run it against a repo of your own and watch the graph. From the gate, you (or your agent) ship the git work, then darkmux mission finalize <id> closes it out. The whole model is in Missions, phases, and dispatch.

From an agent

init installed skills for Claude Code and a CLAUDE.md/AGENTS.md-reading harness. In Claude Code, /darkmux-bootstrap walks this same setup interactively, proposing each command for you to run. In Zed, darkmux acp is an agent server; the radio page shows the wiring. Nothing on this page needs an agent; everything on it works from one.