The four glyphs
A glyph is one bottom-level OS resource. Emet has exactly four glyph
primitives, and golemd enacts exactly these four — nothing else. Every
higher-level shape you write (a container workload, a service, an ingress)
is an Emet function that returns a List Glyph and lowers to these.
Each glyph is a reserved record constructor in Emet. Every field is a
fully-evaluated concrete String — there is no templating, no placeholder
substitution, no ${…} in the compiled output. String interpolation and
String.join run at compile time; the glyph carries only the final bytes.
aptPackage
Ensure an apt package is installed.
aptPackage { name = "nginx" }| Field | Type | Meaning |
|---|---|---|
name | String | the package to install |
Glyph key: apt:<name>.
Reversibility. On apply, golemd queries whether the package is
already installed (dpkg-query). If not, it runs apt-get update and then
apt-get install -y, recording “installed by us”; on reverse it removes it.
The refresh is per-glyph: a fresh Debian cloud image ships with an empty
package list, so an install would fail to resolve without one. If the
package was already present, apply is a no-op and reverse leaves it alone.
Golem never removes a package it did not install.
systemdService
Ensure a systemd unit is enabled and started.
systemdService { unit = "nginx.service" }| Field | Type | Meaning |
|---|---|---|
unit | String | the unit to enable --now |
Glyph key: systemd:<unit>.
Reversibility. On apply, golemd records the unit’s prior
enabled/active state, runs systemctl daemon-reload so a just-written unit
file is visible, then systemctl enable --now. On reverse it restores the
prior state — if golem enabled it, disable --now; if it was already
enabled, leave it.
A generated unit refuses enable: a Podman quadlet is already enabled
by its generator through an [Install] section, so enable --now rejects
it as transient or generated. On that specific failure golemd falls back
to systemctl start and records that it only started the unit — reverse
then stops it and never disables it. Any other enable failure fails the
glyph.
file
Ensure a file exists with fixed contents and mode.
file { path = "/etc/app/site.conf" , contents = "[server]\nlisten = 8080\n" , mode = "0644" }| Field | Type | Meaning |
|---|---|---|
path | String | absolute path on the host |
contents | String | the exact file body |
mode | String | octal mode, e.g. "0644" |
Glyph key: file:<path>.
mode is written as a string, with or without a 0o prefix — "0644" and
"0o644" are the same mode. emetc parses it as octal into the 12
permission bits and the wire carries a u16, so a non-octal mode or one
above 0o7777 is a compile-time error, not a reconcile-time failure.
The wire Perms also carries owner and group — names, resolved to
uid/gid at reconcile time. The surface constructors do not expose them, so
every authored entry leaves them None and ownership unmanaged.
Reversibility. On apply, golemd reads the prior contents and mode
(or notes the file was absent), then writes the desired contents atomically
(temp file + rename). On reverse it restores the prior bytes and mode, or
deletes the file if golem created it.
directory and symlink — the same glyph, two more spellings
file is one of three surface spellings of the filesystem glyph. The other
two build a directory or a symlink at a path:
directory { path = "/var/lib/registry", mode = "0755" }
symlink { path = "/etc/nginx/sites-enabled/site.conf", target = "/etc/nginx/sites-available/site.conf" }Each arm carries only its own fields — directory takes no contents,
symlink takes neither contents nor mode — so a symlink with a mode or a
directory with contents cannot be written down. All three share the
file:<path> key, reconcile through the one filesystem reconciler, and
reverse the same way (restore the prior entry, or remove what golem created).
A Quadlet FromHost mount emits a directory for
its bind-mount source, which is where most of them come from.
lineInFile
Ensure a single line is present in a file golem does not own — a file the distro, another tool, or a human wrote, which golem needs to amend rather than author.
lineInFile { path = "/etc/hosts" , line = "10.0.0.7 registry.internal" }| Field | Type | Meaning |
|---|---|---|
path | String | the file to edit |
line | String | the exact line to ensure present |
Glyph key: fileline:<path>:<line>.
Reversibility. On apply, if the line is already present, apply is a
no-op and reverse does nothing. Otherwise golemd appends it and records
that it added the line; on reverse it removes exactly that line. Golem
never removes a line it did not add.
Conflict rules within a leaf
The conflict scope is the leaf unit, not the whole scroll. Two glyphs
with the same key inside one leaf must be identical, or the compiler’s
pre-apply analysis rejects them at the conflicting glyph’s source span.
Sibling leaves inside one scroll may share a key, and two different
scrolls may share one freely — two hosts installing nginx is fine.
Uniqueness is never fleet-wide.
Building abstractions
Raw glyphs are rare beyond the smallest hosts. They compose either through a
function returning a List Glyph, or through the shipped
Quadlet library, whose Workload lowers a typed
container spec down to exactly these glyphs:
import Quadlet exposing ( image, tcp, Restart(..), Expose(..), Workload(..), workloadGlyphs )
registry : List Glyphregistry = workloadGlyphs (Workload { name = "registry" , image = image "docker.io/library" "registry" "2" , env = [], ports = [ tcp 5000 5000 ], volumes = [] , restart = Always, expose = Internal })That lowers to the podman aptPackage, the .container quadlet file, its
systemdService, one nftables drop-in file for the internal port opening, and
the nftables base that loads it — glyph kinds the agent already understands, no
fifth kind. See the
Quadlet reference, the
service abstraction guide, and the
lichess tour for the full pattern.