One pinned step now installs pnpm, Node.js and the dependencies. `require-lockfile: true` keeps the `--frozen-lockfile` semantics, `cache: true` keeps the pnpm store cache.
`pnpm/setup` reads the runtime version from `package.json` and ignores `.node-version`. With `onFail: download`, pnpm records Node 24.15.0 in the lockfile and downloads it on every install unless `--no-runtime` is passed. The Dockerfile and the docs build pass it, since they already run a pinned Node. `.node-version` stays for the Cloudflare Pages build, which reads it to pick the Node that bootstraps corepack.
`comment_width = 80` counts the whole line, indentation included, which
wraps far more aggressively than intended; the latest nightly rustfmt
(rust-lang/rustfmt#6802) started enforcing it on comments it previously
left alone. Turn `wrap_comments` off rather than reformat everything.
`max_width = 100`, `comment_width = 80` and `use_small_heuristics =
"Default"` are rustfmt's defaults, so drop them too; only the two
nightly-only import options remain.
The oidc-client integration tests read the real system clock through a
`now()` helper. The ID token builder generates a fresh RSA key before
stamping `iat`, while each test captured its own `now` earlier for
verification. Under coverage instrumentation on a loaded CI runner, the
two key generations in `fail_verify_id_token_wrong_auth_time` took more
than the 5 minute `iat` leeway, so verification failed on `iat` before
ever reaching the `auth_time` check the test asserts on.
Make the helper return a frozen `MockClock` timestamp instead. Every
token and verification in the binary now shares one instant, so setup
duration can no longer affect the outcome, and the clippy opt-out for
`Utc::now()` goes away.
Matrix 1.17 requires servers that don't support the legacy
authentication API to reject `m.login.application_service` on
`/login` with a 400 and the `M_APPSERVICE_LOGIN_UNSUPPORTED` error
code. Previously this login type fell through the generic unsupported
credentials path and returned `M_UNKNOWN`, which appservices written
against Matrix 1.17 can't distinguish from an unrelated failure.
Matrix 1.17 requires servers that don't support the legacy
authentication API to reject `m.login.application_service` on
`/login` with a 400 and the `M_APPSERVICE_LOGIN_UNSUPPORTED` error
code. The test fails until the errcode is implemented.
The `rust-toolchain` ecosystem updates the `channel` in `rust-toolchain.toml`, which is now the only place the Rust version is written down. Weekly with the usual 14-day cooldown, so the `.1` point release has usually landed by the time the PR opens.
The builder stage starts from `buildpack-deps` (what the `rust:` image is built on anyway), installs a pinned `rustup-init` with `--default-toolchain none`, copies only `rust-toolchain.toml` and lets rustup install the toolchain it names. The Rust version is now written in exactly one place, shared with CI, so a toolchain bump needs no Dockerfile edit.
The toolchain layer is cached on the content of `rust-toolchain.toml` and the base image, and it is the same bytes we pulled as `rust:` image layers before, so build times are unchanged.
The registry cache mount also moves to `/usr/local/cargo/registry`: the `rust:` image already set `CARGO_HOME=/usr/local/cargo`, so the previous `/root/.cargo/registry` mount never held anything.
Until now only the clippy CI job and the Dockerfile named a Rust version (kept in sync by hand), while every other CI job, the release binaries and the docs build ran on whatever `stable` happened to be that day. Rust 1.98.0 landing on 2026-08-20 broke `build-binaries` overnight because of that (#5935).
`rust-toolchain.toml` pins 1.96.0 with the `minimal` profile plus clippy, and every `rustup toolchain install stable ...` in CI becomes a bare `rustup toolchain install`, which reads the file. Starting at 1.96.0 (the version clippy is already clean against) keeps this change free of lint churn; catching up to 1.98.0 is a follow-up.
rustfmt stays on nightly because `.rustfmt.toml` uses nightly-only options, so that job now invokes `cargo +nightly fmt` explicitly instead of setting a rustup directory override, which would silently take precedence over the toolchain file.
The file does not list the linux cross-compilation targets on purpose: that would make every developer and CI job download `rust-std` they never use. The two consumers that cross-compile add the targets themselves.
Replace the UTC-only SystemTime timer in EventFormatter with chrono's
ChronoLocal, which resolves the zone from TZ, then /etc/localtime, then
/usr/share/zoneinfo, and falls back to UTC when none are available. The
published image is distroless and ships none of them, so the default
output is unchanged and operators opt in by mounting a zone file; the
only difference for existing deployments is the offset suffix, Z ->
+00:00.