Changelog

Changelog

Per-version release notes.

The canonical changelog lives in CHANGELOG.md in the source repository. This page mirrors the highlights for the last few releases.

v0.2.2 - 2026-06-02

Critical durability fix on top of v0.2.1 plus a large performance improvement on big-vault mounts. Closes a real-user data-loss class where v0.2.1's mirror protocol could commit "intended new state" pointers to disk before the chunk-list blocks those pointers referenced were durable, so a crash in a narrow post-mirror-fsync, pre-live-fsync window left a vault whose recovery target pointed at slots that still held pre-flush bytes and failed AEAD on reopen.

Existing v0.2.1 vaults open unchanged; the fix is entirely on the write path. No on-disk format change -- same LBM5 + LUKSBOX2 + sidecar mirror envelope. Vaults that already lost chunk-list blocks to this bug are NOT recovered by upgrading; this release only prevents future occurrences and ships a tolerant recovery mode so the healthy files in a damaged vault can still be copied out.

Performance: 3-minute rm is now milliseconds

A real-user report on a 10k-file vault: a single rm of one tiny file took ~3 minutes; nemo and bash were both unresponsive while the vault was mounted. Root cause: every FUSE metadata op (create, mkdir, unlink, rmdir, rename, setattr, symlink, link, FUSE flush / release) was driving a fully synchronous Vfs::flush that rewrote the entire 64 MiB metadata mirror and live region AND re-spilled every spilled inode's chunk-list chain. Two layers of fix:

Durability model matches ext4 / btrfs / xfs commit intervals: a crash without an explicit fsync can lose up to 30 seconds of metadata changes. The on-disk encrypted bytes that ARE flushed retain the v0.2.2 mirror-protocol durability fence (chunk-list blocks are durable before the mirror commits), so the post-flush state is always crash-consistent. Keyslot revocation is unaffected (revoke() calls persist_header independently of vfs.flush).

luksbox mount --sync (also accepted on deniable-mount) restores the pre-v0.2.2 eager-flush semantics for users whose threat model requires every operation to be durable on return. The wizard TUI surfaces this as an "Eager flush?" prompt before mount; the GUI Browser view exposes it as an "Eager flush (--sync)" checkbox next to the Mount as volume button. Default off in all three.

Platform coverage: the deferred-flush optimisation now runs on every mount backend in v0.2.2 -- Linux libfuse3, macOS macFUSE, macOS FUSE-T, and Windows WinFsp. Each backend hosts its own luksbox-flush-timer thread with identical 30 s cadence, identical --sync opt-out, and identical final-flush guarantee on unmount. The shared interval constant lives in luksbox_mount::LAZY_FLUSH_INTERVAL_SECS. Earlier v0.2.2 drafts shipped Layer 1 for libfuse3 / macFUSE only; the FUSE-T and WinFsp adapters now match.

Fixed: v0.2.1 durability hole (CVE-class data-loss)

Vfs::flush's spill stage writes external chunk-list blocks through the page cache, then called Container::write_metadata, which fsyncs .lbx.meta-bak BEFORE overwriting the live metadata region. A crash between those two fsyncs (power loss, OOM kill, kernel panic, host hypervisor death) could leave .lbx.meta-bak durably committed to the NEW state with fresh chunks_external head/generation tuples, while the .lbx chunk-list-block slots those tuples reference still held bytes from a previous flush. On reopen, mirror recovery picked the NEW pointers as authoritative; the reader's AEAD then verified every targeted chunk-list block under the NEW generation in AAD, but the bytes on disk were written under the OLD generation. Result: crypto: AEAD failure on every chunk-list block that hadn't been fsync'd before the crash, cascading to metadata blob deserialization failed from the VFS layer at open time.

Real-world incident: a v0.2.1 user lost 26 contiguous-in- generation chunk-list blocks in a single flush after a crash, while data chunks and the metadata blob itself were intact.

