apt.install, service, files, dirs — installed, running, owned as declared.
agentless configuration over ssh
shellf_
Write down the state your servers should be in — shellf makes them match, and shows you exactly what will change before it touches anything.
Configuration as plain shell — idempotent, previewable, fast.
What it does
template fills @{vars} from the inventory, rendered per host.
docker.compose-up, docker.network — --check previews the recreate.
ufw.open / default / enable — only the ports you list.
dir-copy byte-for-byte (binary-safe), file-download sha256-checked.
A shell { } block, gated by the same contract.
Also: secrets off disk, hash-verified modules, ssh-agent auth, for / with { }.
The stdlib is early and growing — missing an instruction? Propose a def ↗
Write a plan
defaults = { user: "deploy" } host web1 = { address: "10.0.0.1", role: "edge" } group web = [web1]
on web { apt.install("nginx") template("nginx.conf", "/etc/nginx/nginx.conf") service("nginx", "true", "true") # running, enabled # raw shell — still gated by the same contract if !shell { nginx -t } { service-reload("nginx") } }
Why it beats a script
Checks first, skips what's done. Re-run the whole plan; only drift changes.
--check shows what would change; status diffs current vs. desired. Touches nothing.
One SSH connection, an agent on the host. No round-trips, no runtime.
Architecture
One static binary reads your plan. The only place shellf is installed.
Pushed as an ephemeral agent; evaluates on the host, applies only the drift. Secrets on tmpfs.
The agent self-erases. No daemon, no state file, nothing installed.
Install
or build from source: go build -o shellf ./cmd/shellf