Skip to content

Yuzu v0.14.1

Latest

Choose a tag to compare

@github-actions github-actions released this 07 Oct 09:09
· 4301 commits to main since this release
17ddbd1

Added

  • The agent now says whether it is verifying OTA update signatures. At startup it logs its update-signature mode and the trust bundle path — OTA update signature mode: off (no bundle, so update binaries are not signature-checked), bundle, or bundle+require. When auto-update is enabled, the OTA update checker also warns, once, when it first starts (after the agent first connects) if the bundle is set but cannot be loaded. Every heartbeat carries the same mode as the status tag yuzu.ota_signature_mode; the tag is stored by the server, not yet surfaced in any view — the startup log line is the per-endpoint source of truth. The agent also warns at startup about any environment variable starting with YUZU_UPDATE_ that it does not read, so a misspelling AFTER the YUZU_UPDATE_ prefix (for example YUZU_UPDATE_TRUST_BUNDEL) no longer leaves signing silently off. The Windows Environment-value check script in the server administration guide now also rejects non-ASCII characters in the signing entries and the binary path, and requires $stage to be 1 or 2. (#5249)

  • --postgres-dsn-file and --oidc-client-secret-file read the server's database connection string and OIDC client secret from a file, so they need not appear on the command line, where other local users can read them. On Windows, the service's environment is readable by local users too. The whole file is the value; trailing whitespace and a UTF-8 byte-order mark are ignored. The server refuses to start if the file is missing, empty or larger than 64 KiB, or if the matching direct option (--postgres-dsn/YUZU_POSTGRES_DSN, --oidc-client-secret/YUZU_OIDC_CLIENT_SECRET) is also set. This works with the usual Docker and Kubernetes secret files. Env: YUZU_POSTGRES_DSN_FILE, YUZU_OIDC_CLIENT_SECRET_FILE. (#5272)

Changed

  • Breaking for alert rules — an OTA update refused because the agent's own trust bundle cannot be loaded is now counted as yuzu_agent_ota_signature_refused_total{reason="bundle_unreadable"} and logged as OTA update <version> REFUSED: the update trust bundle could not be loaded, no longer reason="untrusted" / signature verification FAILED (untrusted chain) (the grep string the 0.14.0 notes gave for #5196). Rules, dashboards or saved searches keyed on the old reason or text for this fault need updating; for those faults (missing, permission-denied, not PEM) the refusal itself is unchanged, and the heartbeat total yuzu.ota_signature_refused (so yuzu_fleet_ota_signature_refusing_agents) still includes it. New refusal: both --update-trust-bundle and --plugin-trust-bundle are now read bounded — a bundle over 1 MiB, one that is not a regular file, or (Windows) one held open by another process that does not allow shared reading is refused, where 0.14.0 loaded it; with --update-require-signature or --plugin-require-signature that blocks every signed update or plugin, so check the bundle size before upgrading (see the upgrade notes). Agent-only; no server change. (#5249)

  • The release workflow now refuses to publish over a tag that already has a published release (#5282). A new first job in release.yml, release-guard, refuses any run that is not on a vX.Y.Z[-{alpha,beta,rc}N] tag, and fails the run before any build or push when GET /releases/tags/<tag> answers 200; it proceeds only on HTTP 404 and fails closed on any other answer. Every image push and the release creation additionally re-check that the tag still points at the run's commit (scripts/ci/check-release-tag-sha.sh). Every image-publishing job, the release job and the build jobs wait for it. Before this, a run started against an already-released tag (by mistake, or a superseded run) pushed new image digests over :X.Y.Z (and :X.Y/:latest) that no longer matched the release's signed SHA256SUMS and SBOMs, and Create GitHub Release then skipped with a notice and went green; that step now fails instead. Recovery for a failed job is "Re-run failed jobs" on the newest run for the tag (digests kept); there is no override, and redoing a published release means cutting a new version. Residual paths are listed in docs/ci-architecture.md, Release re-publish guard.

Fixed

  • The Windows agent installer no longer leaves a running agent stopped when an upgrade fails. If an upgrade fails after the installer has stopped the YuzuAgent service (for example, a file in the program directory cannot be replaced, which an unattended /SUPPRESSMSGBOXES install answers with Abort, exit code 5), the installer now starts the service again before it exits if it was running or still starting, even when /NOSTART was given. It waits for a slow stop to finish and then records in the Setup log (/LOG=) the state it actually saw: RUNNING, or that the service could NOT be started, with sc.exe's exit code. Files the failed upgrade had already replaced are not put back, so run the installer again. Earlier installers left the agent offline until someone ran sc.exe start YuzuAgent or rebooted. A trust-anchor refusal (exit code 7) still happens before the service is stopped. Its message now also says what the attempt left at %ProgramData%\Yuzu\agent-certs: when the lock on a new directory is blocked, nothing is created there, and an existing directory's own permissions are never changed. The agent and server installers' messages now say sc.exe, never plain sc, which PowerShell reads as Set-Content. (#5250)

  • A docs- or changelog-only push to main/dev now gets the required CI checks (#5320). ci.yml's push trigger dropped its paths filter, so every push runs; preflight's code_changed gate skips the build matrix for a docs-only push, as it already did for a docs-only PR, and the required contexts appear on the commit as success or skipped instead of being absent. The v0.14.0 release commit was tagged without them because its only change was CHANGELOG.md. The build legs are skipped only when the previous commit's push run succeeded, the push is a fast-forward, and every changed path (and every rename's previous path) is in the docs set — so the skip never follows an unbuilt or red code commit; any API error, control character in a path, or other answer builds. A push touching docs/os-capability-matrix.md still builds and runs its drift gate. The release skill's tag gate now says that skipped required checks count as passing and absent, failed or cancelled ones do not.

  • Action required before upgrading from 0.14.0 on Docker Compose: copy /etc/yuzu/certs out of the running server container first (Upgrading → "Docker Compose: copy /etc/yuzu/certs out of the server container before you recreate it"). Affected: a 0.14.0 Docker Compose stack whose server has no named volume on /etc/yuzu/certs while Postgres has one — deploy/docker/docker-compose.yml (the 0.14.0 README quickstart), docker-compose.uat.yml, docker-compose.full-uat.yml, docker-compose.viz-uat.yml, docker-compose.demo.yml used with --keep, any compose copied from them, and Compose Wizard output from before 0.14.1 in Plaintext mode, with 'Persist generated certs' unticked, or with named volumes off and an external Postgres. Not affected: docker-compose.reference.yml (which the README quickstart saves as docker-compose.yml since #5419) and docker-compose.reference-gateway.yml (their certs volume). The server keeps its internal CA, default certificates and secrets key-encryption key (KEK) in /etc/yuzu/certs, and since 0.14.0 the KEK is also registered in Postgres. In those stacks an image upgrade, docker compose down then up, or --force-recreate deleted the key files, and the server then refused to start with kek_unresolvable. Those composes and the /test upgrade-test compose now mount a server-certs volume on /etc/yuzu/certs. The Compose Wizard's Default and Plaintext output always mount a volume there too (its "Persist generated certs" option is now always on): the named server-certs volume when named volumes are on or Postgres is external, otherwise an anonymous volume that is discarded together with the database's anonymous volume. The wizard's Operator-certs mode still mounts ./certs read-only, so the server cannot write its KEK and stops at first boot with provider_failure (#5420). The path does not move, so CA key paths recorded in Postgres stay valid. The chiselled server image now creates /etc/yuzu/certs owned by the server user, and the demo stack adds a one-shot server-certs-init service so it also works on older chiselled images. The upgrade guide gives a checked copy-out recipe, the diagnostics to run before anything destructive (a lost volume line, the wrong file owner, or keys left on a dangling anonymous volume give the same error), a rollback that keeps the keys, and what can still be saved if the files are already gone. The /test upgrade gate also recreates the upgraded server and checks that it comes back on the same keys. (#5370)

  • The README's Docker Compose quickstart now works with the released images. In 0.14.0 it downloaded deploy/docker/docker-compose.yml and ran YUZU_VERSION=0.14.0 docker compose up -d. That compose builds from a checkout of the repository (build: context: ../..), so a downloaded copy has nothing to build, YUZU_VERSION was ignored, and the stack never started. The quickstart now downloads deploy/docker/docker-compose.reference.yml from the release tag, which pulls ghcr.io/devnullltd/yuzu-server and yuzu-postgres at ${YUZU_VERSION}, serves HTTPS on 8443 and keeps /etc/yuzu/certs on a named volume. It is three short steps that paste safely into bash or zsh: create the directory and a .env with the version and two different Postgres passwords, write the first admin account to yuzu-server.cfg (the server's interactive first-run setup exits when there is no terminal), then start the stack and check /readyz against the server's own CA. Pasting a step twice cannot replace the database passwords or a live admin account: step 1 stops if the directory or any of the project's volumes already exists, and step 2 never overwrites either copy of yuzu-server.cfg. The README says to set YUZU_CERT_SAN before the first start, never to clear /etc/yuzu/certs (it holds the secrets key, #5370), and that on 0.14.x yuzu-server.cfg is the admin password's live source (the server reads it at every start and its entry wins over the database) until 0.15.0 (#5274); the upgrade guide and the AuthDB agent notes that called the file a one-time seed are corrected. The server's warning when --cert-san names are missing from the existing certificates, and the --cert-san and certificate-rotation advice in the server administration guide, the PKI architecture notes and the certificate-expiry alert, no longer say to clear the certificate directory: to add names, rename default-marker.json aside (moving it back undoes this) and restart, which re-issues the certificates under the same CA. The README describes deploy/docker/docker-compose.yml as a development stack for a clone. A docs-suite test, tests/test_readme_quickstart.py, checks the downloaded compose (no build:, ${YUZU_VERSION} image tags, 8443, a persisted /etc/yuzu/certs), that the compose URL's tag, the .env version and the compose default agree, that the steps are paste-safe and never overwrite, and that the admin seed matches the image's --config path and the server's PBKDF2 parameters. (#5419)

Security

  • Docs site and demo app dependencies patched (25 advisories; OpenSSF Scorecard Vulnerabilities check). Lockfile-only bumps, with no package.json change, of transitive npm dependencies that Dependabot does not propose. The server, agent, gateway and installers are not affected: neither project ships in a product artifact. In site/ (the docs site): undici 8.10.0 → 8.11.2 (GHSA-2gqq-gqf2-x968, GHSA-2jfj-6hjv-fm6j, GHSA-3wwx-pv8p-q78v, GHSA-3xpg-4rpp-hhhm, GHSA-8436-99hf-9mmv, GHSA-pmjh-fq2x-6v4x, GHSA-r53p-7pc4-xj5r, GHSA-rfgv-xxqx-mfg5, GHSA-rx4f-c7p8-82vq, GHSA-vp8m-p9jh-q5pm, GHSA-w293-vg96-wgc3), devalue 5.8.1 → 5.9.4 (GHSA-4q55-j62x-fr9h, GHSA-9rgm-9g3h-6x36, GHSA-hx4r-w6wj-j8fg, GHSA-j22f-vq7h-c4qm, GHSA-mcm9-63f2-9j32, GHSA-r9w8-h9r3-54w4, GHSA-wf3x-273g-mvxv, GHSA-x5rw-q4pp-hg5g), svgo 4.0.2 → 4.1.0 (GHSA-4vpr-x523-8j87, GHSA-w27v-7q3p-w38r), smol-toml 1.7.0 → 1.9.0 (GHSA-7w5x-hrqm-74c2) and http-cache-semantics 4.2.0 → 4.3.0 (GHSA-ch52-4w7c-c8xp). In the Cedar & Vale demo app: qs 6.15.3 → 6.16.0 (GHSA-4mjr-xmp4-gh2g, GHSA-x5fp-wj9c-mxmx). An OSV scan of every npm lockfile on the branch reports 0 advisories, down from 25.

  • The Windows server installer now keeps the secrets it handles in a directory only Administrators and SYSTEM can open, and off the service's command line. In every earlier version, %ProgramData%\Yuzu Server kept ProgramData's inherited permissions. Local users could read yuzu-server.cfg (the dashboard password hashes) and the TLS private keys in certs\, and could create files in its subfolders. The OIDC client secret was meant to be passed on the service's command line, which every local user can read. In practice an sc.exe quoting fault dropped every argument, so the service ran with none. The installer now:

    • locks that directory to exactly Administrators and SYSTEM, by SID so non-English Windows works;
    • verifies every entry it writes or resets before the install proceeds (the server's own subfolders in data\ are not walked);
    • stores the PostgreSQL connection string (postgres.dsn) and the OIDC client secret (oidc-client-secret) there, passing only their paths to the server (--postgres-dsn-file, --oidc-client-secret-file);
    • writes the service's command line with its arguments and a quoted executable path, confirms it, and disables the service (exit code 10) if it cannot.

    An upgrade over an earlier, unsecured install builds a new locked directory. It copies across only plain files that Administrators or SYSTEM own and no other account can change, from folders no other account can delete or rename in. It renames the old directory to Yuzu Server.insecure-<date>-<time> rather than changing or deleting it. After upgrading, rotate the passwords, TLS keys and OIDC secret it held. Move data\agent-updates\ and data\upload-blobs\ across if you ran the server, then delete the renamed directory. (#5196, #5210, #5272)

    Behaviour changes for unattended installs:

    • A connection string is now required on a fresh install and on an upgrade from any earlier version: /POSTGRES_DSN_FILE=<file>, which keeps the password out of logs, or /POSTGRES_DSN=. A YUZU_POSTGRES_DSN environment variable is honoured instead, with a warning. Before, the registered service could never start, because no connection string was ever passed.
    • An OIDC client secret is not carried from an earlier version; give it again. If one had been added to the service's command line by hand and OIDC is enabled on the upgrade, the upgrade refuses until it is.
    • An upgrade without /ADMIN_PASS keeps the existing accounts.
    • Upgrade in place rather than uninstalling first.
    • Any input, directory, file or certificate that cannot be secured stops the install before any file is installed, with exit code 7 and the reason in the setup log on a PrepareToInstall: line. A service already stopped by then stays stopped unless the abort came before the old directory was moved.
    • An unsecured data directory that no Yuzu Server installation is registered for is refused.

    The setup log (/LOG=) and deployment tools record the full command line, including any password given on it. Prefer the _FILE parameters. Windows server remains unsupported (ADR-0035). Separately, it cannot yet run under the Service Control Manager at all (#5325). The server's own CA and key-encryption keys stay in C:\ProgramData\Yuzu\certs for now (#5273).

Enterprise installers

Component Windows macOS Debian/Ubuntu RHEL/Fedora
Agent .exe (InnoSetup) .pkg (launchd) .deb (systemd) .rpm (systemd)
Server .exe (InnoSetup) — .deb (systemd) .rpm (systemd)
Gateway — — .deb (systemd) .rpm (systemd)

Windows silent install (GPO / SCCM / Intune)

# Agent
YuzuAgentSetup-*.exe /VERYSILENT /SUPPRESSMSGBOXES /SERVER=yuzu-server:50051 /TOKEN=<enrollment-token>

# Server
YuzuServerSetup-*.exe /VERYSILENT /SUPPRESSMSGBOXES /ADMIN_USER=admin /ADMIN_PASS=<password> `
  /HTTPS_CERT=cert.pem /HTTPS_KEY=key.pem

Silent uninstall — agent: "C:\Program Files\Yuzu\unins000.exe" /VERYSILENT /SUPPRESSMSGBOXES; server: "C:\Program Files\Yuzu Server\unins000.exe" /VERYSILENT /SUPPRESSMSGBOXES (or the path in the product's UninstallString registry value, if it was installed to another directory). Each removes the program but keeps its data directory: %ProgramData%\Yuzu (the agent's identity and mTLS private key) or %ProgramData%\Yuzu Server (databases, configuration, TLS keys). To decommission a device, revoke its certificate on the server and delete that directory (see docs/agent-bundle.md). Uninstallers from earlier releases (up to 0.14.0-rc2) hang on a silent uninstall; upgrade first.

macOS silent install (Jamf / MDM)

sudo installer -pkg YuzuAgent-*.pkg -target /

Linux install

# Debian/Ubuntu
sudo dpkg -i yuzu-agent_*.deb
sudo systemctl edit yuzu-agent  # set --server address

# RHEL/Fedora
sudo rpm -i yuzu-agent-*.rpm

Platform archives

Archive Platform Arch
yuzu-linux-x64.tar.gz Linux (server + agent) x86_64
yuzu-linux-arm64.tar.gz Linux (server + agent) ARM64 (coming soon)
yuzu-gateway-linux-x64.tar.gz Linux (gateway) x86_64
yuzu-windows-x64.zip Windows (server + agent) x86_64
yuzu-macos-arm64.tar.gz macOS (server + agent) ARM64 (Apple Silicon)

Compose Wizard

yuzu-compose-wizard-*.zip — browser-based step-by-step
generator for a customised docker-compose.yml + .env.
Zero dependencies — extract the zip and open index.html in
a browser. Contributed by
@fjarvis in
#405.

unzip yuzu-compose-wizard-*.zip
cd yuzu-compose-wizard-*
# Open index.html in your browser, or:
python3 -m http.server 8000

Docker images

docker pull ghcr.io/DevNullLtd/yuzu-server:v0.14.1
docker pull ghcr.io/DevNullLtd/yuzu-gateway:v0.14.1

Architecture: linux/amd64. Images are published to GHCR by this workflow as part of the release.

Verify checksums

sha256sum -c SHA256SUMS

Verify signature (cosign)

cosign verify-blob --bundle SHA256SUMS.sigstore \
  --certificate-identity-regexp '.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  SHA256SUMS

Supply chain: SBOM, SLSA provenance, signed images

Every release ships CycloneDX + SPDX SBOMs per platform
(yuzu-*-{linux-x64,windows-x64,macos-arm64}.{cdx,spdx}.json,
yuzu-{server,gateway,postgres}-image.{cdx,spdx}.json and
yuzu-{server,gateway,agent}-chisel-image.{cdx,spdx}.json), GitHub-native
SLSA build provenance attestations for every binary archive
and Docker image, and cosign keyless signatures on both the
Docker images and the SHA256SUMS manifest. See
docs/user-manual/release-verification.md
for the full verification workflow (gh attestation verify,
cosign verify, CycloneDX / SPDX validation).

See the user manual for full setup instructions.

Known issues

  • The second first-run account cannot log in on Postgres (#5343). First-run setup, and the Windows server installer's optional /OPERATOR_USER + /OPERATOR_PASS pair, write a second, non-admin account to yuzu-server.cfg, but first boot copies only the admin account into the Postgres auth store, so every login with the second account fails. Workaround: sign in as the admin and create further accounts in Settings → User Management.
  • Linux native packages need a recent distribution (#5143). The .deb, .rpm and tarball binaries are built with GCC 15 and need glibc 2.38; the server also needs GCC 15's libstdc++ (GLIBCXX_3.4.34). The server runs on Ubuntu 26.04 and Fedora 42, the agent also on Ubuntu 24.04; neither runs on Ubuntu 22.04, Debian 12 or RHEL/Rocky 9. 0.14.1 does not change this. Workaround: use the container images, which carry their own runtime libraries. The full matrix is under Supported Platforms in docs/user-manual/README.md.
  • The gateway RPM does not start on RHEL, Rocky or Fedora (#5171). The bundled Erlang/OTP crypto library needs SM4 symbols that Red Hat–family OpenSSL is built without, so the node fails with undefined symbol: EVP_sm4_cbc during boot. The gateway .deb does not declare its OpenSSL 3 dependency either. Workaround: run the gateway container image on those distributions.
  • Compose Wizard composes can lose, or cannot write, the secrets key in /etc/yuzu/certs (#5420). This is the same class of problem as #5370, which 0.14.1 fixes in the tracked composes and the README quickstart, but not in the wizard. In Plaintext mode, or with "Persist generated certs" unticked, the directory is in the container layer and the key is lost when the container is recreated; the server then refuses to boot (kek_unresolvable). In Operator mode the directory is mounted read-only (./certs:/etc/yuzu/certs:ro), so the server cannot write the key at all; this case has not been tested in Docker. Workaround: before the first start, change the generated compose so /etc/yuzu/certs is a named, writable volume, and mount any operator-supplied TLS files separately.

Release gate waiver

The nightly ASan+UBSan and TSan legs were red at tag time on timing flakes seen only under the sanitizers: #4270 (the subprocess-runner K1 natural-exit check loses its race when the runner is starved), #5538 (MFA enroll-race test gets 401 instead of 409) and #5539 (Guardian journal_maintenance_tick gate test sees policy_generation() == 1). #5538 and #5539 each failed on only one of the nightly's two attempts. The maintainer waived these legs for 0.14.1.