The zx logo: a database marked ZX held in a clamp

Every archive is a database.

zx: versioned, searchable and verifiable archives, in pure Dart.

zx keeps every version of your files: updates are appended, earlier states stay readable by date, and identical data is stored once. Any file is found in microseconds by SHA-256 or by similarity, and a SQL database lives inside the archive next to the files. Versions can be signed with NOSTR keys, so anyone can verify who wrote them and that nothing changed since, and a README inside the archive describes its contents. Compression goes well beyond the usual formats when there is time for it: built on the context mixing models of paq8px and cmix, zcm level 9 makes a 6.8 MB mixed corpus 33% smaller than xz -9e and 7z PPMd (1.71 MB against 2.57 and 2.55 MB), with 39 to 40% saved on source code and executables and 43 to 46% on photographs and audio, at a few KB/s; level 1 runs at 1.1 MB/s. And it still reads and writes every 7z, zip, rar and tar you have. Pure Dart: a library, a command line and a file explorer for desktop and Android.

Version 0.5.0 · BSD 3-clause · Linux, Windows, macOS, Android
SourceDownloadOnline.zx specificationMeasurements

Overview

zx also reads and writes the established formats (7z, zip, RAR5, tar and the compressed tars, zpaq) and reads older RAR versions, disk images and firmware. Its LZMA, LZMA2 and PPMd coders are ported from the public domain LZMA SDK and produce output byte-identical to the reference implementation. The library is pure Dart, with no FFI and no native libraries, so the same code runs as the command line program, as a library in Dart and Flutter applications, and in the desktop and Android app.

The .zx format in detail:

The test suite compares results against the reference tools wherever they exist: 7z, xz, zip and unzip, tar, gzip, bzip2, rar and unrar, arj, lhasa, zpaq, zpaqfranz, sqlite3, mksquashfs, xorriso, the mkfs tools and paq8px.

Install

zx consists of two programs: zx, the command line, and zx-gui, the desktop application. On Android the application is distributed as zx.apk. To inspect an archive without installing anything, zx online runs the same library in the browser, read only.

Download

Linuxx86-64 zx-linux-x64
ARM64 zx-linux-arm64
Windowsx64 zx-windows-x64.exe
ARM64 zx-windows-arm64.exe
macOSApple silicon zx-macos-arm64
Intel zx-macos-x64
AndroidThe app zx.apk

Each file is a single executable with no runtime to install. Rename it to zx (zx.exe on Windows) and place it on the PATH; on Linux and macOS make it executable with chmod +x zx. The macOS builds are not notarized: remove the quarantine flag with xattr -d com.apple.quarantine zx. All builds: releases.

Other ways to install

TargetMethod
Any platform with the Dart SDKdart pub global activate --source git https://github.com/x1watt/zx
Linux desktopThe Debian package (tool/build_deb.sh) or tool/install_linux.sh; both install zx and zx-gui.
Android, from sourcetool/build_apk.sh builds zx.apk (--split: one APK per ABI).

Command line

Usage: zx <command> [<switches>...] <archive> [<files>...]. zx without arguments prints every command and switch.

CommandFunction
a, uAdd files; update changed and new files
x, eExtract with and without paths
l, tList; test integrity
d, rnDelete; rename inside the archive
h, i, bHashes of files; supported formats; benchmark
sqlSQL on the database of a .zx archive, or an interactive shell
sealShow, check and sign the seals of a .zx archive
readmePrint or check the README of an archive or of a folder in it
SwitchFunction
-snestList, test or extract through nested archives and disk images as one tree
-mversion=N, -mversion=DATERead a .zx or zpaq archive as it was at a generation or date
-mtimeline=PATHEvery version of one file, with dates
-mcompact[=N]Rewrite a .zx archive keeping the last N generations
-m0=zcm:auto -mtime=T -mmem=MThe strongest zcm level that fits a time and memory budget
-mvdir=DIR[:SIZE|:full]Write volumes to several folders or disks in order
-msign=KEYSign the new generation with a NOSTR key
zx a -mx9 -psecret -mhe backup.7z ~/docs        # 7z, LZMA2 level 9, encrypted file names
zx x release.tar.xz -oout                       # tar.xz extracted in one pass
zx a -v700m -mrr=5% big.rar data/               # RAR5 volumes with a recovery record
zx a backup.zx ~/docs                           # first generation; later runs store changes only
zx x backup.zx -mversion=2026-09-01 -oold       # the archive as of a date
zx x -snest -snld20 firmware.pak -oout          # the root file system inside a firmware image
zx sql backup.zx "SELECT path, size FROM zx_files ORDER BY size DESC LIMIT 10"

Every switch: docs/cli.md.

Formats

Formats are detected by content, not by file name.

