muster

module
v0.20.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2026 License: MIT

README

Muster

A session manager for concurrent Claude Code sessions: a Go daemon (musterd) plus a web dashboard, with interactive terminal panes backed by tmux. One place to see which session is working, which one is waiting on you, and how much context each has left.

Personal tool, macOS only, single user. Not a product. What remains before v1 is in TODO.md.

Install

curl -fsSL https://raw.githubusercontent.com/Zalaras/muster/main/scripts/install.sh | sh

That takes the latest release for your Mac's architecture, verifies it against the release's published SHA-256, and puts musterd in ~/.local/bin. The dashboard is compiled into the binary (//go:embed), so that one file needs nothing beside it.

Piping a script into sh unread is a fair thing to refuse — download it first instead:

curl -fsSL -o install.sh https://raw.githubusercontent.com/Zalaras/muster/main/scripts/install.sh
less install.sh
sh install.sh

Either way, --help lists every option; each has an environment-variable equivalent:

sh install.sh --version v0.9.0          # pin a release instead of taking the latest
sh install.sh --bin-dir /usr/local/bin  # somewhere else on your $PATH

Then confirm it worked — both lines, not just the second:

command -v musterd   # must print the path you just installed to
musterd -version     # prints the musterd version and the Claude Code verified range

If command -v prints some other path, an older musterd earlier in your $PATH (usually /usr/local/bin/musterd) is shadowing the new one and -version is reporting that stale binary. The installer warns when it detects this. Delete the old copy, or re-run with --bin-dir pointing at the directory it lives in.

The binary is unsigned, but curl does not set the com.apple.quarantine xattr, so it runs without a Gatekeeper prompt. A browser download does — clear it with xattr -d com.apple.quarantine musterd.

Verifying a first install yourself

The installer's SHA-256 check guards integrity, not authenticity: checksums.txt comes from the same host as the archive it vouches for. Releases are minisign-signed — a separate matter from the Apple code signing above, and every checksums.txt since v0.12.1 ships a checksums.txt.minisig — but install.sh deliberately does not check it, because a public key it downloaded alongside the payload is no more trustworthy than the payload (ADR). Signature checking begins the moment musterd is on disk, against the key compiled into it — see below.

To check a release yourself first (brew install minisign):

tag=v0.12.2; arch=amd64   # uname -m: x86_64 -> amd64, arm64 -> arm64
base=https://github.com/Zalaras/muster/releases/download/$tag
curl -fsSLO $base/checksums.txt
curl -fsSLO $base/checksums.txt.minisig
curl -fsSLO $base/musterd_${tag#v}_darwin_$arch.tar.gz
curl -fsSLO https://raw.githubusercontent.com/Zalaras/muster/main/internal/selfupdate/minisign.pub

minisign -Vm checksums.txt -p minisign.pub                          # key 7FA9D01D017D739A
grep " musterd_${tag#v}_darwin_$arch.tar.gz\$" checksums.txt | shasum -a 256 -c
tar -xzf musterd_${tag#v}_darwin_$arch.tar.gz -C ~/.local/bin musterd

minisign.pub there is the same key musterd embeds, so Signature and comment signature verified means the release came from the project's own pipeline.

Updating

musterd checks GitHub Releases once a day for a newer version (turn it off with the Check for updates daily box in Settings — off means no request at all). When one exists, the masthead Settings button gains a dot; the dialog's Updates section then offers Update (swaps the binary, then asks you to restart) and Update and restart (swaps and re-execs in place — every Claude session keeps running and is re-adopted; only plain-terminal shells close, and the confirm step names them). From a terminal, musterd -update does the swap without restarting anything. Every download is verified twice: the release's checksums.txt must carry a valid minisign signature against the public key compiled into the binary, and the archive's SHA-256 must match that signed file — nothing is installed otherwise. A musterd installed by Homebrew is updated with brew upgrade musterd once the tap exists, and the dialog says so instead of offering the buttons.

Requirements

  • macOS. Muster is single-user, macOS-only tooling — see SPEC.md.
  • tmux 3.2 or newer. Every session runs in tmux, on Muster's own dedicated socket, never your default server. musterd checks this at startup and refuses to start if tmux is missing or too old, naming the remedy:
    brew install tmux    # not installed
    brew upgrade tmux    # older than 3.2
    
  • Claude Code, verified against 2.1.246–2.1.296. Its auto-updater is deliberately left on — see below.

Claude Code versions

Muster does not disable Claude Code's auto-updater; that would freeze your everyday install. It detects and classifies instead: musterd compares the installed version against the canary-verified range and warns on either side of it — below the floor means "update Claude Code", above the ceiling means Muster hasn't been tested there yet — without ever refusing to start. A green make canary run extends the range automatically. Full rituals: docs/claude-code-versions.md.

Development

git clone https://github.com/Zalaras/muster && cd muster
nvm use && make build        # ./bin/musterd, dashboard embedded
make help      # list targets
make check     # lint + unit tests
make web       # frontend dev server
make e2e       # Playwright suite (needs: npx playwright install chromium)
make canary    # assert the installed Claude Code still emits what Muster needs

Toolchain: Go 1.27.1, Node 24.21.0 (pinned in .nvmrc), Vite 8, TypeScript 7, Playwright 1.63. Claude-Code-specific knowledge is confined to internal/claudecode/.

Issues and contributions

Issues are welcome; pull requests are not accepted at this time — see CONTRIBUTING.md, which also covers what the dashboard's Issue button attaches and what it never sends. Forks are fine; the MIT licence permits it.

Docs

Document What it is
SPEC.md Authoritative spec. Features, roadmap, tech stack, risks
TODO.md Backlog (issues, pre-v1, v1 release, post-v1) and open questions
docs/protocol.md The daemon↔dashboard protocol contract
docs/claude-code-versions.md The verified version range and the upgrade rituals
spikes/FINDINGS.md Measured Claude Code interface facts
docs/history/spikes/canary-fields.md Field inventory the canary asserts before any version bump
docs/adr/ Decision records, including rejected options and their rationale

Licence

MIT.

Directories

Path Synopsis
cmd
musterd command
Command musterd is the Muster daemon: it owns the tmux sessions, ingests Claude Code hooks and status-line posts, derives session state, and serves the dashboard.
Command musterd is the Muster daemon: it owns the tmux sessions, ingests Claude Code hooks and status-line posts, derives session state, and serves the dashboard.
internal
boundedwait
Package boundedwait holds the one piece of a background-loop shutdown that crosses package boundaries: bounded-wait-with-warn — "cancel (or close), wait for the goroutine to exit, but give up and warn once ctx is done", needed by internal/session's liveness poll and by internal/server's ingest queue, update apply and background loop (bgloop.go, which every poller runs on).
Package boundedwait holds the one piece of a background-loop shutdown that crosses package boundaries: bounded-wait-with-warn — "cancel (or close), wait for the goroutine to exit, but give up and warn once ctx is done", needed by internal/session's liveness poll and by internal/server's ingest queue, update apply and background loop (bgloop.go, which every poller runs on).
claudecode
Package claudecode is the adapter boundary for everything Claude-Code-specific: hook payloads, the status-line payload, CLI invocation and version pinning.
Package claudecode is the adapter boundary for everything Claude-Code-specific: hook payloads, the status-line payload, CLI invocation and version pinning.
claudecode/claudecodetest
Package claudecodetest builds Claude-Code-wire-format request bodies for tests in other packages.
Package claudecodetest builds Claude-Code-wire-format request bodies for tests in other packages.
commentpass
Package commentpass strips every comment a plan branch added to production code, hands them to a judge as text, and reconstructs each file from its snapshot minus the drops.
Package commentpass strips every comment a plan branch added to production code, hands them to a judge as text, and reconstructs each file from its snapshot minus the drops.
evict
Package evict holds the one generic map-eviction rule two packages need on opposite sides of a layering boundary: internal/server's captureStore and internal/reader's WriteLog each cap a map at a size limit by dropping its single oldest entry per insert.
Package evict holds the one generic map-eviction rule two packages need on opposite sides of a layering boundary: internal/server's captureStore and internal/reader's WriteLog each cap a map at a size limit by dropping its single oldest entry per insert.
ghissue
Package ghissue is a minimal GitHub Issues client: obtaining a bearer token and filing one issue.
Package ghissue is a minimal GitHub Issues client: obtaining a bearer token and filing one issue.
gitutil
Package gitutil shells out to the `git` CLI (docs/conventions.md: "Git/GitHub: never go-git") to answer the small set of questions the daemon needs about a launch directory: is it a git checkout, where is its top level, what branch is it on, is it a linked worktree, and (for the reader) which files does git see there.
Package gitutil shells out to the `git` CLI (docs/conventions.md: "Git/GitHub: never go-git") to answer the small set of questions the daemon needs about a launch directory: is it a git checkout, where is its top level, what branch is it on, is it a linked worktree, and (for the reader) which files does git see there.
keyedlock
Package keyedlock holds one small primitive internal/session's Manager and internal/server's shellRegistry and terminalRegistry each need: a lazily-created, per-key mutex that serialises one key's whole check-then-act without blocking any other key's (kb:adr/actions-serialized-per-session).
Package keyedlock holds one small primitive internal/session's Manager and internal/server's shellRegistry and terminalRegistry each need: a lazily-created, per-key mutex that serialises one key's whole check-then-act without blocking any other key's (kb:adr/actions-serialized-per-session).
locate
Package locate resolves a dropped file's original on-disk path from its bytes alone (kb:spec/drop): a browser hands a page a dropped file's name and bytes, never its path, so the daemon locates the original by asking a sequence of Finders for basename+size candidates and verifying each one byte-for-byte before it counts.
Package locate resolves a dropped file's original on-disk path from its bytes alone (kb:spec/drop): a browser hands a page a dropped file's name and bytes, never its path, so the daemon locates the original by asking a sequence of Finders for basename+size candidates and verifying each one byte-for-byte before it counts.
reader
Package reader holds the reader feature's filesystem scope and confinement rules — which paths a session's reader may list and serve — independent of HTTP handling (kb:spec/reader).
Package reader holds the reader feature's filesystem scope and confinement rules — which paths a session's reader may list and serve — independent of HTTP handling (kb:spec/reader).
selfupdate
Package selfupdate is to GitHub Releases what internal/claudecode is to Claude Code: the one place that knows the redirect shape of {base}/latest, GoReleaser's asset naming and checksums.txt format, and the minisign signature file format.
Package selfupdate is to GitHub Releases what internal/claudecode is to Claude Code: the one place that knows the redirect shape of {base}/latest, GoReleaser's asset naming and checksums.txt format, and the minisign signature file format.
server
Package server wires up musterd's HTTP+WS surface.
Package server wires up musterd's HTTP+WS surface.
session
Package session holds the state machine (kb:anchor/state) and the in-memory session registry that feeds it.
Package session holds the state machine (kb:anchor/state) and the in-memory session registry that feeds it.
store
Package store owns Muster's SQLite database: opening it in WAL mode, applying migrations, and the hand-written SQL for the kv and event tables.
Package store owns Muster's SQLite database: opening it in WAL mode, applying migrations, and the hand-written SQL for the kv and event tables.
termbridge
Package termbridge owns the daemon-side PTY lifecycle for one terminal socket (kb:anchor/terminal.ws): spawning `tmux attach-session` under a `creack/pty`-managed pseudo-terminal, streaming raw bytes both directions, and resizing with pty.Setsize then tmux resize-window, never the pane-level primitive (kb:adr/surfaces-shared-attach-single-pty).
Package termbridge owns the daemon-side PTY lifecycle for one terminal socket (kb:anchor/terminal.ws): spawning `tmux attach-session` under a `creack/pty`-managed pseudo-terminal, streaming raw bytes both directions, and resizing with pty.Setsize then tmux resize-window, never the pane-level primitive (kb:adr/surfaces-shared-attach-single-pty).
tmux
Package tmux wraps the `tmux` CLI operations Muster needs, always against a dedicated socket (CLAUDE.md hard rule: never the user's default server).
Package tmux wraps the `tmux` CLI operations Muster needs, always against a dedicated socket (CLAUDE.md hard rule: never the user's default server).
tmux/tmuxtest
Package tmuxtest is the one shared per-test tmux socket helper (plan v1-cleanup REQ-4): internal/tmux, internal/termbridge, internal/server and cmd/musterd's tests all built their own copy of the same os.MkdirTemp + t.Cleanup(kill-server) idiom, so this replaces all of them.
Package tmuxtest is the one shared per-test tmux socket helper (plan v1-cleanup REQ-4): internal/tmux, internal/termbridge, internal/server and cmd/musterd's tests all built their own copy of the same os.MkdirTemp + t.Cleanup(kill-server) idiom, so this replaces all of them.
triage
Package triage reduces public GitHub issue bodies to validated, enumerated data before any model holding tools can act on them.
Package triage reduces public GitHub issue bodies to validated, enumerated data before any model holding tools can act on them.
tty
Package tty answers one narrow question about a controlling terminal: is its line discipline canonical (line-buffered, "cooked") or raw.
Package tty answers one narrow question about a controlling terminal: is its line discipline canonical (line-buffered, "cooked") or raw.
usage
Package usage holds the account-level usage aggregator: a neutral Sample shape plus one in-memory Aggregator, the kb:adr/usage-no-source-interface seam for a second usage source down the line.
Package usage holds the account-level usage aggregator: a neutral Sample shape plus one in-memory Aggregator, the kb:adr/usage-no-source-interface seam for a second usage source down the line.
webui
Package webui embeds the built dashboard (web/vite.config.ts's outDir) into the musterd binary, so a copy of the binary moved away from the checkout still serves its own frontend (kb:adr/connection-dashboard-embedded-in-binary).
Package webui embeds the built dashboard (web/vite.config.ts's outDir) into the musterd binary, so a copy of the binary moved away from the checkout still serves its own frontend (kb:adr/connection-dashboard-embedded-in-binary).
plans
_audit/test-audit-tools command
mutate writes one mutant file per mutation of each input Go file: operator swaps (comparison boundary, negation, &&/||, +/-, ++/--) and statement deletion.
mutate writes one mutant file per mutation of each input Go file: operator swaps (comparison boundary, negation, &&/||, +/-, ++/--) and statement deletion.
test
rig/capture command
Command capture is the interface-probe capture server.
Command capture is the interface-probe capture server.
rig/failapi
Package failapi is the injected-failure half of a fault-injection stand-in for the Anthropic API: every request it serves gets one status, an Anthropic-shaped error body and any extra response headers.
Package failapi is the injected-failure half of a fault-injection stand-in for the Anthropic API: every request it serves gets one status, an Anthropic-shaped error body and any extra response headers.
rig/failproxy command
Command failproxy is a fault-injection stand-in for the Anthropic API, used by interface probes to induce API errors inside a Claude Code turn (point ANTHROPIC_BASE_URL at it — honoured under subscription OAuth, spikes/FINDINGS.md §3).
Command failproxy is a fault-injection stand-in for the Anthropic API, used by interface probes to induce API errors inside a Claude Code turn (point ANTHROPIC_BASE_URL at it — honoured under subscription OAuth, spikes/FINDINGS.md §3).
tools
commentpass command
Command commentpass is the orchestrator's comment pass over a plan branch (kb:adr/process-comment-pass-owns-code-comments):
Command commentpass is the orchestrator's comment pass over a plan branch (kb:adr/process-comment-pass-owns-code-comments):
gatelock command
Command gatelock is the machine-wide gate lock: a Playwright sweep needs the machine to itself (two sweeps in two worktrees, or a sweep beside `make test-race`, go red — kb:lesson/concurrent-e2e-across-worktrees-goes-red), while Go unit-test runs may overlap each other.
Command gatelock is the machine-wide gate lock: a Playwright sweep needs the machine to itself (two sweeps in two worktrees, or a sweep beside `make test-race`, go red — kb:lesson/concurrent-e2e-across-worktrees-goes-red), while Go unit-test runs may overlap each other.
triage command
Command triage is the program half of /triage.
Command triage is the program half of /triage.
versions command
Command versions keeps internal/claudecode/observed_versions.txt and the generated fragments in README.md and docs/claude-code-versions.md in sync (docs/claude-code-versions.md "The green ritual").
Command versions keeps internal/claudecode/observed_versions.txt and the generated fragments in README.md and docs/claude-code-versions.md in sync (docs/claude-code-versions.md "The green ritual").

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL