Repository navigation
v0.8.0
Changelog
Three things done by hand now have commands. A release can declare tasks an
operator runs by name, installation remove takes an installation off the
machine, and update generates a secret the new release adds. A bundle author
working with an AI coding agent can give it a skill for writing bundles. Two
writes to the secret state at once no longer lose one of them, init keeps the
recovery key when it has no secret to write, and exec says when it cut a
command's output.
Upgrading changes no schema: the installation stays at 12, and a bundle built
for 0.7.0 installs unchanged. A manifest that declares tasks: needs
compatibility.min_manager_version: "0.8.0". Six things behave differently:
updategenerates each secret the new release declares with a generator
and has no value for, before it converges, and an unattended update does the
same. Such an update used to stop atload-secretsuntil somebody ran
morzer secret generate;update --generate-secrets=falsekeeps that, for
one run.- Writes to the secret state take turns under a new lock,
/var/lib/<product>/manager/locks/secrets.lock. Asecretcommand run while
another write is under way waits for it, a second or two, instead of racing
it. initwith--recovery-recipienton a release that declares no secrets
creates an empty secret state for the machine's key and the recovery key,
where it used to create none and drop the recovery key. A machine initialised
that way by an earlier version, that has written a secret since, has no
recovery recipient:morzer doctorwarns, and
morzer secret recipients add <age-public-key> --kind recoveryadds one.execandtask runprint a warning on stderr after output the runtime
cut at 256 KiB a stream. Stdout and exit codes are unchanged.- A command that prints a line longer than 4 MiB runs to completion. It used to
block on a full pipe and be killed at its timeout. - The journal, notifications and attestations can carry two new operation
types,taskandremove, and a task's container seesPHASE=task.
exec --json carries truncated when the output was cut, and task list,
task run and installation remove have payloads of their own. No existing
JSON field changed.
Added
- A release can declare tasks an operator runs by name. Filling a sandbox with demo data, unlocking a user or rebuilding an index meant
docker execby hand: knowing the Compose project's labels, naming the entrypoint wrapper that loads*_FILEsecrets, and having the container running, with nothing journalled and nothing stopping a sandbox's seed on production. A manifest's newtasks:names a service, a command, whether an operator may append arguments, and the machine modes it may run on.morzer task listshows them and whether this machine may run each;morzer task run <task> [-- args...]runs one as a one-shot container of the service, through its entrypoint, starting its dependencies, as the operationtask: under the deployment lock, journalled with its arguments scrubbed of known secret values, attested and notified. A task whosemodesleave out this machine's is refused by name before anything runs, and--forcedoes not override it. The task's output is printed when it finishes, its stdout on stdout, and the invocation exits with the task's own exit code. A manifest with tasks needscompatibility.min_manager_version: "0.8.0", so an older manager says it is too old. installation removetakes an installation off the machine. Nothing removed one, so retrying a failed first install, changing a mode fixed at creation, or retiring one of several products on a host meant deleting units, containers and directories by hand, with a glob that could reach another product's units and no record that it happened. The command runs as an operation, under the deployment lock, journaled step by step and plannable with--dry-run. It removes the units, the containers, the releases, the images no release left on the machine names, the rendered secrets, the configuration, the signing key and the installation record. By default it keeps the named volumes, the data and backup directories, the attestations, the journal, the secret state and the machine key, which unlock the kept data, and says where each is.--purgeremoves those too, leaving only the removal's own journal record and attestation. Nothing on a backup target is removed, and the removal's own attestation is pushed there. It needs--force, and on a production installation--confirm <installation-id>as well. A machine left by a failedinitis cleaned with--productand--force. The removal is attested and pushed like other operations, and the journal is renamed tooperations-removed-<operation-id>.jsonl, so a laterinitstarts with an empty one.updategenerates a secret the new release adds. Onlyinitgenerated secrets, so a release declaring a new secret with ageneratorstopped every existing machine atload-secretswithrequired secret(s) not set, and an unattended machine retried the same failure every tick until somebody ranmorzer secret generateby hand. An update now generates, before it converges, each secret the new release declares with a generator that has no value and that the release being replaced did not declare; the step names them, a plan names what it would generate, and no value is printed or journaled. A secret both releases declare and nobody set stays unset, a new one with no generator still stops the update, andupdate --generate-secrets=falseleaves the new ones to you. A value generated by an update that then fails is kept, so the retry uses the same one.applyandrollbackgenerate nothing.- A skill for AI coding agents that write a release bundle. An agent asked to author a bundle guessed at fields and conventions the documentation covers. The repository now ships
morzer-bundle-authoringin the Agent Skills format, installed withnpx skills add; it gives the agent the verify loop and the decisions a passing verify cannot catch, and sends it to the right page for each part of the bundle. The documentation build checks every command, long flag and link it names.
Fixed
execsays when it cut a command's output. The runtime keeps the first 256 KiB of each of a command's streams, andexecprinted that much as if it were all of it, somorzer exec db -- pg_dump > dump.sqlcould write a dump cut short with nothing on the terminal to say so. A warning on stderr now follows output that was cut, and--jsoncarriestruncated: true;task rundoes the same. The exit code is still the command's own.- A command that printed a line longer than 4 MiB no longer stalls until its timeout. The manager stopped reading a command's output at such a line, so a hook, migration service,
execor task that went on printing blocked on a full pipe and was killed at its timeout. The rest of the output is now read and dropped, and the output is reported as cut. - Two writes to the secret state at once no longer lose one of them. Every write decrypts the whole state, changes it and encrypts it back, and nothing stopped two from overlapping, so the later one saved over the earlier one's change: a
secret setwhile an update generated a secret its release adds, twosecretcommands in parallel, or a script setting several values at once. Writes now take turns under asecretslock, and reading takes none. initkeeps the recovery key when it has no secret to write. On a release that declares no secrets,initput off adding--recovery-recipientuntil the first secret was written and kept the key nowhere, so the state that first write created could be opened by the machine's key alone.initnow creates the state for both keys at once. A machine initialised that way by an earlier version, with secrets written since, has no recovery recipient, andmorzer doctorwarns about it;morzer secret recipients add <age-public-key> --kind recoveryadds one.
Installing this release
curl -fsSL https://morzecrew.github.io/morzer/install.sh | sh -s -- --version 0.8.0The script verifies the checksum always and the signature whenever
minisign is present; --require-signature makes a missing minisign fatal,
which is what a production runbook sets. --print-only shows everything it
detected and would do, and changes nothing. install.sh is an asset of this
release and is covered by SHA256SUMS below.
Verifying this release by hand
ARCHIVE=morzer_0.8.0_linux_amd64.tar.zst # the one you downloaded
curl -fsSLO https://raw.githubusercontent.com/morzecrew/morzer/main/morzer.pub
minisign -Vm SHA256SUMS -p morzer.pub
grep " ${ARCHIVE}$" SHA256SUMS | sha256sum -c -SHA256SUMS covers every architecture, so checking it whole fails on the
archives you did not download — and --ignore-missing, the usual way
around that, reports OK when no archive is present at all. Pulling out
the line for the file you have fails when there is nothing to check.
The signing key is the same for every release. If you have verified a
morzer release before, use the copy you already have rather than fetching
it again — a key that changes between releases is the thing worth noticing.
Release bundles are a separate artifact with their own signature and their
own key; see
https://morzecrew.github.io/morzer/reference/release-commands/#verification