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.4.0 - 2026-07-04
First stable release of the v0.4.0 line. It brings macOS Secure Enclave keyslots up to parity with the TPM and FIDO2 backends, extends the mount-time symlink and inode guards to every frontend, and closes out the late-June audit passes. On-disk formats are unchanged from the v0.3 line: existing vaults, sidecars, and headers open without migration.
macOS Secure Enclave (SEP) keyslots
Bind a keyslot to a Mac's Secure Enclave on Apple Silicon and T2 machines, via CryptoKit ECDH in derive mode. 13 keyslot kinds cover plain, Touch ID, fusions with FIDO2 and a passphrase, and ML-KEM-768 and ML-KEM-1024 hybrids. The per-slot SEP material lives in an HMAC-covered in-header region, so SEP vaults need no external sidecar. As with TPM slots, keep a backup passphrase slot: a wiped or replaced enclave makes a SEP slot unopenable.
Mountpoint hardening across every frontend
The symlink probe (O_DIRECTORY | O_NOFOLLOW) and the inode re-probe
taken immediately before the mount syscall used to live only in the CLI
mount subcommand. They moved into a shared mount module below every
frontend, so TUI-wizard mounts and the GUI's in-process mounts now
inherit the same protection, including inside the forked child on the
daemonized FUSE path.
Post-quantum sidecars are vault-bound, and panic destroys the recovery mirrors
Hybrid post-quantum sidecars now carry a 32-byte binding to their vault
on every non-deniable create, enroll, and unlock path, so a swapped-in
foreign .kyber sidecar is refused with a clear message instead of a
confusing decryption failure. Deniable vaults keep the unbound format
on purpose, since a binding would link the sidecar to the vault for
anyone holding both files. Separately, every panic action now also
scrubs the crash-recovery mirrors (header-bak, meta-bak), so a
destroyed vault can no longer be rolled back from its own backups.
Other hardening
- macOS mount stability fixes for both FUSE-T and macFUSE.
- The
.kyberseed-file unlock now reserves its KDF memory fallibly (a memory-starved host reports an error instead of aborting) and zeroizes every secret intermediate. - Local-attacker race fixes on the panic, destroy, and deniable-mount paths, plus canonicalised mountpoints in the Unix unmount helpers.
- Dependency advisories cleared: the quick-xml pair
(RUSTSEC-2026-0194 / RUSTSEC-2026-0195) resolved without an ignore,
plus
anyhowandmemmap2updates.
v0.3.1 - 2026-06-17
Hardening and usability fixes, with a focus on running unprivileged on QubesOS AppVMs and closing several file-handling race conditions. Existing vaults open unchanged; there is no on-disk format change.
QubesOS AppVMs now work unprivileged
LUKSbox ran fine on stock Debian but failed on a QubesOS AppVM
unless you used sudo: the GUI aborted with NoGlutinConfigs and
adding a keyslot died trying to allocate the 256 MiB Argon2id
buffer. Root cause was the process-wide memory lock. mlockall
armed MCL_FUTURE (lock every future allocation), and on a host
with a finite RLIMIT_MEMLOCK ceiling (a QubesOS AppVM caps it at
128 MiB) any allocation past that ceiling then failed with ENOMEM
instead of being allowed to swap. That bricked both the Argon2id
buffer and Mesa's software-renderer framebuffers.
mlockallnow armsMCL_FUTUREonly whenRLIMIT_MEMLOCKis unlimited. On a finite ceiling it locks the current pages and stops there, so large transient allocations succeed.- The GUI re-executes itself once with
LIBGL_ALWAYS_SOFTWARE=1where there is no GPU (AppVMs, headless or forwarded X11), so it falls back to Mesa's software renderer instead of failing, then prints an actionable hint if even that is unavailable. - Adding a keyslot on a host that cannot allocate the Argon2id
buffer now returns a clear
KdfOutOfMemoryerror (with the size needed) instead of aborting the process.
The result: both the GUI and keyslot creation work on a stock
QubesOS AppVM with no sudo and no template changes. Key material
itself stays unswappable via memfd_secret (Linux 5.14+) or a
per-allocation mlock on the heap fallback, independent of the
MCL_FUTURE change. See the
Linux install guide for the
dpkg -i vs apt install ./ dependency note on minimal templates.
Stronger memory locking on constrained hosts, old kernels, and macOS
- The
SecretBoxheap fallback (used on Linux kernels older than 5.14 that lackmemfd_secret, and as the normal backing on macOS and the BSDs) now locks its page per allocation withmlock/munlockrather than relying solely on the process-widemlockall. This makes the per-secret locking that the macOS messaging already promised actually true, and closes the old-kernel gap on a finite memlock ceiling. - The Argon2id working matrix and the derived key buffer are now zeroized after use, including on the error and early-return paths.
Security: failure-closed symlink handling and detached-header re-lock (TOCTOU)
Two narrow race conditions in the file-handling layer were closed:
- Strict
LUKSBOX_NO_FOLLOW_SYMLINKS=1mode used a stat-then-open pattern, so an attacker who swapped a symlink in between the two calls could still make the open follow it. Both gate sites now apply the refusal atomically with the open:O_NOFOLLOWon Unix (anELOOPbecomes the policy refusal),FILE_FLAG_OPEN_REPARSE_POINTplus an attribute check on Windows. - The detached-header sidecar was rewritten atomically and then
reopened by path, so a racing directory swap could attach the
advisory lock to the wrong inode.
atomic_secure_writenow returns the inode it committed, and the reopen usesO_NOFOLLOWplus an inode-equality check against that value, so a swapped symlink or regular file is refused withPathSubstituted.
Security: Windows extraction no longer redirectable through an intermediate junction
The Windows branch of the secure extract helper (behind
luksbox get, the wizard/GUI extract, and recovery extraction) did
create + truncate on the caller-supplied path and only checked
for a reparse point on the final component, after truncating. An
attacker-controlled intermediate junction (for example
C:\tmp\extract pointing at C:\Windows\System32) could silently
redirect the plaintext write, an arbitrary-overwrite primitive
under an elevated run; even a final-component reparse point had its
target truncated before the check could refuse it. This closes the
Windows-side gap left after the Unix fix in v0.2.x.
The Windows path now refuses to extract through any reparse point
(junction, symlink, or mount point) anywhere in the path. It takes
the lexically-absolute target, opens it without truncating, refuses
a reparse-point or directory final component, then requires the
handle's GetFinalPathNameByHandleW result to equal the literal
target; any junction crossed on the way makes them differ, so the
write is refused with no destructive truncation. Regression tests:
crates/luksbox-core/tests/windows_extract_junction.rs.
Wizard and TUI keyslot flow
- Removed the post-creation "Enroll a FIDO2 keyslot now?" prompt. It fired even with no authenticator plugged in (a guaranteed failure) and duplicates the keyslot manager; add FIDO2 or any other keyslot later via "Open an existing vault" then "Manage keyslots".
- When two authenticators report the same "Manufacturer Product" label (for example two identical YubiKeys), the picker now appends each device's path so they can be told apart. This covers both the TUI and the GUI, which share the enumeration.
- The "memory not locked" warning is shown only for commands that
handle key material; read-only and metadata commands (
umount,info,header-dump, and similar) no longer print it.
GUI no longer carries a mounted vault into a new open/create
Switching to another vault in the GUI while one was mounted left the previous vault's "Vault mounted" panel (with its "Unmount" button) showing over the new vault, and an unclean unmount could leave that state stuck until the application was restarted. The confirmation that gates a vault switch was checking only for an open (unmounted) vault; a mounted vault has its filesystem handed to the mount thread, so that check missed it. The GUI now recognizes a live mount when you switch, shows a confirmation popup ("Unmount current vault first?"), and on confirm unmounts cleanly before continuing, so the new vault always opens with a fresh view.
v0.3.0 - 2026-06-15
Cross-platform FIDO2 keyslots and an in-place migration path.
FIDO2 keyslots are no longer platform-locked
A FIDO2 keyslot enrolled on Linux or macOS could not unlock the
same vault on Windows, and vice versa. The two backends drive the
authenticator's CTAP2 hmac-secret extension through different
stacks: libfido2 (Linux/macOS) forwards the 32-byte salt to the
device unchanged, while Windows' webauthn.dll is not a
passthrough, it applies the W3C WebAuthn-PRF derivation
T(x) = SHA-256("WebAuthn PRF"\0 || x) to the salt before the
device sees it. Same key, same on-disk salt, two different HMAC
outputs, two different key-encryption keys, unlock failure on the
other platform.
v0.3.0 fixes this at the slot-format level. Both backends now
converge on the device computing HMAC-SHA256(secret, T(salt)):
libfido2 applies T locally before talking to the device, and
webauthn.dll is left to apply the identical T itself. New
FIDO2 slots are cross-platform by construction.
User-verification tip: webauthn.dll always performs user
verification, while libfido2 only does so when a PIN is supplied,
and the two states select different per-credential secrets. Enroll
FIDO2 slots with a PIN so every platform derives from the same
secret.
Migrate existing FIDO2 slots in place
Because T is one-way, a FIDO2 slot whose on-disk convention
predates this fix cannot be reproduced through webauthn.dll and
will not open on Windows. Bring such slots forward with:
luksbox migrate-fido2-slot <vault> --slot N
The migration is in-place (no vault rewrite), idempotent, and
crash-safe. It currently covers wrap-style Fido2HmacSecret slots;
direct MVK-derived and fused TPM/hybrid-PQ FIDO2 variants are out
of scope for v0.3.0 and the command rejects them with guidance. See
the migrate-fido2-slot CLI page.
luksbox info now reports each FIDO2 slot's platform-compat status.
Fixed: luksbox mount default mode refused to start
The default daemonizing form luksbox mount <vault> <mountpoint>
failed on Linux with refusing to daemonize from a multithreaded process once the deferred-flush timer landed in v0.2.x: the mount
spawned the background flush-timer thread before the fork, and the
pre-fork single-threaded check then refused. --foreground was
unaffected. The flush timer is now created in the child after the
fork, so the default background mount works again.
Also in this release
- Nitrokey 3 added to the tested FIDO2 matrix, verified cross-platform end-to-end (enroll and unlock on Linux, macOS, and Windows; 158 B cred_id via the V4 slot layout).
- Ubuntu 26.04 (Resolute) build and CI coverage.
- GUI: the create-vault picker no longer hides extensionless
vaults (the old default name was
secret), matching the open-existing picker. Thanks to community contributor Matt Van Horn (@mvanhorn). - A round of durability and security hardening from the
pre-release review (detached-header locking, keyslot
serialization bounds, deniable FIDO2 envelope unlock,
--syncflush error reporting, TPM handle cleanup).
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:
-
Layer 1 -- deferred flush (default). Those ops now mark the in-memory tree dirty and return immediately. A background
luksbox-flush-timerthread (one per mount) drives a realVfs::flushevery 30 seconds when dirty, plus on unmount. Explicitfsync(2)/fsyncdir(2)syscalls remain eagerly synchronous (POSIX-required). Net effect for the user's report:rmreturns in milliseconds,cp -r 10k_files /mnt/vaultcompletes in seconds instead of hours. -
Layer 2 -- per-inode dirty tracking. When the flush DOES fire, only inodes that were actually written, truncated, or newly created since last flush get re-spilled. Untouched inodes reuse their existing on-disk chunk-list blocks byte-identically. Per-flush cost is now O(K = touched inodes), not O(N = total spilled inodes).
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:
- Compliance test: per-thread flush-op log asserts
sync_data_arearuns strictly beforewrite_metadata. - Bug-reappearance test: a thread-local
set_skip_fence_for_testhatch reproduces the v0.2.1 ordering on demand and verifies the bug recurs there, so a future refactor that drops the fence cannot silently regress. - Crash-impact test: a SimFile backing models page-cache vs
durable bytes; the fence is enabled / disabled across two
runs and the open-after-simulated-crash result matches the
real-world bug signature (
MetadataDeserialize) only in the disabled run.
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:
-
LUKSBOX_DEBUG_OPEN=1instrumentsVfs::opento print metadata head bytes, magic matched, per-inode parse outcome, andvalidate_metadata_treeresults to stderr. Lets a user with a refusing vault pinpoint the exact parse step that trips, without source modification. -
LUKSBOX_TOLERATE_BAD_CHUNK_LISTS=1(or the GUI's "Recovery mode (read-only, skips broken files)" checkbox, or the wizard's "Try opening in recovery mode?" prompt onMetadataDeserialize) installs broken inodes as zero-byte placeholders instead of refusing the whole vault. The user can then mount read-only and copy out every healthy file. A newError::ReadOnlyMountblocksVfs::flushfrom ever persisting the patched tree, so the size-zeroed broken inodes cannot overwrite the originalchunks_externalpointers. The GUI surfaces a modal listing every tolerated file's path + original size + AEAD-failure reason so the user knows what to re-source.
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
- Recent-vaults
[x]button is now actually clickable. Previously an egui z-order issue let the parent Frame'sinteract()swallow the click before it reached the button. Clicking now opens a centered confirmation modal (Forget / Forget AND Delete / Cancel) so the action isn't accidentally destructive. - Vault file picker "All files" filter now really shows all files. The earlier filter quirk hid extensionless vaults on some platforms.
- Open form gains an editable path field with a Browse button, so users can open vaults under non-standard paths or in shells that don't surface them in the picker.
Notes
- Anyone who hit the v0.2.1 durability bug: the lost chunk-list blocks are not recoverable from the live data area (the bytes never made it to disk). Recovery mode lets you copy every healthy file out. Re-source the broken files from their original location.
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 metadata region cap was 16 MiB. The encoded directory tree
for a multi-thousand-file vault overflowed it and surfaced as
MetadataBudgetExhausted-> ENOSPC. - Both the 8 KiB header (with all keyslots) and the metadata blob were written in place with a plain seek+write. A crash mid-write destroyed the only copy.
The fix adds A/B durability for both critical regions plus a higher metadata cap, all cross-platform (Linux, macOS, Windows):
MAX_METADATA_SIZEraised 16 MiB -> 64 MiB withDEFAULT_METADATA_REGION_SIZEto match.- LBM5 metadata format lowers the inline chunk-ref spill threshold from 1024 to 256 chunks per inode, so encoded trees stay compact even for very large vaults.
- LUKSBOX2 header magic with two new HMAC-authenticated flag
bits (
FLAG_HAS_HEADER_MIRROR,FLAG_HAS_METADATA_MIRROR). <vault>.lbx.header-bakand<vault>.lbx.meta-bakhold the intended new bytes for crash-safety recovery, rotated via temp+rename BEFORE every live overwrite (intended-state protocol, see Security below). On open, if the live region fails to parse, the recovery path reads + verifies the mirror under the same MVK and proceeds, then forces a flush so the live region is re-established.- Auto-upgrade on first flush: any operation against a v0.2.0 vault rewrites it as LUKSBOX2 + LBM5 with the new mirrors. No manual migration command required.
- Deniable vaults are explicitly excluded from the sidecar mirror protocol to preserve the >=7.99 bits/byte entropy property of their on-disk artefact set. Crash safety for deniable vaults stays on the existing path (the 36 KiB header has internal slot-layout redundancy).
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:
- Mirror file reads are stat-then-bounded so an attacker-planted
symlink to
/dev/zerocannot OOM the recovery path. - MVK rotation (
begin/commit_atomic_rotation) explicitly deletes both sidecar mirrors at commit time so the post-rotation vault cannot fall back to OLD-MVK-encrypted keyslots; fresh mirrors are written on the next live overwrite. - Mirror recovery is gated strictly on
Header::from_bytesparse failure; unlock-failure on an otherwise-parseable header does NOT trigger mirror fallback.
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.
- Linux libfuse and macFUSE: fixed.
- macOS FUSE-T: fixed in the same release.
- Windows WinFsp: N/A (Windows uses NTFS attributes, not POSIX modes; executable-by-file-extension is the Windows convention).
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:
- >=75% used: informational. CLI users see a one-line message on stderr; GUI users see a non-blocking toast.
- >=90% used: stronger warning recommending content archival
or migration to a new vault with a larger
--metadata-size.
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:
- At create time:
luksbox createprints an advisory noting the tested boundary. - At runtime: when the vault file on disk crosses 30 GiB, a one-shot CLI eprintln or GUI toast asks the user to verify the vault still unlocks and report any anomalies at https://github.com/PentHertz/LUKSbox/issues.
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:
sudo dpkg -i luksbox_*.deb(sudo honorssecure_path)su -thendpkg -i ...(login shell loads root's PATH)export PATH="$PATH:/sbin:/usr/sbin" && dpkg -i ...
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
- External chunk-list blocks in the data area instead of
inline
Vec<ChunkRef>inside the fixed metadata region. Practical per-vault content ceiling goes from ~8-10 GiB (v2) to no practical limit. Measured open time on this hardware: 19 ms at 1 GiB / 262K chunks; extrapolates to ~2 s at 100 GiB. - Per-file spill threshold is 1024 chunks (~4 MiB). Files below stay inline so the small-file case pays no extra read.
- AAD isolation between data chunks and chunk-list blocks: list
blocks live under a synthetic file_id (
real_id | (1<<63)) so the AEAD AAD intrinsically distinguishes them; a data chunk's ciphertext cannot decrypt as a chunk-list block or vice versa. luksbox migrate-to-v3 <src> --dst <new>reads any v2 vault and writes a fresh v3 vault. Source untouched; new vault gets a single passphrase keyslot, other keyslots can be re-enrolled afterwards.- Default flip to v3 happened only after: deniable rotation across
all 8 credential kinds was implemented and tested; fuzz target
- corpus added for the chunk-list parser; perf measured at 1 GiB
with sub-2-s extrapolated open at 100 GiB. Opt out per-vault
with
--format v2or viaLUKSBOX_FORMAT_V2=0in the env.
- corpus added for the chunk-list parser; perf measured at 1 GiB
with sub-2-s extrapolated open at 100 GiB. Opt out per-vault
with
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].
- Panic-destroy symlink TOCTOU (high).
luksbox panicand its wizard / GUI counterparts usedis_file()+ laterOpenOptions::open()-- racy. An attacker with write access to the vault's parent directory could swap in a symlink between the check and the destructive open, redirecting the random-bytes overwrite to a file of their choice. With--wipe-datathe blast radius was the entire file size; run as root, this was an arbitrary-file-overwrite primitive via a deliberate destructive command. Now all three callsites use a newsecure_open_existing_no_followhelper that opens withO_NOFOLLOW(Unix) /FILE_FLAG_OPEN_REPARSE_POINT(Windows) and refuses non-regular files; handles open BEFORE the confirmation prompt so the prompt itself cannot be raced. - Deniable open bypassed
LUKSBOX_NO_FOLLOW_SYMLINKS.try_open_envelope_v2_deniableandopen_with_mvk_deniableused rawOpenOptionsand skipped the hardenedopen_rw_checked- post-lock
verify_path_inodepath that the standardContainer::opentakes. Now both deniable entry points go through the same hardened path.
- post-lock
- Deniable mount mountpoint check. Standard
luksbox mountusesO_DIRECTORY | O_NOFOLLOW+ canonical-path deny-list. Deniable mount was using onlyis_dir() + canonicalize()-- same hardened check is now applied to deniable mount. - Header-backup no-clobber race.
luksbox header-backupdidout.exists()thensecure_create_or_truncate(out)-- racy. Now usesatomic_secure_create_new(POSIXlink(2)/ WindowsMoveFileExW(0)). - Mount canonicalize-before-open.
cmd_mountand the FUSE-T helper used to callpath.canonicalize()BEFORE the hardened open, so whenLUKSBOX_NO_FOLLOW_SYMLINKS=1was set the symlink was already resolved by the time the no-follow check fired. Added a preflight (viastd::fs::symlink_metadata) that runs BEFORE canonicalize for both vault and--headerpaths. - Unlock prescan honors no-follow policy. The kind-dispatch
header peek (~14 callsites across CLI + GUI) used raw
File::openwhich followed symlinks even with the policy gate set. Now routed through a newopen_existing_read_no_follow_policyhelper. Default behaviour (env var unset) still follows symlinks so legit users aren't broken.
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
- FUSE-T support on macOS, in addition to macFUSE. FUSE-T is
the kext-free FUSE implementation that talks to the macOS NFS
client over a localhost loopback; no kernel extension to install,
no Recovery Mode dance, no Apple Silicon-specific opt-in. Ships
as a separate
.dmgper backend so users can pick:LUKSbox.dmg(FUSE-T, the new default-preferred path) orLUKSbox-macfuse.dmg(legacy macFUSE, kept for users who already use it elsewhere). - Subprocess isolation for FUSE-T. The FUSE-T helper runs in
its own process so when libfuse-t aborts itself during unmount
teardown (a known libfuse-t behaviour we cannot intercept from
Rust), only the child dies and the GUI keeps running. MVK is
passed to the child over an anonymous stdin pipe, zeroized on
both sides immediately after transfer. Architecture in
docs/MACOS_FUSE_T.md. - Optional macOS sandbox profile for the FUSE-T helper
subprocess (
LUKSBOX_SANDBOX_HELPER=1). Restricts read/write to the vault directory + mountpoint + a small allowlist of system paths. Hard-fails if the profile file is missing rather than silently downgrading to unsandboxed.
Added, deniable-header mode (v2)
- Deniable vaults. A new on-disk format where every byte of
the 8 KiB header is indistinguishable from random output: no
magic, no version, no slot table, no parseable structure
visible to an attacker without the right credentials. Two-layer
AEAD envelope (passphrase-derived
KEK_envelopeopens the slot, then a per-slotKEK_factorsunwraps the MVK). Every failure mode collapses to a singleOpaqueUnlockFailedto preserve the no-oracle property. - All eight credential variants supported: passphrase,
passphrase + FIDO2, passphrase + TPM, passphrase + TPM + FIDO2,
- the four hybrid-PQ counterparts. Every variant carries a mandatory passphrase (the envelope-opening key) by design; pure FIDO2 / pure TPM variants do not exist in deniable mode because they would re-introduce the fingerprinting tells the v2 design set out to fix.
- GUI, CLI, and wizard support for deniable create, open, mount, slot enroll (passphrase / FIDO2 / TPM / TPM+FIDO2 / all hybrid-PQ combos), and slot revoke. The GUI hides the FIDO2-direct radio at create time when "deniable" is ticked (the variant cannot be deniable-compatible).
- Deniable anchor sidecars (256 B AEAD-encrypted) for rollback detection without leaking the existence of an anchor.
- Full design rationale in
docs/DENIABLE_HEADER.md.
Added, mount UX
- Private mount option on macOS (FUSE-T only). New
--private-mountCLI flag, matching "Mount privately" button in the GUI, and a yes/no prompt in the wizard. Mounts the vault under~/Library/LUKSbox/Mounts/<vault-name>instead of/Volumes/<vault-name>so the mountpoint name is invisible to other user accounts on shared Macs. Seeluksbox mount --private-mount. - Pre-flight check before spawning the FUSE-T helper detects the "another process already holds the vault flock" case and surfaces a friendly toast instead of letting the child exit with a cryptic non-zero status.
Added, packaging
.debRecommendstpm-udev+tpm2-toolson Debian-family..rpmRecommendstpm2-tss+tpm2-toolson Fedora-family. Installing viaapt/dnfnow brings in the/dev/tpm*udev rules and thetsssystem group by default. Post-installusermod -aG tss "$USER"is still required (Debian / Fedora convention forbids silent privileged-group additions). See Permissions.
Added, fuzz coverage
- Two new libFuzzer + AFL++ targets:
chunk_aead_decrypt(per-chunkaead::opencallsite with attacker-controlled bytes) andanchor_parse(standard + deniable readers). Added to both CI matrices (5-min smoke on PR + 30-min nightly). - AFL++ harness
auth_then_processbrought in lock-step with the libFuzzer version (was still on bincode while libFuzzer had migrated to postcard + magic-prefix dispatch).
Improved, deniable open UX
- Anchor verification error messages on the deniable open
path. A pre-flight check rejects missing / wrong-size /
symlink anchors with a specific message; a post-credential
error is translated to "Anchor file does not decrypt under
this vault's master key" instead of the generic "unlock
failed". Format-layer error stays a single
OpaqueUnlockFailedso deniability is unaffected.
Changed, security hardening
- Sandbox hard-fail.
LUKSBOX_SANDBOX_HELPER=1now refuses to spawn the FUSE-T helper unsandboxed if the profile file is missing, instead of falling back silently. The user opted in, so honour it or refuse, never silently weaken. Keyslot::fido2_cred_idzeroized on drop. The CTAP2 cred-id is a public handle (not cryptographically secret), but zeroizing the heap buffer matches the rest of the codebase's defence-in-depth posture.- O_NOFOLLOW on anchor reads. Both
anchor::read_and_verify(48 B standard) andanchor::deniable_read_and_verify(256 B deniable) now refuse to dereference symlinks at the format layer, defending against a swap between the GUI / CLI preflight and the actual open. - FD-based mountpoint validation. The CLI now opens the
mountpoint with
O_DIRECTORY | O_NOFOLLOWas an atomic "directory + not a symlink" check, replacing a small TOCTOU window betweenis_dir()andcanonicalize().
Security review
- New "Not defended" rows in SECURITY.md and on the threat-model page: filesystem snapshots while unlocked, kernel-resident attacker, hardware tampering beyond the FIDO2 device, evil-maid on the binary itself, supply-chain on dependencies.
cargo audit: one unmaintained advisory remaining,registry 1.3.0reachable only viawinfsp_wrs(Windows). No fix possible from our side; upstream-tracked.- dudect re-run on
Header::verify_hmac:|t| = 0.563over 47 K samples, well under the 3.0 leak threshold. Constant-time property preserved.
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
- R13-01
secure_create_or_truncate(used byluksbox getand the GUI extract path) now opens the destination throughopenat(parent_dir_fd, basename, O_RDWR|O_CREAT|O_TRUNC|O_NOFOLLOW, 0600)against a canonical parent fd. Closes the intermediate- directory symlink-swap window that the prior Round 12 fix left open as a documented follow-up. Permission narrowed viafchmodon the open fd; Windows reparse-point rejection retained. - R13-02
luksbox header restoreno longer re-opens the vault path after the HMAC verify. NewContainer::restore_header_bytesreuses the container's already-verified, already-inode-boundself.filehandle (inline) or routes throughatomic_secure_write(detached). The--no-verifydirect write also addsO_NOFOLLOWon Unix and refuses reparse points on Windows.
MEDIUM fixes
- R13-03
Vfs::real_sizeclamps the chunk-0 authenticated u64 against the inode's allocated chunk capacity. Hostile hide-size vaults can no longer panic stat / read / mount via out-of-rangeinode.chunks[idx]. - R13-04
Container::persist_headerusessync_all()on inline- deniable headers, and
atomic_secure_write(temp + fsync + rename + sync_parent_dir) on detached, then re-opens the lock handle to the new inode. A power loss mid-persist no longer leaves a half-rewritten header / sidecar.
- deniable headers, and
- R13-05
.kyberseed-file reads open withO_NOFOLLOW(Unix) / reparse-point rejection (Windows), require a regular file of exactly the fixed format length, thenread_exact. - R13-06 Hybrid sidecar reader preflights
metadata(), requires a regular file under 32 KiB, thenread_exact. Closes the unboundedread_to_endpath on both Unix and Windows. - R13-07 New
luksbox_vfs::MAX_FILE_SIZE = 1 << 44cap +Error::FileSizeExceedsCapvariant.Vfs::writeandtruncaterefuse oversize targets BEFOREpadded_chunk_countcan feednext_power_of_twoa panicking value or the chunk-allocation loop can exhaust RAM.
LOW fixes
- R13-08
luksbox-mount's FUSEreadcaps the requester- suppliedsizeat 16 MiB internally before the vec allocation. - R13-09
SecretBox::cloneallocates a fresh secret-memory backing andcopy_from_slices directly between the two allocator-owned regions. No by-value[u8; KEY_LEN]stack temporary.
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
- Fix-status table at docs/SECURITY_AUDIT_ROUND_12.md.
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
- R12-02 CLI seed-file passphrase blank-= reuse envelope,
matching GUI + wizard. New
cli_pq_decap_with_fallback. - R12-03 Helper subprocess canonicalizes
--headerand the sandbox profile gains${HEADER_DIR}allow rules. - R12-04
MountBackend::Subprocesshasimpl Dropthat kills- reaps the helper child on GUI panic / force-quit.
- R12-05 Helper mountpoint validation uses the same
O_DIRECTORY|O_NOFOLLOWprobe + deny-list as the parent CLI. - R12-06 Hybrid sidecar reads use
O_NOFOLLOW(ELOOPon symlinked.hybridfiles).
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
- R12-08
cmd_mountre-probes the canonical mountpoint inode immediately before the mount syscall and refuses if it changed. - R12-11
open_rw_checkedcaptures the canonical path at first open;verify_path_inodeopens that canonical path withO_NOFOLLOW. Legitimate symlinked-vault workflows preserved. - R12-14 Formally superseded by R12-11 - inverting
open_rw_checked's default would break legitimate workflows for no remaining security gain. - R12-15 Windows reparse-point coverage on the anchor + extract
paths via
FILE_FLAG_OPEN_REPARSE_POINT+ post-open attribute check. - R12-17 New
MasterVolumeKey::from_zeroizingandKeyEncryptionKey::from_zeroizingconstructors take a reference to aZeroizing<[u8;KEY_LEN]>, eliminating the by-valueCopystack-residence pattern at the type level. Helper subprocess MVK construction migrated. - R12-19
HmacSecretis nowpub struct HmacSecret([u8;32])withZeroize + ZeroizeOnDrop,Deref, redactedDebug, and constant-timePartialEq. All three backends updated.
Round 12 closed cleanly. All 19 findings shipped fixes.
New test infrastructure shipped this round
dudect_deniable_envelopebench atcrates/luksbox-format/benches/dudect_deniable_envelope.rs. The statistical timing regressor for R12-01. Expected to FAIL on current branch (|t| > 3.0) and pass after the fix. Reproduce:cargo bench --bench dudect_deniable_envelope -p luksbox-formatdeniable_envelope_multi_slotfuzz target (libFuzzer + AFL++). Builds a real deniable header with a fuzzer-selected slot occupancy bitmap, then drivestry_open_envelope_v2with attacker input. Catches regressions to a non-opaque error variant or a panic on the multi-slot path. Wired into both the 5-min PR CI smoke and the 30-min nightly. Reproduce:cargo +nightly fuzz run deniable_envelope_multi_slot # or AFL++: cargo afl build --release --bin deniable_envelope_multi_slot afl-fuzz -i fuzz-afl/seeds/deniable_envelope_multi_slot \ -o fuzz-afl/out/deniable_envelope_multi_slot \ -- target/release/deniable_envelope_multi_slotround12_findings.rsregression suite atcrates/luksbox-format/tests/round12_findings.rs. 7 tests: 2 always-run smoke tests + 5#[ignore]-gated placeholders, one per open HIGH finding. Each placeholder is the regression slot the matching fix PR will populate. Reproduce:# The two always-run tests (smoke + functional) cargo test --test round12_findings -p luksbox-format # The placeholders (will panic until each fix lands) cargo test --test round12_findings -p luksbox-format -- --ignored
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
- WinFsp: files copied via Explorer disappeared after unmount /
remount. The
Cleanupcallback only flushed the VFS metadata on the DELETE path, so normalCreateFile -> WriteFile -> CloseHandleleft chunks on disk with no inode pointing at them. Fixed; integration test added to the WinFsp CI job that runs on every push. - GUI: ML-KEM-1024 TPM keyslots could not be unlocked. Hybrid PQ + TPM unlock dispatch only matched the 768 variants, silently skipping every 1024-grade slot. Fixed.
- macOS Developer ID signing pipeline failures. PBES2 vs PBES1
PKCS12 incompatibility with
security import(now handled withopenssl pkcs12 -export -legacy ...); AMFI strict-XML rejection of comments in the entitlements<dict>(comments stripped, rationale moved todist/macos/README.md). - Test pollution between parallel symlink tests. Env-var leak
between
nofollow_symlinks_env_var_refuses_symlinked_vaultandsymlink_to_real_vault_opens_cleanly; fixed with a per-fileOnceLock<Mutex<()>>serializing env-mutating tests.
Added - forensic / partial-recovery CLI toolkit
Walkthrough on the Forensics page.
header-backup- save the 8 KiB header bytes to a separate file (no unlock required, mode 0600).header-restore- restore from backup, HMAC-verified by default;--no-verifyenumerated as an operator-explicit safety bypass in SECURITY.md sec.3.header-dump- JSON dump of every inode, chunk reference, generation counter, and keyslot summary. Read-only.check- walk every used chunk, AEAD- decrypt, report per-chunk status. Exit non-zero on failure.--jsonfor tooling.extract --tolerate-errors- best-effort extraction; writes 4 KiB zeros for unrecoverable chunks. Flag is mandatory so users don't silently capture lossy output.
Added - release pipeline
- Apple Developer ID codesigning + notarization for macOS
.dmgreleases. Opens with the standard "downloaded from internet" prompt rather than the Gatekeeper block. - Windows static-CRT linking. No more
VCRUNTIME140.dll,MSVCP140.dll, orapi-ms-win-crt-*.dllruntime dependency. End users no longer need a Visual C++ Redistributable. SmartScreen still warns on first launch (EV Authenticode is on the v0.2 roadmap). - Per-Ubuntu-release
.debbuilds - separate package per supported Ubuntu line so apt resolves the matchinglibfido2-1/libfuse3-3/libssl3runtime. - GitHub Artifact Attestations (Sigstore-backed) on every
release:
gh attestation verify <file> --owner penthertz.
Changed - security hardening
Non-breaking tightenings of the safe envelope. No vault or workflow that worked under v0.1.0 is affected.
.kyberseed-file Argon2id memory cap:SAFE_M_COST_KIB_MAXlowered from 4 GiB to 512 MiB. Peak Argon2id memory request a hostile.kybercan force drops from 16 TiB to 1 TiB. All 5 existing seed-file DoS-guard regression tests still pass.- libfido2 cred-ID pointer null-check: defends the
from_raw_partsunsafe block against a hostile / firmware-buggy authenticator returning(id_len > 0, id_ptr = NULL). - WebAuthn DLL trust-boundary documentation: makes explicit why
the Windows path doesn't need the same defence (
webauthn.dllis part of Windows itself; trusting it is the same trust we place in every Win32 API call). - SECURITY.md sec.3 now enumerates every operator-explicit safety
bypass (
LUKSBOX_NO_LOCK=1,LUKSBOX_NO_FOLLOW_SYMLINKS=1,header restore --no-verify) with their preconditions and consequences.
Documentation
- CRYPTO_SPEC sec.3.9 Per-chunk encryption layering - canonical reference for the three-layer chunk-protection property (per-chunk random nonce, binding AAD, per-file derived key) with mermaid diagram, source line refs, and an explicit "what removing each layer would break" walkthrough.
- CRYPTO_SPEC sec.sec.3.4 - 3.8 complete the on-disk footprint:
detached headers, the
<file>.tmp.<16hex>transient temp-file convention,<vault>.rotatingMVK-rotation temp, GUI state files ($XDG_DATA_HOME/luksbox/{recent,preferences}.json), and the crash-orphan classification policy. - New DISCLAIMER.md + matching Disclaimer page restating Apache 2.0 sec.7-sec.8 in plain English. New "use LUKSbox for shared / backup copies, not as your only copy" notice on the docs landing page, README, Quickstart, and homepage FAQ.
Known limitations
- Windows SmartScreen still warns on first launch (EV Authenticode certificate planned for v0.2).
- Apple Silicon Macs need the one-time Recovery Mode -> Reduced
Security setup before macFUSE's kernel extension loads - only
required for
mount; CLI / GUI / extract work without it. - Format compatibility guarantee remains pre-1.0; v0.1.x reads every v0.1.x vault, format may evolve under audit guidance before v1.0.
v0.1.0 - 2026-05-06
Initial public release. The core feature set was audit-tracked through 9 internal review rounds before the cut.
- Encrypted vault containers with up to 8 keyslots
- Keyslot factors: passphrase (Argon2id), FIDO2 (CTAP2 hmac-secret
- cred-ID-derived modes), TPM 2.0 (7 variants), Windows Hello, hybrid post-quantum (ML-KEM-768 / ML-KEM-1024 + classical)
- AES-256-GCM-SIV default cipher (legacy AES-256-GCM and ChaCha20-Poly1305 also supported)
- FUSE (Linux + macFUSE on macOS) + WinFsp (Windows) mount adapters
- Anchor-based rollback detection (separate-storage sidecar)
- MVK rotation with crash-safe
.rotatingtemp + atomic commit - See the audit log for per-round summaries
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.