HTTP API
Base URL is wherever --http points; 127.0.0.1:8080 by default. Every endpoint
except /healthz requires Authorization: Bearer <token> when a token is
configured.
Interactive reference
The document below is generated from the agent’s route table by
cmd/openapi-gen, so it describes the endpoints the binary actually serves. CI
regenerates it and fails if it differs from the checked-in copy — a renamed field in
a response type cannot reach main with the spec still claiming the old name.
Use it with your own tools
The raw document is published alongside this page:
openapi.yaml. Point oapi-codegen, openapi-generator or your
HTTP client at it, or run task openapi in a checkout to regenerate it.
The endpoints in prose
The generated reference above is exhaustive. What follows is the same surface with the reasoning attached — why an endpoint behaves the way it does, and the traps.
GET /healthz
Exempt from authentication, and exposes no component detail — safe for a load balancer or a systemd health check.
GET /metrics
Prometheus exposition format. See Metrics.
GET /v1/components
state is one of none, stopped, running, failed. last_health is
healthy, unhealthy or unknown. pid is 0 for container components and for
anything not running. The guarantees these fields carry are in
Component state.
GET /v1/plan/status
error is present only when the last operation failed. Note that this endpoint
carries the component list too, so a fleet poller needs one request, not two.
GET /v1/plan/graph
edges maps a dependency to the components that depend on it. order is a valid
start order.
POST /v1/plan/apply
Body: the plan TOML. Query: dry=true to preview.
202 Accepted on success, 500 with the error text on failure (including a
rollback summary), 400 if the body is not a usable plan.
Note
A JSON body with planPath is rejected. Upload content — letting the API name
a local path would turn it into a file-read primitive.
POST /v1/plan/stop
Stops every component in reverse dependency order and marks the plan stopped,
which also means it will not be resumed on the next boot.
GET /v1/recipes
Lists name:version entries in the agent’s recipe store.
POST /v1/recipes
Body: recipe TOML. Stores it so plans can refer to it as name:version. Recipes
added this way are trusted through API authentication rather than a file signature.
?force=true overwrites an existing version.
DELETE /v1/recipes/{name}/{version}
Removes a recipe from the store. Name and version are validated against an allow-list — no path traversal.
POST /v1/components/{name}:stop
Stops one component. Its dependents are not stopped.
POST /v1/components/{name}:restart
| Parameter | Values | Meaning |
|---|---|---|
wait | pid (default), health | Return when a new PID exists, or when it probes healthy |
timeout | duration, e.g. 60s | How long to wait |
dry | true | Report what would be restarted, change nothing |
dependents maps each cascaded component to its new PID, so one call tells you
everything that moved. Dependents are restarted according to each edge’s
dependency type — hard and soft cascade,
ordering does not.
With dry=true the shape is different — the plan, not the result: