# `internal/daemon` architecture This document captures the current (pre-refactor) layout of the daemon package and the lock ordering its callers must respect. It is the baseline against which the phased split described in `~/.claude/plans/fluffy-seeking-teapot.md` is executed. ## Composition `Daemon` is a single struct aggregating state for every subsystem: - Layout, config, store, runner, logger, pid — infrastructure handles. - `mu sync.Mutex` — coarse lock, currently guards guest session controller map mutations and image registry mutations. - `vmLocks sync.Map` — per-VM `*sync.Mutex`, one per VM ID. - `createOps`, `createOpsMu` — in-flight `vm create` operations. - `imageBuildOps`, `imageBuildOpsMu` — in-flight `image build` operations. - `tapPool`, `tapPoolNext`, `tapPoolMu` — TAP interface pool. - `sessionControllers` — active guest session controllers (guarded by `mu`). - `listener`, `webListener`, `webServer`, `webURL`, `vmDNS` — networking. - `vmCaps` — registered VM capability hooks. - `imageBuild`, `requestHandler`, `guestWaitForSSH`, `guestDial`, `waitForGuestSessionReady` — injectable seams used by tests. ## Lock ordering Acquire in this order, release in reverse. Never acquire in the opposite direction. ``` vmLocks[id] → mu → {createOpsMu, imageBuildOpsMu, tapPoolMu} ``` Notes: - `vmLocks[id]` is the outer lock for any operation scoped to a single VM. Acquired via `withVMLockByID` / `withVMLockByRef`. - `mu` is currently load-bearing for both session controller lookups and image registry changes. Holding it while calling into guest SSH is discouraged; prefer copying needed state out under the lock and releasing before blocking I/O. - The three subsystem locks (`createOpsMu`, `imageBuildOpsMu`, `tapPoolMu`) are leaves. Nothing else is acquired while one is held. The upcoming Phase 2 refactor will retire `mu` entirely by giving each concern it currently guards its own owning type and lock. At that point the ordering collapses to `vmLocks[id] → subsystem-local lock`. ## External API Only `internal/cli` imports this package. The surface is: - `daemon.Open(ctx) (*Daemon, error)` - `(*Daemon).Serve(ctx) error` - `(*Daemon).Close() error` - `daemon.Doctor(...)` — host diagnostics (no receiver). All other `*Daemon` methods are reached only through the RPC `dispatch` switch in `daemon.go` and are free to move/rename during refactoring.