Applying changes
Applying is two steps: compile your Emet to a manifest, then ship it to the
agent. golemctl apply does both — carrying on from the web.emet you wrote
in The config.
golemctl apply web.emet http://127.0.0.1:7474That address is a local agent. A deployed one binds loopback and is named
ssh://[user@]host[:port] instead — golemctl opens the SSH forward and
carries the shared bearer token from GOLEM_AUTH_TOKEN or
GOLEM_AUTH_TOKEN_FILE. Every command on this page takes either shape. See
Trust model.
Read the diff first
golemctl plan asks the agent what an apply would do and writes nothing:
golemctl plan web.emet http://127.0.0.1:7474It answers with one collapsed line per action and a change count, diffed
against golem’s own journal — golem’s record of what it has already applied.
--detail expands every group to one glyph per line with content ids. A plan
is safe to run while an apply is in flight.
Plan against the host, not just the journal
The journal is silent about anything golem did not do itself. Point golem at
a host another tool already configured — Ansible, a hand-run script, a human
with a shell — and the journal is empty, so every glyph in the manifest reads
as an install, even one the host already has byte for byte. --against-host
answers the question the journal can’t: not what golem’s records say, but
what is actually on the box right now, read live from its filesystem,
systemd, and dpkg.
golemctl plan web.emet http://127.0.0.1:7474 --against-hostThe report grows a second block beneath the journal one, the same glyphs checked against reality:
Plan for web-01 · against no prior revision · against the host · manifest 3f9c1a…
journal + install 12 apt packages nginx curl jq git vim htop tmux rsync … and 4 more (web/base, web/extra) + install 4 files /etc/nginx/nginx.conf /etc/ssh/sshd_config /etc/motd /etc/hosts.allow (web/nginx, web/base) + install 1 systemd unit nginx.service (web/nginx)
host = match 17 glyphs
17 changes · 17 install host · every declared glyph already matches — applying this manifest changes nothingThat is the case --against-host exists for: proving, before the first
apply of a fleet’s life, that a host already matches the manifest and
applying it changes nothing — a statement no amount of reading the journal
could produce, because the journal has nothing in it yet.
--against-host is opt-in because it costs more, not because it needs more
authority. It runs the same per-glyph checks apply already runs before
touching anything, against the same files, units, and packages the manifest
already names — a change in how much work plan does, not in what it is
allowed to touch. It also surfaces drift the journal-only plan is blind to: a
glyph whose content id hasn’t moved is a silent Noop against the journal
even if someone hand-edited the file since, or a unit was stopped by hand —
the host block reports that row as disagreeing while the journal still says
there is nothing to do. Anything golem can’t read on the host — a file it
lacks permission for, a secret it has no key for — reports unknown rather
than a guess, and an unknown never counts toward “already matches.”
See CLI reference for the flag on fleet plan and the
/plan endpoint, and Trust model for what a
secret-bearing file’s row can and can’t reveal.
What golemctl apply does
-
Compiles (if given
.emet). For a.emetsource,golemctlshells out toemetc buildand captures the binary manifest — a content-addressed artifact carrying every host’s scroll. Hand it a prebuilt.manifestinstead and it ships those bytes directly. -
Posts it.
POST /manifestto the agent. The manifest is the whole fleet; the agent picks out its own host. -
Follows the reconcile. The POST comes back at once with a
reconcile_id;golemctlthen polls that reconcile and draws the units as they settle, ending on the report.
You can also split compile from ship:
emetc build web.emet -o web.manifest # compileemetc build web.emet --text # eyeball the plangolemctl apply web.manifest http://127.0.0.1:7474What the agent does on receipt
POST /manifest returns 202 Accepted with {"reconcile_id": N} as soon as
the manifest is ingested; the reconcile runs detached from the request. A
reconcile can take tens of minutes — an apt update, a cold image pull — and a
held-open request loses its report the moment the connection drops. Progress
is read back instead from GET /reconciles/:id (or /reconciles/latest),
which takes ?after=<seq> so a follower resumes where it left off.
On the request itself:
- Decode + check
format_version. - Select the
AddressedScrollwhosescroll.namematches its--host. Every other host’s scroll is ignored.
Then, detached:
- Diff the desired scroll’s glyphs against its journal, by glyph key
and content id, into ordered ops:
Install,Remove,Replace,Noop. - Enact each op through a reversible reconciler, capturing the prior host state so the edit can be undone.
- Journal the ordered outcomes as a
Reconcilerevision.
Because the diff is content-addressed, re-applying an unchanged fleet is all no-ops. See Reversible reconcile for the mechanism.
Inspecting a node
# The applied scroll and its content id.golemctl state http://127.0.0.1:7474
# The revision journal.golemctl history http://127.0.0.1:7474
# One revision — its ops and reversal receipts.golemctl show http://127.0.0.1:7474 1More than one host
Naming an address per host stops scaling at about two. golemctl fleet reads
a TOML inventory instead and fans the same verbs out concurrently, one
connection per host:
golemctl fleet statusgolemctl fleet plan web.emet --hosts web-1,web-2golemctl fleet apply web.emetOne host’s failure never stops the others, and a host the manifest names no scroll for is skipped rather than emptied. The inventory’s fields are in the CLI reference.
Rolling forward
Edit your Emet, re-apply. The changed glyphs get new content ids and are replaced (reverse the old version, apply the new); everything unchanged is a no-op. There is no version number to type — content ids are the version axis.
# Bump an image tag in your Emet, then:golemctl apply web.emet http://127.0.0.1:7474Applying is also how you go back: edit the source to the version that worked and apply again. And if a unit of the new state fails, it returns to its last committed state on its own while the rest of the host settles forward, so trying a change you are unsure of costs one apply. See Reversible reconcile.
Taking it back
Remove a glyph (or a whole abstraction) from your Emet and re-apply. The
glyphs no longer in the scroll become Remove ops — each the exact
recorded uninstaller. A package golem installed is removed; a package that
was already on the box is left alone. “Remove everything” is applying a
scroll with no glyphs.
Where to next
- A worked example: A first glyph
- The full CLI: CLI reference
- The bytes the agent sees: Manifest format
- How reversal works: Reversible reconcile