Repository navigation
Sinter v1.3.0
Sinter v1.3.0 adds progress output for plan, apply and audit, and makes plan report more of what apply would refuse. It also accepts rpm package versions that contain ~ or ^ and refuses Jinja statement and comment tags in templates, which Sinter never rendered.
Progress is written to standard error only. It changes no recipe, command line, --format json document, standard output or exit code, and it changes the output of a non-interactive run only if you set SINTER_PROGRESS.
Highlights
Progress output
- A transient progress line on interactive terminals. When standard error is a terminal,
TERMis notdumband the output format is text,plan,applyandauditshow one line that names the stage, the count of finished items (17/42, never a percentage or an estimate), the resource type andidin flight, and, after a quiet spell, the time since Sinter last moved on to a new item. The line is erased when the run ends, so what remains on the screen is the output you had before. - Opt-in persistent lines:
SINTER_PROGRESS=plain. Writesprogress: ...lines to standard error for logs and CI: run, stage and count milestones, and a heartbeat line (30s since last progress) after 30 seconds of silence, backing off to one line per 300 seconds. The number of lines depends on the stages and on waiting time, not on the number of resources. Only the exact valueplainhas an effect; any other value is ignored. There is no command-line flag or configuration setting. - No progress where it could interfere.
--format jsonnever produces progress, and neither does a recipe that references an encrypted secret, whateverSINTER_PROGRESSsays. By default a standard error that is not a terminal gets no progress, so scripted and CI output is unchanged byte for byte. - Progress contains only the stage, counts, the resource type and
idof the item, and fixed outcome words: no host, address, user, command, file content, diff, secret reference or error text. Progress text is informational and, like other standard-error text, not part of the stable 1.x interface.
plan and apply agree more often
planreports whatapplywould refuse. For afile,directory,linkortemplatechange,plannow runs the same read-only checks asapply: the parent path check (an ancestor owned by another user, writable by group or other, a symlink, an ACL, or missing), the security-metadata check of a file whose content is replaced, and, fordirectorywithstate: absent, whether the directory is empty. When no earlier change in the run can affect the path, a refusal is a plan error (exit 4) withapply's reason; previouslyplanshowed a change andapplyfailed. When an earlier change can (a directory this run creates, a package, a command),planshows the change with a note (--verbose, JSONnotes) andapplydecides.planstill changes nothing. Plans of adirectorywithstate: absentrun one more read-only command (find <dir> -mindepth 1 -maxdepth 1 -print -quit).- Backup store and command guards in
plan. A recipe withbackup:paths now counts the backup store directory thatapplycreates first as an earlier change, andplanreads that directory (onestat) to see whether it exists. Acommandthat is skipped because itscreates/removesguard holds is treated the same way when an earlier change can flip the guard, including a guard whose path has a symlinked ancestor. - Parent path refusals say what to do, for example: with
--sudoonly root-owned parents are trusted, so connect as that user without--sudoor use a root-owned location such as/optor/srv. - Jinja tags in a template are an error. Templates render only
{{ expression }}. A{% ... %}statement or{# ... #}comment tag used to be written to the target unchanged;validate,planandapplynow refuse it (exit 2) and name the resource, the source file, and the line and column. Write{{ "{%" }}for a literal{%. Only an opener followed by its own closer is a tag; an opener with no closer (such as an Elixir{%{...}), a closer written inside{{ ... }}, and a{#right after$(shell${#var}) are plain text and are published as written. - dnf packages whose version or release contains
~or^(rpm pre-release and snapshot versions, such as1.0~rc1) can be installed. The transaction used to be refused as uninterpretable. Other characters outside rpm version text are still refused.
Documentation site
The documentation site's dependencies were updated for published security advisories, including the optional image library sharp (now 0.35.5, with libvips 1.3.4). This affects how the documentation site is built; it is not part of the sinter binary in the release archive, which has no new dependency.
See CHANGELOG.md for the complete entry.
Compatibility
- 1.x policy. This is a minor release. Existing recipes, command lines,
--format jsondocuments and the mapping from status to exit code are unchanged (the JSON output code is untouched;noteswas already part of the document). - Behaviour that can change a script's result. Two changes make an existing run end with an existing exit code earlier than before: a
planthat used to show a change can now end with exit 4 whenapplywould have refused it, and a template that contains a{% ... %}or{# ... #}tag now failsvalidate,planandapplywith exit 2 instead of being written unchanged. Both are the point of the change, and neither introduces a new exit code. - Progress appears by itself only on a terminal. Nothing is added to standard output, to a JSON document or to a non-terminal standard error unless you set
SINTER_PROGRESS=plain. On an interactive terminal the transient line appears without any setting. - MCP. The
sinter mcptool set is unchanged: the same 8 read-only tools. On every acceptance target the tool count, the read-only hints and the reported version were checked. - No new dependencies in the
sinterbinary (theCargo.tomlandCargo.lockdifferences from v1.2.0 are the version line only).
Supported platforms
Linux x86_64: Ubuntu 24.04 / 26.04 LTS, Rocky Linux 9 / 10, RHEL 9 / 10, AlmaLinux 9 / 10 — one artifact for all of them, built on Rocky Linux 9.8 (glibc 2.34; the binary requires no glibc symbol newer than GLIBC_2.34).
Acceptance-tested point releases for this artifact: Ubuntu 24.04.5 LTS, Ubuntu 26.04.1 LTS, Rocky Linux 9.8, Rocky Linux 10.2, RHEL 9.8, RHEL 10.2, AlmaLinux 9.8, AlmaLinux 10.2, all x86_64. Other point releases have not each been independently accepted.
Downloads
sinter-v1.3.0-linux-x86_64.tar.gzandSHA256SUMS- Acceptance evidence:
sinter-v1.3.0-acceptance-manifest.json,sinter-v1.3.0-acceptance-evidence.tar.gz,sinter-v1.3.0-acceptance-SHA256SUMS
sha256sum -c SHA256SUMS
sha256sum -c sinter-v1.3.0-acceptance-SHA256SUMSThe archive holds a single directory sinter-v1.3.0-linux-x86_64/ with sinter, README.md, README.ja.md, LICENSE-MIT and LICENSE-APACHE.
Validation
- Linux release gate. Formatting, lints and all tests passed on Ubuntu 24.04.5 LTS (x86_64) in strict mode, where a missing test prerequisite fails instead of skipping: 1552 root tests (including the Linux-only suites and the real-sshd suites), 18 installer tests, 82 checker tests and 198 gateway tests; 0 failed, 0 skipped.
- Acceptance on eight real hosts. The same artifact (SHA-256
1bfd21cc22df846c6a0559819e7d34818047b5b0dd523837b2cdca3b26db13f0, executable3a67ddead75b6014707d68c20921ba46ce5e0ad26420791bb9e6736ccad24fc0) passed on all eight targets: 3,980 of 3,980 checks passed, 0 failed (444 per Ubuntu target, 516 on Rocky Linux and 515 on RHEL and AlmaLinux). The tarball and executable hashes were compared on each target before anything was executed. Each target ran the established single-host acceptance suite, the real-host account lifecycle (103 checks), and a new real-host suite for the features in this release. - What the new suite covers on every target. Terminal behaviour on a real pseudo-terminal (the transient line, no progress off a terminal, with
TERM=dumb, with--format jsonand with invalidSINTER_PROGRESSvalues; plain lines; a failing run; an interrupted run; the heartbeat);planandapplyagreeing on parent-directory,--sudo, ACL, extended-attribute and directory-removal refusals, withplanleaving the target byte-identical; backup-store and command-guard notes; the Jinja tag errors; and, on the six RHEL-family targets, installing, auditing and removing packages whose versions contain~and^from a local test repository. - Ubuntu 26.04.1. Accepted on a real Ubuntu 26.04.1 LTS host (uutils coreutils 0.8.0, GNU findutils 4.10.0, sudo-rs 0.2.13), including the directory-emptiness check through
find -mindepth 1 -maxdepth 1 -print -quit,mkdir -m 0700and thedirectoryresource withmode: "0700". - Progress over SSH and the long-run heartbeat were exercised in pre-release testing on Ubuntu 24.04 and Rocky Linux 9.8, not in the acceptance run of this artifact.
- Verify the evidence with
release/check_acceptance_manifest.pyfrom the tagged source (seerelease/ACCEPTANCE_EVIDENCE.md).
Known limitations
- Rare dnf refusals on the version-10 RHEL family. dnf 4.20.0 (Rocky Linux 10.2, RHEL 10.2 and AlmaLinux 10.2 ship the same version) sometimes prints its "Last metadata expiration check" line twice. Sinter accepts that line only once, so it refuses the dnf operation (exit 5, fail-closed). Nothing is changed, and a retry usually succeeds. This was seen once in this release's acceptance, on Rocky Linux 10.2; it is not new in this release.
- Interrupted runs. After an interruption by
SIGINT(Ctrl-C) orSIGTERM, the last transient progress line can remain on the terminal, and a plain progress stream ends without a closingrun: ... completedorfailedline. Sinter installs no signal handler for these. - README in the archive. The
README.mdandREADME.ja.mdinside the tarball still name v1.2.0 as the current release. The binary is v1.3.0 (sinter --versionprintssinter 1.3.0); the repository documentation is updated after publication. - Plain progress on hosted CI.
SINTER_PROGRESS=plainwas verified on real Linux hosts, but its output has not been verified on GitHub-hosted CI runners. - A stalled terminal cannot change a run's exit code; at the end of each progress scope Sinter waits at most half a second for the progress writer, per scope rather than per run.
plancannot always know whatapplywill do: where an earlier change in the same run can affect a path, or where it cannot list a directory to see whether it is empty, it shows the change with a note andapplydecides.
Carried over from earlier releases, not changed by this release (see the v1.2.0 notes):
- Linux x86_64 only; Oracle Linux is recognized but not part of the acceptance matrix.
groupandusermanage local accounts only.user.password_hashrequires--sudo, and$y$hashes are refused on RHEL-family 9.- The per-target acceptance scenarios are single-host.
- A server that offers only legacy SSH algorithms cannot be managed; there is no override.
- Package resources do not pin versions.
- On DNF targets, a package change is refused (fail-closed) while the dnf package cache still holds downloaded packages left behind by an interrupted transaction;
dnf clean packagesclears them.