caravan: declarative provisioning that converges a provider to a spec
The InfraKit-shaped companion to uniboot and uplink: uniboot builds
and publishes the immutable image, caravan makes the provider
substrate that runs it exist and match a declared target, uplink moves
signed updates onto machines already running. The planner is a port of
the SpaceOS provisioner's substrate planner, made provider-neutral and
relicensed ISC by its copyright holder; the spec tests come with it.
The planner is pure -- spec and observation in, ordered actions or a
conflict out -- and three rules shape every plan. The data volume is
never destroyed: drift that only destruction can close is a conflict,
and resolving one is an explicit act (apply --force runs only the
non-destructive resolutions: detaching a stale holder, adopting a
pre-caravan resource by tagging it). Compute is disposable: a changed
provision input replaces the instance, preserving its volume and
address. Routine image updates are out of scope: an instance that
updated itself over the air keeps its provision tag and is left alone.
There is no state file. Every resource caravan creates carries the
identity tag namespace:name, and resolution requires name and tag
together, so a same-named resource caravan did not create -- a stale
leftover or a plant aimed at becoming the node's persistent state --
is a Foreign_resource conflict instead of a silent adoption. The
provision input rides on the instance as a second tag, the way
InfraKit recorded its config SHA.
Backends implement one PROVIDER contract; Caravan.Make executes plans
over any of them, resolving the ids of resources created earlier in
the same apply. caravan.scaleway is the first backend, and the CLI
(plan, apply, status) prices billable steps from the provider's
server-type catalog and totals them. The engine tests run over an
in-memory provider and require convergence: after an apply, the next
plan is empty.