Skip to content

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" }
FieldTypeMeaning
nameStringthe 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" }
FieldTypeMeaning
unitStringthe 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"
}
FieldTypeMeaning
pathStringabsolute path on the host
contentsStringthe exact file body
modeStringoctal 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.

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"
}
FieldTypeMeaning
pathStringthe file to edit
lineStringthe 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 Glyph
registry =
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.