AccessFormats
Read and writezx, 7z, zip and jar (ZipCrypto, WinZip AES, Zip64), RAR5, tar, tar.gz, tar.bz2, tar.xz, gzip, bzip2, xz, lzma, zpaq, ARJ, LHA
ReadRAR 1.5 to 7, ISO 9660, UDF, MBR, GPT, FAT, ext2/3/4, SquashFS, cramfs, JFFS2, UBI, UBIFS, cpio, U-Boot images, device trees, Reolink firmware, split volumes

The .zx format

A .zx file is a header, a sequence of blocks, and an index with a footer; each update appends further blocks, a new index and a new footer. Every block names the codec chain that produced it, and the file states the oldest version of zx that can decode it, so new codecs do not require a new format and an older reader refuses a file it can not read instead of misreading it. The complete specification is docs/zx-format.md.

Generations
Earlier bytes are never rewritten. A reader opens the last valid footer, so an update interrupted by a crash leaves the previous generation intact. Any generation can be listed or extracted; -mcompact reclaims space.
Deduplication
Content-defined chunks (64 KiB average) are stored once across files and generations. A second version of a source tree added to an archive of the first costs 0.31 MB instead of 4.76 MB; the chunk index of a 100 GiB archive needs 13 MB of memory.
Parallel coding
Blocks are coded by worker isolates within a memory budget, with output identical to the single-threaded coder: 2.9 times faster on 4 threads than on one (LZMA2 level 5, 40 MB of C sources in 2 MiB blocks).
Content hashes
SHA-256 per file in a sorted table, verified on extraction and test, and a TLSH digest per file for similarity search.
Encryption
AES-256-CTR with HMAC-SHA-256 per block (encrypt then MAC), keys from scrypt; file names and sizes are encrypted by default.
Volumes
Volumes of different sizes over several folders or disks, with budgets or until a disk is full; earlier volumes are not needed to append.
READMEs
A README.md in the archive, or in one of its folders, is shown by the app as the description of that folder. Images and linked files must be entries of the archive; links to external sites are permitted, fetching external resources is not (zx readme -check reports violations).

Compression: zcm

zcm is a family of context mixing compressors built from the models of zpaq, paq8px and cmix: data type detection per block (text, x86 code, images including headerless raw ones, audio), an English dictionary transform, word, match, sparse, image and audio models, two-layer mixers with SSE stages, PPMd and DMC predictors gated by measured gain, and cmix's LSTM byte mixer at the top level. The arithmetic is deterministic, so every machine decodes the same bytes. -m0=zcm:auto selects the strongest level that fits a given time and memory budget on the machine at hand.

CompressorOutput bytes
7z, PPMd384,433
xz -9e381,240
zpaq, method 5357,160
zcm level 1366,552
zcm level 3350,935
zcm level 5341,206
zcm level 9331,324
paq8px -8 (C++, 2.4 GB of memory)330,801

Input: a 700,986 byte corpus of text, source code, an x86 binary and a firmware slice. Method and further corpora: docs/performance.md.

File (6,758,876 byte corpus)xz -9e7z PPMd -mx9zcm level 9Smaller than xz
Book, English text223,640180,645148,83033%
C++ source146,500141,44288,71439%
x86-64 executable478,896410,356289,48140%
Firmware558,976609,164527,0576%
Photograph, PPM176,456193,65295,01846%
Photograph, BMP241,872264,029136,64444%
Grayscale image56,36455,47730,70146%
Music, 16-bit stereo WAV668,704679,210379,68343%
Voice, 8-bit WAV22,48017,48415,42431%
Total2,573,8882,551,4591,711,55233%

Measured with zcm stream version 2 at 9 KB/s; the current version 3 was equal or smaller on the small corpus and has not been measured on this one yet. The corpus (a Project Gutenberg book, the paq8px sources, photographs, synthesized audio, a firmware slice) is described in docs/performance.md.

Data typezcm level 9 relative to paq8px -8
English text0.7 to 5% smaller
Source code (C++, Dart)0.5 to 1% smaller
Grayscale image0.05% larger
Color photograph (PPM, BMP)0.45 to 3.45% larger
16-bit stereo audio0.96% larger
x86 binary2.6% larger
LevelSpeed (AOT)MemoryModels
11.2 MB/s40 MiBFastest level; smaller than xz -9e
3200 KB/s30 MiBSmaller than zpaq method 5
585 KB/s35 MiBData type detection, paq8 models
710 KB/s170 MiBpaq8px text, image and audio models
94 to 6 KB/s400 MiBAll models, PPMd and DMC, gain gating
cmix2 to 3 KB/sper budgetLevel 9 with cmix's LSTM byte mixer

Database: zxdb

A .zx archive can hold a database next to its files: SQL tables in the SQLite dialect (cross-checked against sqlite3 by differential fuzzing), key-value stores with expiry, partitioned time series and full-text search. Pages are compressed with the archive's codecs. Each commit is a generation of the archive, so every table can be read as of an earlier commit (AS OF '2026-09-01'). Read-only system tables expose the archive's files, generations and similarity search to SQL.

