Reconciliation
The reconciliation loop observes, diffs, plans, applies, and observes again — boringly, deterministically, and with every step recorded.
Reconciliation is the heart of Hostwright. It should be boring: deterministic, observable, and hard to corrupt. Every cycle treats desired state and observed state as separate inputs, computes a plan, applies actions idempotently, and records what happened.
The loop
load desired state from the state store
observe runtime state through the RuntimeAdapter
build a runtime snapshot with timestamps
compare desired state to observed state
produce a deterministic diff
produce an ordered action plan
validate safety gates
persist plan and intent
apply actions idempotently
record events and status transitions
observe again
The diff and the plan are first-class artifacts. hostwright plan shows you the
ordered actions before hostwright apply runs them.
Dry-run and apply
plancomputes the diff and the action plan and prints it. Nothing in the runtime changes.applyruns the plan, persisting intent before each mutation and the result immediately after.
Because intent is persisted before mutation, a daemon restart in the middle of an apply does not lose track of what was being attempted.
Idempotency
Applying a converged state is a no-op. The rules that make this safe:
- Creating an already-correct container is a no-op or a clear “already converged” result.
- Starting an already-running container is not an error.
- Stopping an already-stopped container is safe.
- Deleting a missing Hostwright-owned container is safe.
- Resources created out-of-band are never deleted without ownership proof.
Drift
Drift is the gap between what you declared and what is running. Hostwright
reports drift for resources that are missing, stopped, unhealthy, or modified
out from under it. Drift is surfaced in hostwright status, not silently repaired
in ways you cannot see.
Events
Every meaningful step emits a structured event so failures are explainable after the fact:
Event {
type: planned | applied | failed | health_changed
| drift_detected | restarted | cleaned
severity: info | warning | error
project, service?, instance?
message, runtimeRef?, timestamp, correlationID
}
Open questions
- Should rollback for a partial apply be automatic, manual, or plan-only by default?
- How aggressively should the loop re-reconcile on drift versus waiting for an explicit
apply?