Skip to content

Sinter v1.3.0

Latest

Choose a tag to compare

@hagix9 hagix9 released this 11 Oct 03:36
· 1 commit to main since this release

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, TERM is not dumb and the output format is text, plan, apply and audit show one line that names the stage, the count of finished items (17/42, never a percentage or an estimate), the resource type and id in 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. Writes progress: ... 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 value plain has an effect; any other value is ignored. There is no command-line flag or configuration setting.
  • No progress where it could interfere. --format json never produces progress, and neither does a recipe that references an encrypted secret, whatever SINTER_PROGRESS says. 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 id of 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

  • plan reports what apply would refuse. For a file, directory, link or template change, plan now runs the same read-only checks as apply: 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, for directory with state: 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) with apply's reason; previously plan showed a change and apply failed. When an earlier change can (a directory this run creates, a package, a command), plan shows the change with a note (--verbose, JSON notes) and apply decides. plan still changes nothing. Plans of a directory with state: absent run one more read-only command (find <dir> -mindepth 1 -maxdepth 1 -print -quit).
  • Backup store and command guards in plan. A recipe with backup: paths now counts the backup store directory that apply creates first as an earlier change, and plan reads that directory (one stat) to see whether it exists. A command that is skipped because its creates/removes guard 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 --sudo only root-owned parents are trusted, so connect as that user without --sudo or use a root-owned location such as /opt or /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, plan and apply now 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 as 1.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 json documents and the mapping from status to exit code are unchanged (the JSON output code is untouched; notes was 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 plan that used to show a change can now end with exit 4 when apply would have refused it, and a template that contains a {% ... %} or {# ... #} tag now fails validate, plan and apply with 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 mcp tool 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 sinter binary (the Cargo.toml and Cargo.lock differences 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.gz and SHA256SUMS
  • 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-SHA256SUMS

The 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, executable 3a67ddead75b6014707d68c20921ba46ce5e0ad26420791bb9e6736ccad24fc0) 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 json and with invalid SINTER_PROGRESS values; plain lines; a failing run; an interrupted run; the heartbeat); plan and apply agreeing on parent-directory, --sudo, ACL, extended-attribute and directory-removal refusals, with plan leaving 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 0700 and the directory resource with mode: "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.py from the tagged source (see release/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) or SIGTERM, the last transient progress line can remain on the terminal, and a plain progress stream ends without a closing run: ... completed or failed line. Sinter installs no signal handler for these.
  • README in the archive. The README.md and README.ja.md inside the tarball still name v1.2.0 as the current release. The binary is v1.3.0 (sinter --version prints sinter 1.3.0); the repository documentation is updated after publication.
  • Plain progress on hosted CI. SINTER_PROGRESS=plain was 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.
  • plan cannot always know what apply will 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 and apply decides.

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.
  • group and user manage local accounts only. user.password_hash requires --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 packages clears them.