Fix: new Container::sync_data_area() method (one self.file.sync_all()), called from Vfs::flush between the spill stage and write_metadata. The mirror's "reader can trust these pointers" invariant is now backed by durable bytes. Deniable vaults are unaffected (they explicitly opt out of the sidecar-mirror protocol). v0.2.0 and earlier vaults are also unaffected (no mirror protocol there at all).

Regression-tested three ways:

Fixed: FIDO2-direct vaults rejected by --fido2

luksbox check --fido2 (and the GUI's "Open" path) refused fido2-direct vaults with no FIDO2 keyslots in this container. The flag-driven unlock path iterated only SlotKind::Fido2HmacSecret slots and skipped SlotKind::Fido2DerivedMvk, even though the format layer's UnlockMaterial::Fido2 arm has handled both kinds since v0.2.0. Fixed to accept both. Subcommands that don't get --fido2 / --tpm2 / --tpm2-fido2 / --pq-hybrid on a vault with zero passphrase keyslots now also error with a clear vault has no passphrase keyslot; rerun with --fido2 instead of silently prompting for a passphrase that no slot will ever accept.

Fixed: GUI YubiKey "Touch your YubiKey" overlay was un-cancellable

Real-user report: during a YubiKey unlock the overlay caught the button press intermittently, sometimes after 5-6 attempts, with no way to abort and retry. v0.2.2 adds a Cancel button to the overlay and binds Esc to the same handler. The overlay redraws every frame; either input cleanly aborts the pending FIDO2 op so the user can retry without restarting the GUI.

Added: tolerant recovery for already-broken v0.2.1 vaults

For vaults that already lost chunk-list blocks to the v0.2.1 durability hole (or any other corruption class that makes chunk-list-block AEAD fail), two opt-in recovery primitives:

Used in production by the incident-affected user to recover 1885 healthy files from a 10k-file vault and identify all 26 broken paths as reproducible pentest scan output.

GUI improvements

Notes

v0.2.1 - 2026-05-22

Durability + correctness release on top of v0.2.0. Headline fix: crash-safe header and metadata writes via sidecar mirrors so a mid-write force-quit no longer destroys the vault (the user-reported v0.2.0 data-loss class). Also restores the executable bit when git clone-ing into a mounted vault, raises the metadata cap, surfaces capacity warnings in CLI and GUI, and fixes the macFUSE Apple build.

Existing v0.2.0 vaults auto-upgrade to the new envelope on the first flush. The upgrade is one-way; pre-v0.2.1 LUKSbox binaries can no longer open the vault after that point. Set LUKSBOX_FORMAT_V2=1 in the environment at create time if you need the old envelope for sharing with a pre-v0.2.1 install.

Headline: crash-safe header + metadata writes (LBM5 + LUKSBOX2)

Fixes the real-user data-loss case from v0.2.0 where a vault that hit ENOSPC mid-copy at ~13 GB with ~5000 small files became permanently unopenable after force-quit (blob deserialization failed on unlock; or, in a separate failure mode where the header was the corrupt region, no keyslot accepted the provided unlock material).

Two root causes:

The fix adds A/B durability for both critical regions plus a higher metadata cap, all cross-platform (Linux, macOS, Windows):

Security: intended-state mirror protocol (closes revoke-bypass)

An initial draft of the durability fix kept "previous-good" copies in the mirrors. An independent review and an end-to-end exploit script demonstrated that an attacker who corrupts the live header could force the recovery path, and the mirror's stale keyslot table would accept a previously-revoked credential. Confirmed auth-bypass.

Fixed by switching to intended-state mirror semantics: the mirror is committed to the NEW bytes BEFORE overwriting live, so the mirror always reflects what the user currently wants as the authoritative state, never a historical snapshot. After a revoke, the mirror reflects the post-revoke keyslot table. Even forcing the recovery path cannot resurrect the revoked credential. Regression-tested by v2_corrupted_live_after_revoke_does_not_unlock_via_mirror and by the new v2_mirror_recovery libFuzzer target.

Other security hardening in the durability path:

Fixed: git clone preserves the executable bit

Files materialised by git clone into a mounted LUKSbox vault no longer lose the executable bit on scripts and binaries. The FUSE create(O_CREAT, mode) callback now honors the requested mode all the way through to the persisted inode field, instead of defaulting every newly-created file to 0o644 regardless of what the caller passed.

Ground-truth verified end-to-end: git clone https://github.com/PentHertz/RF-Swift.git into a mounted v0.2.1 vault preserves -rwxrwxr-x on the shell scripts; unmount + remount confirms the bits round-trip through LBM5 disk persistence.

Added: metadata-budget capacity notification

Vaults holding very large inode counts (hundreds of thousands of files) can approach the 64 MiB metadata region cap before they exhaust host disk space. v0.2.1 emits a soft notification BEFORE the hard ENOSPC:

Notifications fire at most once per level per session. Vfs::metadata_budget_status() is exposed as a public API for external tooling that wants to build dashboards or pre-flight checks.

Added: tested-boundary advisory (~30 GiB)

v0.2.1 has been ground-truth validated end-to-end (real FUSE mount, write, force-quit, reopen, verify) up to roughly 30 GiB of stored content with several thousand files. The format is engineered for larger vaults but usage beyond that boundary has not yet been explicitly tested.

LUKSbox surfaces a one-shot heads-up at two moments:

The advisory is informational, not a hard limit. The boundary will be raised in subsequent releases as validated usage data accumulates.

Fixed: macFUSE Apple build (cfg-gated renameat2 flags)

Linux-only renameat2(2) flag constants (RENAME_EXCHANGE, RENAME_WHITEOUT, RENAME_NOREPLACE) are exposed by the fuser crate's RenameFlags only under #[cfg(target_os = "linux")], so the macFUSE build broke when the shared fuse.rs file referenced them unconditionally. The rename handler now cfg-gates the entire flag check; on macOS / Windows the renameat2 flags don't exist in the FUSE protocol and the check is moot. Linux behavior unchanged.

Install note: Trixie deb + plain su root

dpkg -i luksbox_*.deb aborts on Debian 13 with dpkg: error: 2 expected programs not found in PATH or not executable (ldconfig, start-stop-daemon) when run from a shell where root's PATH lacks /sbin. This happens when you switch to root via su root rather than su -. Dpkg's pre-flight check fails BEFORE any maintainer script runs, so we cannot fix it inside the package. Workarounds:

The dist/install.sh driver uses sudo and is not affected.

UI: "v3 metadata format" label clarified

The GUI checkbox and TUI wizard prompt now refer to the v0.2.1 envelope (LBM5 + LUKSBOX2 + sidecar mirrors) explicitly, instead of the misleading "v3" label inherited from when v3 just meant LBM3 / external chunk lists. The boolean field name (use_v3_format) is kept for API stability across the GUI -> ops boundary.

v0.2.0 - 2026-05-20

Bigger feature drop on top of v0.1.1: FUSE-T on macOS, deniable- header v2 mode, private mountpoints, on-disk metadata format v3 (new default), deniable MVK rotation that actually re-encrypts chunks, plus the usual hardening pass. The metadata-format change is forward-incompatible -- new vaults default to v3 (LBM\x03 magic) and cannot be opened by pre-v0.2.0 LUKSbox installs. Pass --format v2 (or untick the box in the GUI) when creating a vault you need to share with an older install. v0.1.x vaults open unchanged. Deniable mode is a format that did not exist before v0.2.0 and gained v3 support in this release as well.

Added: on-disk metadata format v3

Fixed: filesystem-boundary hardening (security audit follow-up)

A round of fixes for an external audit report covering TOCTOU / symlink / path-substitution surfaces. All fixes preserve existing vault contents; the changes harden write paths and the deniable open path. Disclose new findings to [email protected].

Other

Bigger feature drop on top of v0.1.1: FUSE-T on macOS, deniable- header v2 mode, private mountpoints, plus the usual hardening pass. No breaking format changes for standard vaults, every v0.1.x vault opens unchanged. Deniable mode is a NEW format (v2) that did not exist before v0.2.0.

Added, macOS FUSE-T backend

Added, deniable-header mode (v2)

Added, mount UX

Added, packaging

Added, fuzz coverage

Improved, deniable open UX

Changed, security hardening

Security review

Security audit, Round 13 - findings shipped fixes same revision

Internal Round-13 sweep across local filesystem boundary races, header durability, sidecar DoS surfaces, and remaining secret-copy hygiene. 2 HIGH, 5 MEDIUM, 2 LOW, 1 INFO. No CRITICAL. ALL 9 findings fixed in the same revision. Full per-finding report + Fix-status table at docs/SECURITY_AUDIT_ROUND_13.md.

HIGH fixes

MEDIUM fixes

LOW fixes

INFO R13-INFO-1: cargo audit advisory RUSTSEC-2025-0026 (registry 1.3.0 via winfsp_wrs_sys) remains accepted as documented in audit.toml.

New regression coverage: 4 test files, 14 deterministic tests (0 ignored). Run them with cargo test --test round13_findings -p luksbox-format (and similarly per-crate), or simply cargo test --workspace --exclude luksbox-gui.

Security audit, Round 12 - findings shipped fixes same revision

Four-axis adversarial sweep (FUSE-T subprocess + deniable v2 + filesystem TOCTOU + memory safety). All CRITICAL + HIGH + 5 of 7 MEDIUM fixed in the same revision. Full per-finding report

Headline fix - R12-01 (CRITICAL): deniable envelope discovery loop is now constant-time. try_open_envelope_v2 runs identical work per slot (always-allocate fixed scratch, always-memcpy via subtle::Choice-driven byte selection), and SlotPayload::decode runs ONCE on the constant-time-chosen slot's plaintext - so variable-length heap allocations are slot-position-independent. Pinned by the new dudect bench crates/luksbox-format/benches/dudect_deniable_envelope.rs.

HIGH fixes

MEDIUM fixes (R12-07 sandbox-subpath canonicalize; R12-09 extract-parent deny-list; R12-10 .rotating tmp O_EXCL|O_NOFOLLOW; R12-12 helper MVK stdin Zeroizing<[u8;32]>; R12-13 deniable cand_bytes Zeroizing).

LOW fixes (R12-16 vault-name : rejection + byte-cap; R12-18 TPM SensitiveData heap Zeroizing).

Empty-passphrase warnings (Round 12 follow-up) CLI's read_passphrase_confirmed now mirrors the wizard's and the GUI's empty-pass confirm modal (default no, with LUKSBOX_ACCEPT_EMPTY=1 escape hatch).

All other fixes

Round 12 closed cleanly. All 19 findings shipped fixes.

New test infrastructure shipped this round

v0.1.1 - 2026-05-08

First post-release iteration on top of v0.1.0. No breaking format changes - every v0.1.0 vault opens unchanged under v0.1.1.

Fixed

Added - forensic / partial-recovery CLI toolkit

Walkthrough on the Forensics page.

Added - release pipeline

Changed - security hardening

Non-breaking tightenings of the safe envelope. No vault or workflow that worked under v0.1.0 is affected.

Documentation

Known limitations

v0.1.0 - 2026-05-06

Initial public release. The core feature set was audit-tracked through 9 internal review rounds before the cut.

Pre-1.0 status

LUKSbox is currently pre-1.0 and the on-disk format may evolve under audit guidance. Migration tools are provided for any breaking format change, but the conservative move is to let the v1.0 cut settle before relying on it for archival storage.

When the v1.0 line is cut, format compatibility becomes a hard guarantee: any v1.x release will read any vault produced by any other v1.x release.

Release verification

Every published release ships a SHA-256 manifest and a Sigstore attestation. See Download / Verify your download for the verification commands.