Documentation
¶
Overview ¶
Package server wires up musterd's HTTP+WS surface: auth, static serving, the ingest endpoints and the state stream. Business logic (parsing, persistence) is delegated to internal/claudecode and internal/store; handlers here only decode, delegate, encode.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ClaudeCodeInfo ¶
ClaudeCodeInfo is the daemon's startup snapshot of the installed Claude Code against the canary-verified range, used to build the WS `hello` message (docs/protocol.md §5.1) and the issue-capture snapshot. Installed is nil iff Status is "unknown" (the startup version check failed, hung past its timeout, or was unparseable); Floor/Verified are always populated. internal/server carries Status only as a plain string — the status-word type itself lives in internal/claudecode (docs/conventions.md "Boundary").
type ClaudeThemeInfo ¶
type ClaudeThemeInfo struct {
Family string `json:"family"`
}
ClaudeThemeInfo is the `claudeTheme` object inside a snapshot (docs/protocol.md §5.2/§5.6, plan new-ui-design-colors REQ-15) — the daemon's latest read of Claude Code's own theme family. Family is always present: "unknown" while polling is disabled (-claude-theme-poll 0) or no read has yet succeeded.
type Config ¶
type Config struct {
Store *store.Store
Logger zerolog.Logger
UIToken string
IngestToken string
// WebDist, when non-empty, serves the dashboard from this on-disk directory instead
// of the embedded copy (dev override, plan embed-dashboard REQ-2) — behaviour
// unchanged from before that plan. Empty (the new -web-dist default) serves
// internal/webui's go:embed-ed tree instead.
WebDist string
DaemonVersion string
ClaudeCode ClaudeCodeInfo
// IngestQueueSize bounds the ingest queue; a full queue drops and logs rather than
// blocking the HTTP response (REQ-23). Zero uses a sane default.
IngestQueueSize int
// ClaudeBin is the `claude` binary to spawn (REQ-19's -claude-bin, default "claude").
ClaudeBin string
// TmuxSocket is the dedicated tmux socket name (REQ-19's -tmux-socket, default "muster").
TmuxSocket string
// BrowseRoot is the folder browser's root (protocol §3.6): GET /api/browse's
// no-param default and the "Up" ceiling. Empty means the daemon user's home
// directory. E2E passes its per-run scratch dir so browse tests never touch
// the real home.
BrowseRoot string
// HookScript/StatusLineScript are the absolute paths to the generated command-hook
// wrapper scripts (internal/claudecode.WriteWrapperScripts) — every event, including
// SessionStart, is registered against HookScript now.
HookScript string
StatusLineScript string
// LegacyScripts lists prior wrapper paths MergeSettings must still recognise and
// drop from an already-instrumented directory (REQ-3/REQ-4) — currently just the
// pre-plan hook-sessionstart.sh path WriteWrapperScripts returns for removal.
LegacyScripts []string
// UsagePoll is the poll interval (-usage-poll). <= 0 disables polling entirely:
// POST /api/usage/refresh then 404s (docs/protocol.md §3.9).
UsagePoll time.Duration
// UsageAPIURL is the per-model usage endpoint's base URL (-usage-api-url). main
// always passes the flag's non-empty default, so this is the *only* place that
// URL is defined — there is deliberately no fallback constant here duplicating it.
// Empty disables the poller entirely (same fail-safe shape as UsagePoll <= 0):
// a zero-value Config must never reach the real api.anthropic.com.
UsageAPIURL string
// UsageTokenFile, when non-empty, reads the Claude Code OAuth token from this file
// instead of the macOS Keychain (-usage-token-file, REQ-13's test seam — makes it
// structurally impossible for a test using it to fall through to the real Keychain).
UsageTokenFile string
// KeychainUser is the account name `security` looks the Keychain item up under
// (main passes os/user.Current()'s value) — used only when UsageTokenFile is empty.
KeychainUser string
// HTTPClient is the client FetchUsage uses; nil defaults to http.DefaultClient.
HTTPClient *http.Client
// ClaudeThemePoll is the poll interval (-claude-theme-poll). <= 0 disables polling
// entirely: snapshot.claudeTheme.family stays "unknown" forever.
ClaudeThemePoll time.Duration
// ClaudeConfigFile is Claude Code's global config file to poll
// (-claude-config-file, default claudecode.DefaultConfigPath()) — a test seam like
// UsageTokenFile (INV-5: E2E always passes a scratch path).
ClaudeConfigFile string
// IssueRepo is the GitHub repo issues are filed against (-issue-repo).
IssueRepo string
// IssueAPIURL is the GitHub API's base URL (-issue-api-url). main always passes the
// flag's non-empty default, so this is the only place that URL is defined — no
// fallback constant duplicates it here. Empty disables both new endpoints entirely:
// they 404 not_found (docs/protocol.md §3.12/§3.13).
IssueAPIURL string
// IssueTokenFile, when non-empty, reads the bearer token from this file's trimmed
// contents instead of running `gh auth token` (-issue-token-file, REQ-14's test seam
// — makes it structurally impossible for a test using it to execute the real gh).
IssueTokenFile string
// Locator resolves a dropped file's original path for POST /api/sessions/{id}/locate
// (plan file-drop-fix, docs/protocol.md §3.14). main always constructs locate.New();
// tests that never exercise the endpoint may leave this nil.
Locator *locate.Locator
// TmuxClient overrides the paneSpawner sessionLauncher and shellRegistry use (plan
// v1-cleanup REQ-1). Nil constructs the real tmux.New(cfg.TmuxSocket) exactly as
// before this plan — every production call site, main included, leaves this nil.
TmuxClient paneSpawner
// Attach overrides how a terminal socket attaches to a tmux target (plan v1-cleanup
// REQ-2). Nil goes through termbridge.Attach against the server's own tmux client
// exactly as before this plan — every production call site, main included, leaves
// this nil.
Attach attachFunc
}
Config wires everything a Server needs. Built entirely in main — no init() magic, no package-level state.
type PrefsInfo ¶
type PrefsInfo struct {
View string `json:"view"`
Density string `json:"density"`
UsageModel string `json:"usageModel"`
RailSort string `json:"railSort"`
Theme string `json:"theme"`
}
PrefsInfo is the `prefs` object inside a snapshot (docs/protocol.md §3.3/§5.2). M2 makes this persist (kv) and broadcast; Density is new in M2 (the Tiles grid density). UsageModel is new in the usage-model-bar plan (2026-08-30): which modelScoped entry the masthead's third readout shows, default "Fable". RailSort is new in the order-sidebar plan (2026-08-30): the rail's sort mode, "manual" | "attention", default "manual". Theme is new in new-ui-design-colors (2026-09-02): opaque to the daemon beyond its pattern (docs/protocol.md §3.3), default "follow".
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server holds musterd's HTTP mux and the long-lived pieces (ingest queue, WS registry, session manager) that outlive any single request.
func New ¶
New builds a Server and wires its routes. Nothing here starts a goroutine; call Start once the caller is ready to begin processing.
func (*Server) EndAllSessions ¶
EndAllSessions ends every currently alive session (REQ-3's `-on-exit=kill` path): a final snapshot, tmux kill, and alive:=false for each, before the rest of shutdown proceeds. cmd/musterd calls this itself, ahead of Shutdown, once it has resolved (and, for `-on-exit=ask`, prompted) the final leave/kill decision — Shutdown's own signature is unchanged so it keeps working for every existing caller that never deals with the on-exit policy at all. Returns how many sessions were ended.
func (*Server) LiveSessionCount ¶
LiveSessionCount reports how many sessions are currently alive — used by cmd/musterd's `-on-exit=ask` prompt to decide whether to prompt at all (REQ-3: zero live sessions, no prompt, no log line).
func (*Server) Shutdown ¶
Shutdown closes every open WS connection (unblocking their handler goroutines, which http.Server.Shutdown cannot do for hijacked connections), stops the liveness poll, and drains the ingest queue, giving up when ctx is done.
func (*Server) Start ¶
func (s *Server) Start()
Start reloads persisted sessions, reconciles them against tmux reality, begins the liveness poll, and begins asynchronous ingest processing. Call once, after New — Reconcile (m4-reconcile REQ-1) runs synchronously here, before the caller starts accepting connections, so neither `/ws` nor `GET /api/state` can observe a pre-reconcile session list.
func (*Server) TmuxSocket ¶
TmuxSocket returns the dedicated tmux socket this daemon instance is bound to — used by cmd/musterd's `-on-exit=ask` prompt copy ("N live sessions on tmux socket X").
type Snapshot ¶
type Snapshot struct {
Sessions []sessionWire `json:"sessions"`
Usage UsageInfo `json:"usage"`
Prefs PrefsInfo `json:"prefs"`
ClaudeTheme ClaudeThemeInfo `json:"claudeTheme"`
}
Snapshot is the daemon's full state, shared verbatim (aside from the WS "type" wrapper) between GET /api/state and the WS `snapshot` message (docs/protocol.md §5.2) — one function builds it so the two can never drift apart.
type UsageBucket ¶
UsageBucket is one of the five-hour/seven-day usage readouts (docs/protocol.md §5.4). M0 never populates it; it exists so the null shape below type-checks.
type UsageInfo ¶
type UsageInfo struct {
FiveHour *UsageBucket `json:"fiveHour"`
SevenDay *UsageBucket `json:"sevenDay"`
Model *sessionWireModel `json:"model"`
SampledAt *string `json:"sampledAt"`
Source string `json:"source"`
ModelScoped []UsageModelWindow `json:"modelScoped"`
ModelScopedAt *string `json:"modelScopedAt"`
ModelScopedError *string `json:"modelScopedError"`
ModelScopedSource string `json:"modelScopedSource"`
}
UsageInfo is the `usage` object inside a snapshot. M0 has no usage samples yet, so every field renders null/"unknown" (REQ-16). Model is new in M3 (§5.4): the freshest sample's model, for the masthead readout — present as an explicit `null` key (no `omitempty`), matching FiveHour/SevenDay/SampledAt: "null iff buckets null" per the plan's Protocol Contract, not an absent key. ModelScoped/ModelScopedAt/ModelScopedError are the second, independent usage source (plan usage-model-bar, 2026-08-30): nil slice/pointer renders `null` on the wire (no `omitempty`) until the first successful fetch; an empty-but-non-nil ModelScoped is a valid, distinct successful result. ModelScopedSource is a wire constant in v1, never empty.
type UsageModelWindow ¶
type UsageModelWindow struct {
DisplayName string `json:"displayName"`
UsedPct float64 `json:"usedPct"`
ResetsAt string `json:"resetsAt"`
}
UsageModelWindow is one entry of the `usage.modelScoped` list (docs/protocol.md §5.4, plan usage-model-bar) — one per-model weekly usage window.