Install darkmux, verify the setup, configure your first profile, swap to it, and run a smoke test. Everything else in the guide assumes you've done this.
| Required | Why | Install |
|---|---|---|
| LMStudio | darkmux drives LMStudio via the lms CLI for load/unload. |
macOS / Windows / Linux installer. |
| At least one model in LMStudio | Nothing to swap to without one. | Download via the LMStudio UI; verify with lms ls. |
brew install needs no toolchain. Homebrew handles the build, and bottled binaries (once published) ship precompiled. The Rust toolchain is needed only for the from-source path below, which documents rustup at its first step.
darkmux is developed and tested on Apple Silicon. Linux should work; Intel Mac is untested.
The swap / status / profiles core needs only LMStudio. Docker is required for crew dispatch / lab run (the default internal runtime); an external agent runtime is an optional alternative to it.
| Tool | When you need it |
|---|---|
| Docker | Required for darkmux crew dispatch / lab run (the default internal runtime). The dispatch runs in a per-invocation darkmux-runtime container; build the image once with docker build -t darkmux-runtime:latest runtime/. Skip only if you opt out via --runtime openclaw. |
| An agent runtime (e.g. OpenClaw, Aider, Cline) | Only when you opt into --runtime openclaw on darkmux crew dispatch or darkmux lab run (both default to the internal Docker-bounded runtime). Override the openclaw binary path per dispatch with --runtime-cmd <path> (e.g. for Aider / Cline). Pure swap/status/profiles work without any runtime. |
| Claude Code (or any frontier orchestrator) | Recommended. darkmux is designed to be driven by a frontier orchestrator. darkmux init installs agent skills (including the /darkmux-bootstrap setup skill) and dispatches agentic work through one. The bare CLI works without it, but the orchestrator is the intended path; see Recommended: let your orchestrator set it up below. |
Works from a fresh macOS machine that has LMStudio installed:
# 1. Tap + install
brew tap kstrat2001/darkmux
brew install darkmux # stable release (or --HEAD for latest main)
# 2. Bootstrap config + agent skills
darkmux init
brew install refuses with an "untrusted tap" error, newer Homebrew gates third-party taps behind a one-time trust step: run brew trust kstrat2001/darkmux, then re-run the install. (Older Homebrew won't prompt for this.)The tap is at kstrat2001/homebrew-darkmux. The formula installs the darkmux CLI + serve daemon + the keychain-aware wrapper script. For the production-grade always-on hub setup (Redis hardening, audit substrate, log rotation, daily integrity checks), see the always-on hub guide.
Updating later is then brew upgrade darkmux (+ brew services restart darkmux if you have the daemon running).
You don't need this to dispatch: the darkmux-runtime Docker image (used by darkmux crew dispatch and darkmux lab run) is pulled from GHCR on demand on the first dispatch (#759), so a brew install is complete. Use this path if you're contributing to darkmux, or want to build the runtime image offline instead of pulling it:
# 1. Install Rust toolchain (skip if `cargo --version` already works)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# 2. Clone + build darkmux
git clone https://github.com/kstrat2001/darkmux
cd darkmux
cargo install --path .
# 3. Optional: build the runtime image locally instead of pulling it from GHCR
docker build -t darkmux-runtime:latest runtime/
# 4. Bootstrap config + agent skills
darkmux init
The source "$HOME/.cargo/env" line is the one people miss most. Without it, a fresh cargo install fails with command not found: cargo in the same shell that just ran the rustup installer.
darkmux is built to be driven by a frontier orchestrator: a Claude Code, Gemini, Antigravity, Cursor, Codex, or Copilot session that runs the loop, reads the project docs, and proposes the commands. darkmux init installs the agent skills + guidance for exactly this. The recommended way to finish setup (and to operate darkmux day to day) is from inside an orchestrator session, not by hand.
Whichever frontier model you prefer, the first move is the same: have it read the project docs. They're written for any agent and carry darkmux's operating doctrine:
README.md: what darkmux is for (the north-star) + the quick start.CLAUDE.md / AGENTS.md: the agent-facing operating doctrine. Claude Code auto-loads CLAUDE.md; Antigravity / Gemini / Codex / Cursor auto-load AGENTS.md. Same doctrine, both conventions shipped.DESIGN.md: the implementation reasoning, for when the agent needs the "why."Claude Code gets a head start: open a session in your working directory and run the guided setup skill (installed by darkmux init):
/darkmux-bootstrap
It walks the whole setup interactively: detecting your hardware tier, downloading the recommended models, registering profiles, declaring the orchestrator for flow-record provenance, and validating the end state with darkmux doctor. The skill reads and proposes; you run the commands, so you stay in the loop with provenance on every step. On any other frontier, point the session at the docs above and ask it to do the same; the steps below are exactly what it follows. (First-class bootstrap skills for Gemini / Codex / Copilot are on the roadmap: #179.)
darkmux's reason for existing is local-AI orchestration: the frontier model is the strategist that drives your local models, and setup is the first place that shows up. Everything from here down documents the same steps by hand: read it to understand what the orchestrator does for you, or follow it directly if you're running darkmux as a bare CLI.
darkmux doctor runs pre-flight checks across the substrate: profile registry, lms binary, models loaded, runtime command, RAM headroom, power state, agent role scaffolds, crew role prompt coverage, daemon reachability, flow-sink health, audit integrity, machine_id resolution, orchestrator declaration, recommendation drift, per-role model-pin drift, plus a battery of empirical rules from the eureka set.
darkmux doctor
Each check returns Pass, Warn, or Fail. Fail-level lines include actionable hints; the exit code is 0 on all-pass-or-warn, 1 if anything Fails.
Output is one line per check, color-coded: ✓ for Pass, ! for Warn, ✗ for Fail. Run it yourself on a fresh setup. That's the canonical reference for what it looks like.
Common first-run warnings:
daemon: reachable Warn: you haven't started darkmux serve yet. That's fine for now; you start it in the Observability section.profile match Warn: your ~/.darkmux/profiles.json doesn't yet reference what's loaded in LMStudio. That's also fine, we fix it next.runtime version Warn: your installed agent runtime is older than darkmux's validated minimum. If you're not running an orchestrator, you can ignore this for now./darkmux-bootstrap do it)This is the manual version of what the /darkmux-bootstrap skill does for you. Follow it to understand the moving parts, or if you're running darkmux without an orchestrator.
A profile is a named loadout: which model(s) to keep resident in LMStudio at which context length. Profiles live in ~/.darkmux/profiles.json (created by darkmux init).
darkmux profiles # list configured profiles
cat ~/.darkmux/profiles.json # see the actual JSON
The bootstrap config ships with four reference profiles in increasing capacity: fast (32K), balanced (100K), deep (200K), and a gpt-oss preset (the 120B MXFP4 build for Apple Silicon, at 100K). fast/balanced/deep carry a placeholder worker id, <your-worker-model-id>, for you to fill in; gpt-oss ships a concrete id (mlx-community/gpt-oss-120b-MXFP4-Q8). The default profile is balanced, not the 120B preset.
The quickest way: darkmux scan lists the models LMStudio has downloaded that aren't yet in any profile, with a suggestion for each, so you can see what's available to slot in without hand-matching ids.
# Downloaded models not yet in a profile, with suggestions
darkmux scan
Then replace a placeholder in profiles.json with a real id (lms ls shows the raw list if you prefer). Pick one to start, whatever you've been using day-to-day:
# See what's available (raw)
lms ls
# Edit the file (use whatever editor)
${EDITOR:-vim} ~/.darkmux/profiles.json
Each profile model has two fields that matter:
id: the LMStudio model id from lms ls output (e.g. qwen3.6-35b-a3b).n_ctx: context length to load at. Defaults are usually fine; 32768 is a safe starter for any model.Profile models are worker models. There's no per-model role field (it was removed in #590). The standing utility/compactor model is declared once for the machine in the top-level internal.utility binding, not per profile. If a profile lists more than one worker, an optional default_model names which one is the default.
darkmux profiles # the profile should now list your model
darkmux doctor # confirm "profile match" is green or close
swap recommendedIf you'd rather not author profile JSON by hand on a fresh install, darkmux ships a curated per-hardware-tier recommendation registry (per #159) with the bake-off-validated models for your machine class:
darkmux swap recommended --dry-run # resolves your tier → validated profile → pre-flight checks
darkmux model pull-recommended # downloads any missing models via `lms get`
darkmux swap recommended # load them for real
On Apple Silicon 96+ GB (m-series-128), the validated recommendation pairs a 35B-A3B MoE primary with a 4B compactor (specific model ids in the registry). Other tiers carry a pending-bake-off status: the verb errors loudly with the rationale, surfacing the gap rather than silently picking. Doctor's recommendation-drift check warns when your loaded models don't match the registry's pick.
darkmux swap <profile> transitions LMStudio's loaded model set to match the named profile: it unloads anything that doesn't belong and loads what does. By default it writes nothing outside LMStudio. The internal runtime reads the loaded model directly. If you dispatch through openclaw, add --runtime openclaw and swap will also patch ~/.openclaw/openclaw.json to reference the now-loaded identifier; without that flag, no config file is touched.
darkmux status # what's loaded right now; which profile (if any) matches
darkmux swap fast # load the "fast" profile
darkmux status # confirm: should show "fast" as the active profile
darkmux issued lms load for your profile's primary model with a darkmux: namespace prefix (so darkmux can recognize its own loads later), set the context length, and reported success. Nothing outside LMStudio was modified. The internal runtime resolves the loaded model directly. (Had you run darkmux swap fast --runtime openclaw, swap would additionally patch ~/.openclaw/openclaw.json so an openclaw dispatch routes to the loaded identifier.)
If swap reports a warning about RAM headroom or a model not found in lms ls, that's doctor-style feedback at swap time, usually fixable by either trimming context length or downloading the model first via the LMStudio UI.
darkmux lab run quick-q # single-turn smoke prompt against the active profile
darkmux lab inspect <run-id> # quick look at what happened
This runs through darkmux's default internal Docker-bounded runtime. Build the darkmux-runtime image once (see the lab section) and it works with no external runtime. To dispatch through openclaw instead, pass --runtime openclaw. If you have neither Docker nor openclaw set up yet, skip this step and come back after reading the lab section.
If you set up via /darkmux-bootstrap (or had your orchestrator follow the steps above), your profiles, models, and orchestrator declaration are already in place. Pick a direction: