banger/internal/daemon/ARCHITECTURE.md
Thales Maciel ca4865447c
Refresh daemon docs and mark web UI experimental
internal/daemon/doc.go and ARCHITECTURE.md were written before the
subpackage extractions and still referenced old structure (in-progress
phrasing, missing opstate/dmsnap/fcproc/imagemgr/session/workspace,
mentions of opRegistry by its old name). Both now describe the current
shape: composition root + six leaf subpackages, lock ordering rooted
at vmLocks[id], and the one intra-package dependency (workspace →
session for ShellQuote + FormatStepError).

README.md and AGENTS.md mark the local web UI as experimental. It is
still enabled by default at 127.0.0.1:7777, but the docs now state
plainly that its surface is not stable or hardened and not intended for
anything beyond single-user localhost use. AGENTS.md also points at
ARCHITECTURE.md for the subpackage layout.

No code changes; tests still green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 16:44:11 -03:00

4 KiB

internal/daemon architecture

This document describes the current daemon package layout: the Daemon composition root, the subpackages that own stateless helpers and shared primitives, and the lock ordering every caller must respect.

Composition

Daemon is the composition root. Subsystem state and locks live on their owning types:

  • Layout, config, store, runner, logger, pid — infrastructure handles.
  • vmLocks vmLockSet — per-VM *sync.Mutex, one per VM ID.
  • createVMMu sync.Mutex — serialises CreateVM (guards name uniqueness
    • guest IP allocation window).
  • imageOpsMu sync.Mutex — serialises image-registry mutations (BuildImage, RegisterImage, PromoteImage, DeleteImage).
  • createOps opstate.Registry[*vmCreateOperationState] — in-flight VM create operations; owns its own lock.
  • imageBuildOps opstate.Registry[*imageBuildOperationState] — in-flight image build operations; owns its own lock.
  • tapPool tapPool — TAP interface pool; owns its own lock.
  • sessions sessionRegistry — active guest session controllers; owns its own lock.
  • listener, webListener, webServer, webURL, vmDNS — networking.
  • vmCaps — registered VM capability hooks.
  • imageBuild, requestHandler, guestWaitForSSH, guestDial, waitForGuestSessionReady — injectable seams used by tests.

Subpackages

Pure helpers have moved into subpackages so the daemon package itself stays focused on orchestration. Each subpackage takes explicit dependencies (typically a system.Runner-compatible interface) and holds no global state beyond small test seams.

Subpackage Purpose
internal/daemon/opstate Generic Registry[T AsyncOp] for async-operation bookkeeping.
internal/daemon/dmsnap Device-mapper COW snapshot create/cleanup/remove.
internal/daemon/fcproc Firecracker process primitives (bridge, tap, binary, PID, kill, wait).
internal/daemon/imagemgr Image subsystem pure helpers: validators, staging, build script gen.
internal/daemon/session Guest-session helpers: state paths, scripts, parsing, utilities.
internal/daemon/workspace Workspace helpers: git inspection, copy prep, guest import script.

workspace imports session for ShellQuote and FormatStepError; all other subpackages are leaves (no other intra-daemon subpackage imports).

Lock ordering

Acquire in this order, release in reverse. Never acquire in the opposite direction.

vmLocks[id]  →  {createVMMu, imageOpsMu}  →  subsystem-local locks

Subsystem-local locks (tapPool.mu, sessionRegistry.mu, opstate.Registry mu, guestSessionController.attachMu / writeMu) are leaves. They do not contend with each other.

Notes:

  • vmLocks[id] is the outer lock for any operation scoped to a single VM. Acquired via withVMLockByID / withVMLockByRef.
  • createVMMu and imageOpsMu are narrow: each guards one family of mutations and is released before any blocking guest I/O.
  • Holding a subsystem-local lock while calling into guest SSH is discouraged; copy needed state out under the lock and release before blocking I/O.

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.

Web UI

The optional web UI served at web_listen_addr is experimental. It is enabled by default for local observability but is not considered a stable or supported interface. Set web_listen_addr = "" in config to disable.