.\" Manpage for vaptvupt (formerly zupt; INPI Brasil trademark rename in v3.0.0) .\" SPDX-License-Identifier: AGPL-3.0-or-later .\" Copyright (c) 2025-2026 Cristian Cezar Moisés .TH VAPTVUPT 1 "July 2026" "vaptvupt 4.2.1" "User Commands" .SH NAME vaptvupt \- post-quantum backup compression utility (formerly zupt) .SH SYNOPSIS .B vaptvupt compress .RI [ options ] .I out.zupt .I files... .br .B vaptvupt extract .RI [ options ] .I archive.zupt .br .B vaptvupt list .RI [ options ] .I archive.zupt .br .B vaptvupt test .RI [ options ] .I archive.zupt .br .B vaptvupt info .I archive.zupt .br .B vaptvupt bench .I files/dirs... .br .B vaptvupt disk backup .RI [ options ] .I out.zupt .I device .br .B vaptvupt disk restore .RI [ options ] .I archive.zupt .I device .br .B vaptvupt keygen .RI [ options ] .br .B vaptvupt version .br .B vaptvupt help .PP The legacy command name .B zupt is preserved as an alias for backward compatibility; both invocations behave identically. .SH DESCRIPTION .B vaptvupt compresses and encrypts files, directories, and whole block devices into self-contained, authenticated archives with the .B .zupt extension. It targets long-lived backup storage where: .RS .IP \(bu 2 the archive is written once and restored under time pressure many years later; .IP \(bu 2 the encryption envelope must remain secure against a future cryptographically-relevant quantum computer (ML-KEM-768); .IP \(bu 2 every byte of the archive — header, footer, per-block metadata, comments — is authenticated, and a single bit-flip is rejected at restore time. .RE .PP The compression layer is the .B VaptVupt LZ + ANS codec (version 2.53.3), which prioritises decode speed and ratio over encode speed. Aggregate decode throughput on this build is 1.27\(mu zstd\-3; encode throughput is 0.2\(mu\(en0.5\(mu zstd\-3 depending on content. See .B PERFORMANCE below. .PP The on-disk format is v1.6 and has been wire-compatible since release v2.3.1. The product was renamed from .B Zupt to .B VaptVupt in v3.0.0 because of a prior INPI Brasil trademark registration of the name "Zupt" for unrelated software. The .B .zupt file extension and the .B ZUPT header magic bytes are unchanged: archives produced by any v2.x release extract cleanly under v3.0.0 and vice versa. .SH COMMANDS .TP .B compress Create an archive. Default codec is VaptVupt (level 7). Compression is multi-threaded; one worker per detected CPU by default. .TP .B extract Decompress an archive into the current directory (or .BR -o " " \fIdir\fR ). Refuses to write outside the destination directory (path-traversal defence). Files are written with their original permissions and mtime preserved. .TP .B list Print archive metadata: per-file path, size, mtime, mode, compressed size, codec. With .B --verbose also prints per-block sizes and HMAC tags (first 8 bytes). .TP .B test Decompress all blocks in memory and verify HMAC tags + archive integrity trailer. Does not write any files. Use to validate an archive without restoring it. Exit code is non-zero on any failure. .TP .B info Print archive header metadata without requiring the decryption key. Reports: format version, codec, encryption type (none / PBKDF2 / Argon2id / ML-KEM-768+X25519 hybrid / ML-KEM-768 pure-PQ), KDF iteration count, file count, creation timestamp, archive UUID, AIT presence. Safe to run on an untrusted archive. .TP .B bench Compare compression levels 1\(en9 on the supplied files; reports ratio and encode/decode throughput per level. Useful when picking the right .B -l for a given workload. .TP .B disk backup Read a block device and write a sparse-aware archive. Detects all-zero regions and records them as runs rather than compressing them. .TP .B disk restore Inverse of .BR "disk backup" . Writes the archive's contents back to a block device. Verifies target device size before writing; refuses if the target is smaller than the archived size. With .B --sync issues .BR fsync (2) after each block. .TP .B keygen Generate a key file. With no PQ flag, writes a 32-byte raw key for keyfile-mode encryption. With .B --pq generates a native ML-KEM-768 + X25519 \fBhybrid\fR keypair for .B --pq encryption (in-tree crypto; no external library). With .B --pq-only generates a native pure ML-KEM-768 keypair (magic .BR ZPQK ) for .B --pq-only encryption. With .B --sdk generates a keypair for the optional .B --pq-sdk mode, and with .B --box a libpqvaptvupt sealed-box keypair for .B --pq-box (both need a .B WITH_SDK=1 build). With .B --pub extracts the public key from an existing private key (combine with the matching PQ flag, e.g. .BR "keygen --pub --pq-only" ). .SH GLOBAL OPTIONS .TP .BR -v ", " --verbose Print per-file and per-block details during compress/extract/list/test. .TP .BR -q ", " --quiet Suppress non-error output. .TP .BR -j " " \fIN\fR ", " --jobs " " \fIN\fR Worker thread count for parallel compression. Default: number of online CPUs. .SH COMPRESS OPTIONS .TP .BR -l " " \fI1..9\fR ", " --level " " \fI1..9\fR Compression level. 1\(en2 = ultra-fast (~80 MB/s encode on typical hardware, lower ratio). 3\(en7 = balanced (default 7). 8\(en9 = extreme (optimal parsing, ~5\(en10\(mu slower encode, best ratio). .TP .B --codec \fIid\fR Force a specific codec by id. Accepted values: .BR store " (0x0000), " .BR vaptvupt-lz " (0x0008), " .BR vaptvupt-lzh " (0x0009), " .BR vaptvupt-lzhp " (0x000A), " .BR vaptvupt " (0x0010 — default), " .BR auto " (0xFFFF — pick at runtime)." .TP .BR -p " " \fIpassword\fR Enable password-based encryption (PBKDF2-SHA256 KDF; Argon2id is available only in a WITH_SDK=1 build via \fB--kdf argon2id\fR). Reading the password from a flag exposes it in .BR ps (1) output; prefer .B --pass-file or interactive prompt. .TP .B --pass-file \fIpath\fR Read password from the first line of the file. The file's permission bits should be 0600. .TP .B --pass-fd \fIN\fR Read password from file descriptor N. .TP .B --kdf \fIalgo\fR Choose key-derivation function for password mode: .BR argon2id " (default since v2.4.1; memory-hard) or " .BR pbkdf2 " (SHA-256, 600 000 iter; needed for compatibility with v2.4.0 and earlier readers)." .TP .B --keyfile \fIpath\fR Use a 32-byte raw key file (generated with .BR "vaptvupt keygen" ). .TP .B --pq \fIpub\fR Enable native post-quantum \fBhybrid\fR encryption (envelope type 0x02, recommended). Combines ML-KEM-768 (FIPS 203) with X25519 (RFC 7748) so the archive key is secure unless \fBboth\fR the lattice KEM and the elliptic-curve exchange are broken. Uses the in-tree crypto only — no external library, always available. The .I pub argument is the recipient's public-key file from .BR "vaptvupt keygen" . On extraction, pass the secret key: .B --pq \fIpriv\fR. .TP .B --pq-only \fIpub\fR Enable native \fBfull\fR (pure) post-quantum encryption (envelope type 0x06). ML-KEM-768 is the \fIsole\fR key-establishment mechanism — no X25519 component. Choose this only when a policy mandates a single NIST-standardised PQ primitive with no classical KEM in the envelope (e.g. CNSA 2.0-style "PQ-only" postures). The archive key is SHA3-512(ml_ss || ml_ct || "ZUPT-PQ-ONLY-v1"). Note the deliberate trade-off: unlike .BR --pq , a future weakness in ML-KEM-768 alone is sufficient to break the envelope, because there is no classical KEM to fall back on. When in doubt use .B --pq (hybrid). Keys are generated with .BR "vaptvupt keygen --pq-only" ; the private and public key files (magic .BR ZPQK ) are not interchangeable with hybrid .B --pq keys. On extraction, pass the secret key: .B --pq-only \fIpriv\fR. .TP .B --pq-box \fIpub\fR Enable post-quantum sealed-box encryption via libpqvaptvupt (envelope type 0x05). \fBRequires an optional\fR \fBWITH_SDK=1\fR \fBbuild\fR: the default source-only tree ships no vendored library, so this mode is absent unless you build against libpqvaptvupt yourself. Prefer the native .B --pq or .B --pq-only modes, which need no external library. ML-KEM-768 + X25519 shared secrets are combined through HKDF-SHA256 with a domain-separating info string ("pqvv-seal-v1"); the box carries AES-256-CTR + HMAC-SHA256 Encrypt-then-MAC. The .I pub argument is the recipient's public-key file from .B keygen --box (magic-tagged; public and secret key files are not interchangeable). On extraction, pass the secret key: .B --pq-box \fIpriv\fR. .TP .B --pq-sdk \fIpub\fR Enable post-quantum hybrid encryption via libzuptsdk (envelope type 0x03). \fBRequires an optional\fR \fBWITH_SDK=1\fR \fBbuild\fR and is absent from the default source-only tree; use the native .B --pq instead, which provides the same ML-KEM-768 + X25519 hybrid with no external dependency. Uses ML-KEM-768 + X25519 with HKDF combiner, HPKE binding, and key commitment. The .I pub argument is the recipient's public-key file generated by .BR "vaptvupt keygen --sdk" . .TP .B -c \fItext\fR ", " --comment " " \fItext\fR Embed an encrypted UTF-8 comment in the archive (up to 4096 bytes). The comment is bound to the archive's frame-preface AAD; tampering is detected at extract time. .TP .B --comment-file \fIpath\fR Read the comment from a file rather than the command line. .TP .B -b \fIsize\fR ", " --block-size " " \fIsize\fR Compression block size. Default 4 MiB. Smaller blocks improve random-access decode but lose some ratio. .SH EXTRACT, LIST, TEST OPTIONS .TP .BR -o " " \fIdir\fR ", " --output " " \fIdir\fR Extract into .IR dir (created if it doesn't exist). Default: current directory. .TP .B --no-mtime Do not restore archived modification times; use current time instead. .TP .B --strip-components \fIN\fR Strip .I N leading path components from each entry, like .BR tar 's flag of the same name. .SH POST-QUANTUM ENCRYPTION .B vaptvupt offers two native post-quantum modes, both built entirely from the in-tree crypto (no external library): .RS .IP "\fB--pq\fR (hybrid, recommended)" 4 A hybrid KEM combining ML-KEM-768 (FIPS 203) with X25519 (RFC 7748). The archive key is derived as: .RS .nf ss_pq = ML-KEM-768.decaps(sk_pq, ct_pq) ss_ec = X25519(sk_ec, pk_ec_peer) session = HKDF-SHA256(ss_pq || ss_ec, info = "vaptvupt-pq-sdk-v1", salt = archive_uuid) .fi .RE The hybrid combiner means the session key is at least as strong as the strongest of {ML-KEM-768, X25519}: an attacker must break \fBboth\fR to recover the key. This is the default recommendation and the right choice for almost every user, because it stays secure even if one primitive is later found weak. .IP "\fB--pq-only\fR (full / pure PQ)" 4 ML-KEM-768 as the \fIsole\fR key-establishment mechanism, with no classical component. The archive key is derived as: .RS .nf (ss_pq, ct_pq) = ML-KEM-768.encaps(pk_pq) archive_key = SHA3-512(ss_pq || ct_pq || "ZUPT-PQ-ONLY-v1") .fi .RE Use this only when a compliance posture requires a single NIST-standardised PQ primitive with no classical KEM in the envelope (for example CNSA 2.0-style "PQ-only" requirements). The deliberate trade-off is that the envelope has \fBno hybrid safety net\fR: a future cryptanalytic break of ML-KEM-768 alone breaks the archive, whereas under .B --pq the attacker would still have to break X25519 as well. Unless a policy forbids the classical component, prefer .BR --pq . .RE .PP Both modes carry the same authenticated envelope as password mode: per-block AES-256-CTR with a fresh random 128-bit nonce, HMAC-SHA256 Encrypt-then-MAC, and ML-KEM Fujisaki-Okamoto implicit rejection, so a wrong or tampered ciphertext is rejected rather than yielding garbage. .PP .B Key commitment: the ciphertext is bound to the exact public key it was encrypted to via an HPKE-style derivation. An attacker cannot present a different public key that decrypts to the same plaintext (this defeats the "partitioning" attack class). .PP .B Implementation notes: the ML-KEM-768 implementation is vendored from a clean reference and verified against the FIPS 203 KAT vectors. The X25519 implementation uses 4\(mu64-bit field arithmetic with Jasmin-verified constant-time field operations on x86_64. On other architectures the same routines run in pure C, also constant-time by construction. .SH SECURITY .SS Threat model What .B vaptvupt .B protects against: .RS .IP \(bu 2 Confidentiality of archived data at rest (AES-256-CTR with HMAC-SHA256 EtM, or AEAD via libzuptsdk on the .B --pq-sdk path). .IP \(bu 2 End-to-end byte-level tamper detection on encrypted archives. The F-09 byte-sweep regression (1827 positions on a representative archive, 2000 trials, every run) shows zero silent-accept positions. .IP \(bu 2 Wrong-password and tampered-archive indistinguishability at the user-visible message layer (F-11). The default error wording is identical for both cases; only .B --verbose prints the distinguishing detail. This closes the "verbal probe-oracle" attack class where the error string leaked which check failed first. .IP \(bu 2 Post-quantum forward secrecy on archives encrypted with .B --pq-sdk (assuming ML-KEM-768 holds against future quantum attack). .IP \(bu 2 Archive-header and footer authentication via a 32-byte HMAC-SHA256 trailer (F-08). Tampering with the file count, comment offset, or timestamp is detected at archive open time. .IP \(bu 2 Path-traversal at extract time. Entries with absolute paths or .B .. components are refused or stripped. .RE What it does .B NOT protect against: .RS .IP \(bu 2 Compromise of the endpoint that creates or restores the archive. If the host is compromised, the password, key file, or plaintext is accessible. .IP \(bu 2 Compromise of the key file or password. Key custody is the user's responsibility. .IP \(bu 2 A weak password. Argon2id with default parameters needs ~256 MiB and ~1 s to derive a key on commodity hardware; a 4-character password is still trivially crackable. .IP \(bu 2 Metadata leakage. File names, sizes, and modification times are encrypted, but the archive's total size and the count of compressed blocks are visible to an observer. .IP \(bu 2 Side channels on the compression layer (CRIME/BREACH-style). If the same archive contains both attacker-controlled and secret data and the attacker can observe the compressed size, length-based oracles may be possible. .IP \(bu 2 Denial-of-service via malformed input on the decoder. The decoder rejects malformed input cleanly (no crashes in the fuzz harness), but a very large compressed input can still consume CPU and memory proportional to its size. .RE .SS Cryptographic primitives .TS tab(|); l l. SHA-256 | FIPS 180-4 SHA-3 / SHAKE | FIPS 202 ML-KEM-768 | FIPS 203 AES-256-CTR | NIST SP 800-38A HMAC-SHA256 | RFC 2104 / FIPS 198-1 X25519 | RFC 7748 HKDF-SHA256 | RFC 5869 PBKDF2-SHA256 | RFC 8018 Argon2id | RFC 9106 XXH64 | non-cryptographic; used only inside the AEAD envelope .TE .SS Constant-time guarantees All secret-dependent comparisons and table lookups in the cryptographic core are constant-time. On x86_64 the hot paths (HMAC equality compare, ML-KEM Fujisaki-Okamoto implicit rejection) are implemented in Jasmin and assembled with .BR jasminc (1). On other architectures the same routines run in portable C; the constant-time property is preserved by source-level construction. .SH FILES .TP .I ~/.config/vaptvupt/ Per-user configuration directory (reserved; not used in v3.0.0). .TP .I /etc/vaptvupt/ System-wide configuration directory (reserved; not used in v3.0.0). .TP .I /usr/share/bash-completion/completions/vaptvupt Bash completion (and the symlinked legacy .IR /usr/share/bash-completion/completions/zupt ). .TP .I /usr/share/zsh/site-functions/_vaptvupt zsh completion. .TP .I /usr/share/fish/vendor_completions.d/vaptvupt.fish fish completion. .SH ENVIRONMENT .TP .B VAPTVUPT_BIN Override the binary path used by the GUI front-end. Legacy .B ZUPT_BIN is also honoured. .TP .B VAPTVUPT_DEBUG If set to any non-empty value, the GUI front-end prints its binary-discovery log to stderr. .SH EXIT STATUS .TP .B 0 Success. .TP .B 1 General error (bad arguments, file not found, etc.). .TP .B 2 Authentication failed. Wrong password, wrong key file, or the archive has been tampered with. Use .B --verbose to see the distinguishing detail (subject to F-11's threat model: detailed messages may leak which failure cause fired first). .TP .B 3 Archive-format error (wrong magic bytes, unsupported format version, corrupted header). .TP .B 4 I/O error (disk full, permission denied, network failure). .TP .B 5 Compressed-data integrity error (per-block HMAC mismatch detected mid-stream). .SH PERFORMANCE Numbers below are from the v3.0.0 release benchmark (May 2026), run on an Intel Xeon @ 2.8 GHz with the codec compiled with the distribution's default optimisation level. .TS tab(|); l l l l l. \fBFixture\fR | \fBTool\fR | \fBRatio\fR | \fBEnc MB/s\fR | \fBDec MB/s\fR text 8 MB | vv-9 | 34.6% | 5.4 | 219 text 8 MB | gzip-9 | 30.9% | 6.2 | 137 text 8 MB | zstd-3 | 31.6% | 137 | 427 text 8 MB | zstd-19 | 25.5% | 1.6 | 384 source 670 KB | vv-9 | 25.1% | 11.4 | 128 source 670 KB | gzip-9 | 23.2% | 10.0 | 107 source 670 KB | zstd-3 | 24.1% | 91 | 160 binary 2.4 MB | vv-9 | 44.7% | 7.7 | 153 binary 2.4 MB | gzip-9 | 52.0% | 12.6 | 109 binary 2.4 MB | zstd-3 | 77.3% | 164 | 382 binary 2.4 MB | zstd-19 | 48.0% | 5.8 | 229 random 5 MB | vv-9 | 100.0% | 11.1 | 477 random 5 MB | zstd-3 | 100.0% | 397 | 681 .TE .PP Honest reading of these numbers: .RS .IP \(bu 2 On binary-structured data (game saves, mmap'd structures, struct arrays), .B vaptvupt beats zstd-3 by a wide margin on ratio (44.7% vs 77.3%) at the cost of being ~20\(mu slower to encode. For write-once / restore-often workloads this is the right trade. .IP \(bu 2 On text and source, zstd-19 beats .B vaptvupt on ratio. The fundamental codec difference is that zstd's reference encoder has had years of compiler-engineering attention that .B vaptvupt has not. .IP \(bu 2 Encode throughput is .BR vaptvupt 's weak axis. If encode latency matters more than ratio, use .B -l 1 or .BR -l 2 . .IP \(bu 2 On random / already-compressed data, all codecs hit the incompressibility wall; comparing encode/decode throughput in that regime is mostly measuring memcpy speed plus framing overhead. .RE .SH EXAMPLES .PP Compress with default settings (PBKDF2-SHA256 password, VaptVupt level 7, multi-threaded): .RS .nf $ vaptvupt compress -p secret backup.zupt ~/Documents .fi .RE Compress with post-quantum hybrid encryption to a published public key: .RS .nf $ vaptvupt keygen --sdk -o ~/.config/vaptvupt-mykey $ vaptvupt keygen -o ~/.config/vaptvupt-mykey $ vaptvupt keygen --pub -o mykey.pub -k ~/.config/vaptvupt-mykey $ vaptvupt compress --pq mykey.pub backup.zupt ~/Documents .fi .RE Compress with full (pure) post-quantum encryption — ML-KEM-768 only, no classical component (compliance postures that mandate a single PQ primitive): .RS .nf $ vaptvupt keygen --pq-only -o pqkey $ vaptvupt keygen --pub --pq-only -o pqkey.pub -k pqkey $ vaptvupt compress --pq-only pqkey.pub backup.zupt ~/Documents $ vaptvupt extract --pq-only pqkey -o restored backup.zupt .fi .RE Backup a block device, sparse-aware: .RS .nf $ sudo vaptvupt disk backup -p secret system.zupt /dev/nvme0n1p2 .fi .RE Verify an archive without restoring: .RS .nf $ vaptvupt test -p secret backup.zupt .fi .RE Print archive metadata without supplying a password: .RS .nf $ vaptvupt info backup.zupt .fi .RE Compare compression levels: .RS .nf $ vaptvupt bench ~/Downloads/big-dataset.bin .fi .RE Run the GUI from a desktop session where /usr/bin isn't on PATH (the bug fixed in v3.0.0): .RS .nf $ VAPTVUPT_DEBUG=1 vaptvupt-gui 2> /tmp/discovery.log .fi .RE .SH STANDARDS ISO C11; POSIX.1-2017 for I/O and threading. The cryptographic primitives implement the specifications listed in .BR SECURITY above. The on-disk archive format is documented in .B FORMAT.md in the source distribution. .SH AUTHORS Cristian Cezar Moisés — primary author and maintainer. .SH BUGS Report bugs at https://git.securityops.co/cristiancmoises/vaptvupt/issues or by email to . .SH LICENSE AGPL-3.0-or-later for the application; GPL-3.0-or-later for the embedded VaptVupt codec. Dual-licensed: a commercial licence is available from . .SH SEE ALSO .BR vaptvupt-gui (1), .BR zstd (1), .BR gzip (1), .BR xz (1), .BR tar (1), .BR cryptsetup (8), .BR jasminc (1). .PP Project home: https://git.securityops.co/cristiancmoises/vaptvupt .br Threat model: see .B THREAT_MODEL.md in the source distribution. .br Archive format spec: see .B FORMAT.md in the source distribution.