SELECT path, size FROM zx_files WHERE sha256 = x'9f86d081...';
SELECT path, distance FROM similar('photos/IMG_0042.jpg', 20);
SELECT * FROM zx_files AS OF '2026-09-01';

CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT) WITH (compression = 'max');
CREATE KV STORE settings WITH (ttl = '30d');
CREATE TIMESERIES logs (ts DATETIME, level TEXT, msg TEXT)
  PARTITION BY DAY RETENTION '400d';
Measurementzxdbsqlite3
Point select, 100k rows4.8 us11.7 us
File size, 20k JSON records (2.4 MiB)158 KiB2,680 KiB
File size, 1M log lines17.8 MiB153.9 MiB
Key-value get, cached1.4 us
Key-value put, random keys into a 1M-key tree161k/s
SHA-256 lookup2 us
20 nearest files by TLSH among 1M< 15 ms

The sizes are at compression level max. SQL reference: docs/zxdb-sql.md.

Signed generations

Whoever holds a file can modify it; signing can not prevent that, but it can make any modification detectable. When sealing is enabled, each generation of a .zx archive ends with a seal: a SHA-256 over the stored bytes of the generation and over its index, chained to the seal of the previous generation and signed with a NOSTR key (BIP-340 Schnorr over secp256k1). Because the hashes cover the stored bytes, the seals of an encrypted archive can be verified without its password.

The seal also carries the archive's roles: one admin and any number of maintainers, identified by their npubs and readable by anyone. The admin adds and removes maintainers, decides whether they may sign, and can transfer the admin role to a new key, which must countersign its acceptance. A reader replays the role history offline and checks each generation against the roles in force when it was written; generations signed by a maintainer before their removal remain valid.

zx a -msign=@admin.nsec book.zx chapters/          # new archive; the key becomes the admin
zx seal -sign=@admin.nsec -add=npub1... book.zx     # add a maintainer
zx u -msign=@maintainer.nsec book.zx chapters/     # an update signed by the maintainer
zx seal -full book.zx                              # verify every byte and every signature

A modified byte, a forged generation, a removed seal, a signature by a key that may not sign and an unauthorized change of roles are all reported. Two limits are inherent to any scheme of this kind: the verification establishes which npub signed, not who controls it; and a truncated copy that ends at an earlier, validly sealed generation can only be recognized against an independent record of the latest one. Sealing is optional, can be enabled on an existing archive, and has no effect on archives that do not use it. Keys are passed as a file or through the ZX_NSEC environment variable, not on the command line.

CostMeasured
Write time, LZMA2 level 5, signed relative to unsigned+0.4%
Write time, stored (no compression)+4.4%
Opening and listing a sealed archiveunchanged
Quick check (indexes, signatures, roles)10 ms
Full check of a 41 MB archive0.33 s
One signature; one verification2.3 ms; 1.6 ms

The block workers hash the payloads in parallel, so the cost appears only where the coder is faster than SHA-256. Specification: section 17 of docs/zx-format.md.

Desktop and Android application

zx-gui is a file manager built with Flutter on the same library. It opens archives, nested archives, disk images and firmware as folders: copying out extracts, pasting in adds. For .zx archives it lists the generations, shows the archive's README, provides a view of the embedded database (tables, key-value stores, time series, queries, file metadata, similarity search) and reports the state of the seals. All archive work runs in background isolates.

On Linux it registers file associations and an "Extract to folder" entry for Nautilus and Thunar; on Android it supports the system's storage access, SD and USB volumes, opening from and sharing to other applications. Details: docs/app.md.

zx online is the same application in the browser, read only: the library compiled to WebAssembly runs in a worker. Local files are read in place and never uploaded. An archive given by URL is read with HTTP range requests when the server permits cross-origin access, so listing a large archive transfers its first and last blocks rather than the whole file; otherwise it can be downloaded into browser storage. Requires a browser with WebAssembly garbage collection (Chrome 119, Firefox 120, Safari 18.2).

Archives can be linked by pasting their address after the page's: online/https://example.org/archive.zx, optionally followed by #path=PATH to open a folder or select a file, and #theme=green (or dark, light, orange, eighties) to choose the theme. The Share link button produces the link of the current view.

Credits and license

zx is distributed under the BSD 3-clause license. It is derived from, or follows, the work of Igor Pavlov (7-Zip and the LZMA SDK), Matt Mahoney (zpaq, paq8, lpaq), the paq8px authors, Byron Knoll (cmix), Dmitry Shkarin (PPMd), Jean-loup Gailly and Mark Adler (zlib), Julian Seward (bzip2), Tim Kientzle and the libarchive contributors, Nicholas Waples (rardecode), Simon Howard (lhasa), Franco Corbelli (zpaqfranz), Wei Dai (SHA-256), the TLSH and BLAKE2 authors, and Pieter Wuille, Jonas Nick and Tim Ruffing (BIP-340). Each source file names its origins. 7-Zip is a registered trademark of Igor Pavlov; zx is not affiliated with or endorsed by him.