/ Documentation / CLI reference / luksbox create

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.