luksbox create
Create a new encrypted container.
luksbox create [OPTIONS] <PATH>
Creates a new vault at <PATH> with one initial keyslot. The kind
of slot 0 is set by --kind (passphrase by default).
An empty vault is already about 64 MiB on disk, plus a same-size
<PATH>.meta-bak mirror: the encrypted metadata region is reserved
up front so the vault never needs resizing. Pass
--metadata-size 4M (or 1M) for a small vault; it caps how many
files and chunks the vault can hold, not the size of any one file, and
cannot be changed later.
Examples
Passphrase-only vault (default)
luksbox create my.lbx
Creates my.lbx with one passphrase keyslot using AES-256-GCM-SIV,
the interactive Argon2id preset and
header format v3.
Detached header
luksbox create my.lbx --header /media/usb/my.hdr
Writes the 8 KiB header to a separate file. Without that sidecar,
my.lbx is unreadable - no magic bytes, no keyslot table, just
random data.
FIDO2-protected vault
luksbox create my.lbx --kind fido2
# Two touches: makeCredential + getAssertion
Adds a FIDO2 keyslot as slot 0. This is the only keyslot: lose the device and you lose the vault. Add a backup passphrase right away, unlocking with the device:
luksbox enroll my.lbx --fido2 --kind passphrase
Hybrid post-quantum
# Writes the vault AND a new Kyber seed file at the --pq-hybrid path
# (keep the seed on separate storage)
luksbox create my.lbx --kind hybrid-pq --pq-hybrid /media/usb/my.kyber
Or for the strongest variant (FIPS 203 cat 5):
luksbox create my.lbx --kind hybrid-pq1024-fido2 \
--pq-hybrid /media/usb/my.kyber
TPM 2.0 or Secure Enclave
TPM and Secure Enclave keyslots cannot be the first slot. Create the
vault with a passphrase (or FIDO2), then add the hardware-bound slot
with enroll; the first slot stays as your recovery
path:
luksbox create my.lbx
luksbox enroll my.lbx --kind tpm2 --tpm-pin-srk
--tpm-pin-srk (a global flag, or LUKSBOX_TPM_PIN_SRK=1) records the
TPM's storage root key in every new TPM keyslot, so unlock refuses a
substituted key before anything is unsealed (this defeats an active
TPM-bus interposer). A pinned slot needs LUKSbox 0.6.0+ to unlock.
luksbox info shows which TPM slots are pinned. See
TPM 2.0 keyslots.
Key flags
| Flag | Purpose |
|---|---|
--kind <KIND> |
First keyslot kind: passphrase (default), fido2, fido2-direct, hybrid-pq, hybrid-pq-fido2, hybrid-pq1024, hybrid-pq1024-fido2. TPM and Secure Enclave kinds are added afterwards with enroll |
--cipher <aes|aes-gcm|chacha> |
AEAD primitive. aes (default) is AES-256-GCM-SIV per RFC 8452 |
--kdf <interactive|moderate|sensitive> |
Argon2id preset. sensitive multiplies brute-force cost by 6x |
--kdf-target-time <TIME> |
Instead of a preset, calibrate Argon2id on this machine so one unlock takes about this long: 500ms, 5s, 30s (100 ms to 30 s). Cannot be combined with --kdf. See the warning below |
--header <PATH> |
Write header to a separate sidecar file |
--header-v2 |
Keep header format v2 so LUKSbox 0.2.1-0.5.x can still open the vault, at the cost of header rollback detection. Implied by --format v2. See Header format v3 |
--anchor <PATH> |
Create a rollback-detection anchor at this path. Keep it on separate storage and pass --anchor <PATH> to later commands, which then refuse a rolled-back vault |
--fido2-device <PATH> |
Pin to a specific FIDO2 device (see list-fido2-devices) |
--pq-hybrid <PATH> |
Where to write the new .kyber seed file (required for hybrid-PQ kinds) |
--metadata-size <BYTES> |
Reserved metadata region size (default + cap: 64 MiB). Lower this only for tiny demo vaults; higher values are rejected at the CLI boundary. |
--max-size <SIZE> |
Maximum size of the vault file: 6G, 500M, 1.5G (1024-based), or 0 / unlimited (the default). Writes that would grow the vault past it fail with "no space left". Change it later with luksbox size --max. Needs --format v3 (the default); a vault with a limit needs LUKSbox 0.6.1+ to open. |
--format <v2|v3> |
On-disk metadata format (default v3). See the format section below. |
--pad-files |
Pad each file's chunk count to next power of 2 (hides exact size from disk forensics) |
--hide-sizes |
Move the size field into AEAD-encrypted chunks (implies --pad-files) |
Weak Argon2id warning
--kdf-target-time keeps 3 passes and scales the memory cost to hit
the target. A short target on a slow machine can land below the RFC
9106 floor (64 MiB, 3 passes); since 0.6.0 create then warns, and
still creates the vault:
warning: Argon2id memory cost 32768 KiB is below the 64 MiB floor (RFC 9106).
A stolen vault is then cheap to brute-force; prefer the interactive preset (256 MiB, t=3) or stronger unless this host cannot allocate it.
Pick a longer target or a --kdf preset (every preset is above the
floor). luksbox kdf-bench shows what each preset costs on this
machine.
Header format v3
New vaults use header format v3 (LUKSBOX3, default since 0.6.0).
Every keyslot change gives the header a new generation number that the
--anchor records, so an older copy of the header put back over the
vault (for example a backup taken before a revoke) is refused at an
anchored open. A v3 vault needs LUKSbox 0.6.0 or later to open.
Pass --header-v2 when LUKSbox 0.2.1-0.5.x must still open the vault;
you can move it to v3 later with
luksbox upgrade-header, which is one-way.
--format v2 keeps the old header automatically. --header-v3 asks
for v3 explicitly (for scripts) and is refused together with
--format v2.
luksbox create shared.lbx --header-v2
On-disk format: v2 vs v3
Every new vault picks one of two on-disk envelopes. The choice is permanent for that vault.
| v3 (default, the v0.2.1 format) | v2 (legacy) | |
|---|---|---|
| Metadata magic | LBM\x05 (LBM\x06 while a maximum size is set) |
LBM\x02 |
| Header magic | LUKSBOX3 (header format v3), or LUKSBOX2 with --header-v2 |
LUKSBOX1 |
| Per-file chunk list location | External: linked chain of encrypted chunk-list blocks in the data area. Files above 256 chunks (~1 MiB plaintext) spill; tiny files stay inline. | Inline: Vec<ChunkRef> packed into the fixed metadata region. |
| Practical per-vault content ceiling | Tested up to ~30 GiB; engineered for much more. 64 MiB metadata cap. | ~8-10 GiB before the metadata region overflows. |
| Crash-safety | <vault>.lbx.{header,meta}-bak sidecar mirrors; mid-write force-quit recovers automatically. |
NOT crash-safe. A force-quit mid-write can leave the vault permanently unopenable. |
| Persistent chmod / hardlinks / symlinks | Yes | No |
| Reader compatibility | LUKSbox 0.6.0+ (header format v3); 0.2.1+ with --header-v2; 0.6.1+ while a maximum size is set |
Every LUKSbox release ever shipped |
Pick the v3 default unless you need to share the new
vault with a colleague on a pre-v0.3 LUKSbox install AND can
suppress the auto-upgrade trigger via LUKSBOX_FORMAT_V2=1 (see
below). Otherwise the first write by a
current LUKSbox upgrades the vault to v3 and the old binary can no
longer open it.
# Default: v3 (LBM5 + sidecar mirrors, header format v3)
luksbox create my.lbx
# Explicit v2 legacy for cross-version compatibility
luksbox create my.lbx --format v2
# Power-user knob: shrink the metadata region for a demo vault
luksbox create demo.lbx --format v2 --metadata-size 256K
Migrating an existing v2 vault to v3
luksbox migrate-to-v3 old-v2.lbx --dst new-v3.lbx
Reads the v2 vault, creates a fresh v3 vault, copies every file
through the VFS read API. Source vault is left untouched; verify the
new one opens cleanly (luksbox info new-v3.lbx) before deleting
the source. The destination is created with a single passphrase
keyslot -- re-enroll other keyslots afterwards via luksbox enroll.
In scripts its passphrase comes from LUKSBOX_NEW_PASSPHRASE; since
0.6.0, LUKSBOX_PASSPHRASE only unlocks the source. The destination
gets header format v2: run
luksbox upgrade-header new-v3.lbx afterwards
for header format v3.
Deniable vaults can be created in v3 format directly via the wizard
(it asks for the format alongside the cipher and KDF params); there's
no automated migrate-to-v3 path for deniable vaults yet because the
deniable open ceremony is interactive.
Keeping a v2 vault on v2
A --format v2 vault is upgraded to v3 the first time a current
LUKSbox writes to it. To keep it readable by the old binary, set
LUKSBOX_FORMAT_V2 to any non-empty value (1) wherever a current
LUKSbox writes to that vault:
export LUKSBOX_FORMAT_V2=1 # writes keep --format v2 vaults on v2
luksbox create itself always follows --format, whatever the
variable says.
What gets created on disk
| File | When |
|---|---|
<PATH> (the .lbx) |
Always |
<PATH>.meta-bak, <PATH>.header-bak |
--format v3 (the default) - crash-recovery mirrors of the metadata region and the header (with --header, the header mirror sits next to the header file) |
The --header file |
When --header is passed |
The --anchor file |
When --anchor is passed |
<PATH>.hybrid |
For hybrid-PQ kinds only - small sidecar with ML-KEM data |
The --pq-hybrid file |
For hybrid-PQ kinds only - the new .kyber seed file create writes |
After create, the vault is empty. Use luksbox put to add files
or luksbox mount to mount as a drive and write to it normally.