WolfStack v25.6.2
v25.6.2: deleting a cluster share now deletes it everywhere, and SMB mount failures say what actually went wrong
Two reports from klasSponsor.
── Removing a share didn't remove it on all nodes ──
It could not have. gateways_delete did push the surviving set to every
peer, but the receiving end — merge_from_peer — only ever inserted and
updated. A share missing from an incoming snapshot was read as "no
information", never as "deleted", so the removal landed on the node the
operator used and nowhere else, permanently.
The obvious fix is wrong and worth saying why: a snapshot carries every
gateway a node knows, including ones it merely learned from other peers,
so having the receiver replace its whole set would let one node silently
delete another node's shares.
Instead the sender now identifies itself (X-WolfStack-Node-Id) and is
treated as authoritative only for shares it OWNS: any gateway with
origin_node_id equal to the sender that is absent from the sender's
snapshot is removed. Safe because deletion is already gated by
require_owner, so only the owner can have removed it. A peer that
sends no node id — anything pre-25.6.2 — falls back to merge-only, i.e.
exactly today's behaviour: a stale share beats one deleted on a guess.
Second cause of the same symptom, independent of the first:
push_to_peers skips peers that are offline at that instant and ignores
transport failures, and nothing reconciled afterwards. So even correct
delete-propagation would miss any node that happened to be down. Every
node now re-broadcasts its share set every 5 minutes, which makes the
sync eventually consistent instead of dependent on one fire-and-forget
delivery landing. The receiver only writes to disk when something
actually changed, so the steady state costs a JSON POST per peer and
nothing else — there's a test pinning that "no changes" reports
unchanged, because a false positive there would mean needless writes
fleet-wide every 5 minutes forever.
merge_from_peer is gone rather than kept as a wrapper — one entry
point, so the sender-identity argument can't be forgotten at a call site.
── SMB mount errors were unactionable ──
"SMB mount failed: mount error(2): No such file or directory" names
neither which thing wasn't found — the share? the mount point? a
subdirectory? — nor the fact that WolfStack may have connected
anonymously, so it reads as a bad path when the server is actually
saying "I have no share by that name, at least not for you".
The raw mount.cifs text is still passed through verbatim; an
interpretation is now appended for the errnos we recognise (2, 13, 112,
115, 95), matched on the errno rather than message wording, which varies
between cifs-utils versions. The ENOENT case states that the mount point
exists (WolfStack creates it, so that's ruled out), that a NAS app
folder is usually a folder inside a share rather than a share, that
names are case-sensitive, and suggests smbclient -L. Where no username
was configured it also says the attempt was made as guest — which is
what a share requiring credentials looks like from the client side.
Not a mount-point bug and not a malformed UNC: storage/mod.rs:332
creates the directory, and klas's kernel log shows CIFS reaching the
tree connect with the correct UNC before returning -2.
Untested at runtime: neither path exercised against a live cluster or a
real SMB server. The prune logic, the peer-cannot-delete-another-node's
-shares property, and the unchanged-detection are unit-tested.
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.