HTTP

On by default, bound to 127.0.0.1:8080. Disable it with --http "".

See API authentication before exposing it off-device — the agent will refuse a non-loopback bind without a token.

Endpoints

MethodPathDoes
GET/healthzAgent liveness. Exempt from auth
GET/metricsPrometheus metrics
GET/v1/componentsEvery component with state, PID, restarts, health
GET/v1/plan/statusPlan path, status, last error
GET/v1/plan/graphNodes, edges and topological start order
POST/v1/plan/applyApply a plan (body = plan TOML). ?dry=true to preview; the result goes to the agent’s log
POST/v1/plan/reconcileRepair the plan in effect: restart components that died and ran out of retries
POST/v1/plan/stopStop every component
GET/v1/recipesList recipes in the store
POST/v1/recipesAdd a recipe (body = recipe TOML)
DELETE/v1/recipes/{name}/{version}Remove a recipe from the store
POST/v1/components/{name}:stopStop one component
POST/v1/components/{name}:restartRestart one component, cascading per dependency type

Examples

# What is running
curl -s localhost:8080/v1/components | jq

# Apply a plan (content, not a path)
curl -X POST --data-binary @plan.toml localhost:8080/v1/plan/apply

# Preview a plan
curl -X POST --data-binary @plan.toml "localhost:8080/v1/plan/apply?dry=true"

# Restart one component and wait until it is healthy again
curl -X POST "localhost:8080/v1/components/api:restart?wait=health&timeout=60s"

# With a token
curl -H "Authorization: Bearer $KEYSTONE_API_TOKEN" https://device/v1/plan/status

The restart endpoint takes wait=pid (default — return once a new PID exists) or wait=health (return once it probes healthy), and dry=true to see what a restart would cascade to without doing it.

keystonectl

The same API with less typing:

keystonectl status
keystonectl components
keystonectl apply plan.toml
keystonectl restart api
keystonectl stop api          # one component
keystonectl stop-plan         # everything

It reads KEYSTONE_API_TOKEN from the environment and takes --addr for a remote agent. See the keystonectl reference for every subcommand.

Bruno collection

bruno/ in the repository is a ready-made Bruno collection with every request above, which beats hand-writing curl while you are exploring.