Skip to content

WolfStack v25.17.0

Choose a tag to compare

@github-actions github-actions released this 20 Aug 13:48
· 137 commits to master since this release

v25.17.0: a trailing slash on an S3 endpoint no longer breaks every S3 call, and a backup asks before it copies a 20 TB mount

Two reports from klas.

An S3 remote whose endpoint was entered as https://s3.example.com/ failed
every connection test and every health probe with 400 Bad request — Your browser sent an invalid request, an HTML page from the endpoint's own front-end
proxy rather than anything S3-shaped, while the same endpoint mounted perfectly
under s3fs. The trailing slash was the whole bug. Region::host() in
aws-region returns EVERYTHING after :// (aws-region-0.25.5
src/region.rs:300-308) and rust-s3 sends that verbatim as the Host header
(rust-s3-0.35.1 src/request/request_trait.rs:197, inserted at :536), so the
request went out with Host: s3.example.com/ — a malformed Host header that a
compliant proxy can only reject. s3fs was untouched because it derives its Host
header from the parsed URL, which is exactly why a working mount sat next to a
failing probe on the same credentials. Confirmed at the wire against the
reported endpoint: Host: s3.hexabyte.se answers 200, the same request with
the slash returns that error page byte for byte.

endpoint_url now normalises the endpoint to scheme plus host[:port], and
build_s3_region returns Result so the compiler proves every rust-s3 call
site reports an unusable endpoint instead of sending a doomed request. The
three hand-rolled Region::Custom sites behind the S3 backup destination —
upload, retrieve, delete — carried the same latent fault and now share one
normaliser with the mount, sync and bucket paths. An endpoint carrying a path
prefix can never work through rust-s3 for the same Host-header reason, so it is
refused by name up front rather than becoming another unexplained 400; s3fs and
rclone parse the URL themselves, so a prefixed endpoint that works for them is
left alone. An error body that turns out to be HTML now says so — something
other than the S3 API answered — on every S3 surface at once.

The health card's "error x 405" in that report was 405 consecutive probe
failures, not an HTTP status: one endpoint typo, five weeks of them.

Second: a container can be two gigabytes of application and twenty terabytes of
bind-mounted array, and nothing said so until the backup was already running.
Backup Now and saving a schedule now both check what the backup would actually
archive, and any included mount past 50 GB raises a dialog naming it — exclude
them, include them anyway, or cancel. Exclusions already set are never
re-asked. A "back up everything" schedule resolves its targets at each run and
so has nowhere to store a per-container exclusion; it says that plainly instead
of offering a button that could not work.

Measuring had to stop being unbounded to make this safe: du on a 20 TB array
would have held the mount inventory open for minutes. A source that is itself a
filesystem root is read from df instantly; anything else gets du with a
five-second deadline; past that the honest answer is "size unknown, up to
", which is itself worth knowing before it enters an
archive. Every figure now carries how it was arrived at, so nothing downstream
mistakes a bound for a measurement — and the staging-space guard keeps taking
the exact slow walk, because it refuses backups and must not work from a bound.
Two false positives found by running the check against live containers are gone
with it: a mount source that is not on the host, and a Proxmox storage-backed
mountpoint, no longer look like 20 TB arrays.

Co-Authored-By: CodeWolf paul@wolf.uk.com
Co-Authored-By: Wolf Software Systems Ltd paul@wolf.uk.com


Verifying this release

Each binary is signed via cosign keyless OIDC (no key distribution — signing identity is the GitHub Actions workflow itself, anchored to the Sigstore Fulcio CA and the Rekor transparency log) and ships with a SLSA build provenance attestation.

Verify the cosign signature:

cosign verify-blob \
  --bundle wolfstack-x86_64.cosign.bundle \
  --certificate-identity-regexp 'https://github.com/wolfsoftwaresystemsltd/WolfStack/\.github/workflows/release\.yml@.*' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  wolfstack-x86_64

Verify the build provenance:

gh attestation verify wolfstack-x86_64 --repo wolfsoftwaresystemsltd/WolfStack

Verify the SHA-256 checksum:

sha256sum -c SHA256SUMS

Artifacts

  • wolfstack-x86_64 / wolfstack-aarch64 / wolfstack-armv7 — static musl binaries (Linux x86_64, ARM64 / Raspberry Pi 4+, and 32-bit ARM).
  • wolfstack-<arch>.cosign.bundle — cosign signature bundle (cert + signature + Rekor entry).
  • SHA256SUMS — checksums for both binaries.

For per-version history see CHANGELOG.md.