st8ctl (like "state") safely applies, tracks, diffs, checkpoints, rolls back, and branches config state.
It is built around a simple idea: state changes should feel more like Git and less like scp plus hope.
st8ctl gives config and operational state a proper lifecycle:
- review what will change
- apply it as an immutable revision
- mark known-good checkpoints
- branch for experiments
- restore or roll back without rewriting history
It is a pure client for st8d — all state lives in the daemon, st8ctl only talks to it over HTTP.
Under the hood, st8d uses t4 as its embedded persistence engine.
State is organized into namespaces and branches.
A namespace is a flat string that scopes state to a logical owner — a service, team, or environment. You choose the naming convention: payments/prod, platform-dev, acme — whatever makes sense for your workflow. The default namespace is default.
A branch works like a Git branch: the same namespace can have multiple branches so you can draft changes, experiment, or promote state without touching the live revision history. The default branch is main.
With st8ctl, you can:
- apply config changes as immutable revisions
- inspect current and historical state
- diff pending changes before applying them
- create named checkpoints
- roll back instantly to any revision or checkpoint
- branch state for experiments
- restore from another branch
st8ctl supports two ways to connect to st8d:
-
Remote —
st8ctl --server http://host:8748(orserver.urlin config) talks to a runningst8ddaemon. -
Local demo —
st8ctl --localstarts a temporaryst8dinstance for the lifetime of the command. Useful for demos and local testing without running a separate process.
Create ~/.config/st8ctl/config.yaml to avoid repeating flags:
server:
url: http://st8d.internal:8748
token: my-secret-token
defaults:
namespace: payments/prod
branch: mainFor local demo mode:
server:
url: local
dir: ~/.st8
defaults:
namespace: payments-devCreate a config file:
cat > config.json <<'EOF'
{
"enabled": true,
"timeout": 30
}
EOFStart a temporary local st8d and apply:
st8ctl --local --namespace payments/prod apply -f config.jsonSee history:
st8ctl --local --namespace payments/prod logCreate a checkpoint:
st8ctl --local --namespace payments/prod checkpoint before-changeRead current state:
st8ctl --local --namespace payments/prod get
st8ctl --local --namespace payments/prod get config.json--local starts a temporary st8d for the duration of the command. For persistent state, run st8d yourself (see below) and point st8ctl at it.
Start the daemon:
st8d --listen :8748 --state-dir .st8dProtect the API with a bearer token:
st8d --listen :8748 --state-dir .st8d --token my-secret-tokenEnable TLS by supplying a certificate and key:
st8d --listen :8748 --state-dir .st8d \
--tls-cert /etc/st8d/server.crt \
--tls-key /etc/st8d/server.keyProxy alternative: if you already have an nginx / Caddy / Envoy reverse proxy in front, simply terminate TLS there and let
st8dlisten on plain HTTP on a local port.
Then point the CLI at it:
st8ctl --server http://127.0.0.1:8748 --namespace payments/prod apply -f config.json
st8ctl --server http://127.0.0.1:8748 --namespace payments/prod log
st8ctl --server http://127.0.0.1:8748 --namespace payments/prod checkpoint before-changest8ctl --namespace payments/prod diff config.json
st8ctl --namespace payments/prod apply -f config.jsonst8ctl --namespace payments/prod rollback --checkpoint before-changest8ctl --namespace payments/prod rollback --revision 1rollback does not rewrite history. It creates a new revision whose contents match the target state.
Example:
- revision 1: good config
- revision 2: bad config change
st8ctl rollback --revision 1- revision 3 is created with the same contents as revision 1
st8ctl --namespace payments/prod branch create migration-test --checkpoint before-change
st8ctl --namespace payments/prod --branch migration-test apply -f config.json
st8ctl --namespace payments/prod --branch migration-test logst8ctl --namespace payments/prod restore --from-branch migration-testapply Apply one or more files into state
get Read current or historical state
diff Compare current state to files or a prior revision
checkpoint Create a named checkpoint
checkpoint delete Delete a named checkpoint
rollback Roll back to a revision or checkpoint
log Show revision history
branch List branches or create a new branch
restore Restore state from a revision, checkpoint, or branch
gc Garbage-collect old revisions (--keep N, default 10)
applystores each input file under its normalized path as the state key.--localis per-command demo mode, not a persistent auto-reused daemon.st8dexposes an HTTP JSON API thatst8ctluses in all modes.