Desired state
Hostwright is a desired-state system. You declare what should run; Hostwright observes what is running, computes the difference, and reports it as drift with a deterministic plan.
Two kinds of state
Section titled “Two kinds of state”- Desired state: what you declared in
hostwright.yaml. Persisted as a snapshot to the explicit SQLite database you pass with--state-db. - Observed state: what is running, read back from the runtime
through the RuntimeAdapter at a point in time.
hostwright status --state-db <path>records it as an observed snapshot.
The reconciler compares the two, emits typed drift records and issues, and produces a plan with a deterministic hash. Convergence is never automatic: mutation happens only through a confirmed lifecycle plan.
Why declarative
Section titled “Why declarative”Imperative commands describe how; a manifest describes what. A declarative model provides properties that imperative commands do not:
- Idempotency: a lifecycle command recomputes the plan and refuses to run when the confirmed hash no longer matches reality, so a stale intent cannot re-execute.
- Drift detection: with desired and observed snapshots both recorded, Hostwright reports when observed state diverges from desired state.
- Reviewable change: the difference is shown as a plan, with a hash, before anything mutates.
- Recovery: operation intent, checkpoints, and recovery records are
persisted before mutation, so an interrupted operation is diagnosable with
hostwright recovery.
The unit of declaration
Section titled “The unit of declaration”A manifest declares a project that contains one or more services. A service references an image and the runtime details needed to run it: ports, environment, secret references, health checks, and restart policy.
version: 2project: api-local
services: api: image: ghcr.io/example/api:latest ports: - "8080:8080"What convergence is not
Section titled “What convergence is not”Hostwright does not run an aggressive convergence loop. The foreground
daemon observes, plans, and records events; it never
calls a lifecycle command itself. Mutation stays reserved for an operator (or
an operator-triggered script) running up, down, run, start, stop,
restart, rm, or update with an explicit --confirm-plan. A confirmed
plan can converge every affected service in one invocation, in dependency
order, but only the services and actions named in that exact plan; see
Limitations for what recovery can and cannot do
when a plan is interrupted partway through.
