1use clap::{Parser, Subcommand};
2use std::path::PathBuf;
3
4#[derive(Parser)]
6#[command(name = "hexz", version, about, long_about = None)]
7#[command(disable_help_flag = true)] #[command(styles = get_styles())]
9pub struct Cli {
10 #[arg(short, long, action = clap::ArgAction::SetTrue)]
11 pub help: bool,
12
13 #[command(subcommand)]
14 pub command: Option<Commands>,
15}
16
17fn get_styles() -> clap::builder::Styles {
18 use clap::builder::styling::{AnsiColor, Effects, Styles};
19 Styles::styled()
20 .header(AnsiColor::Yellow.on_default() | Effects::BOLD)
21 .usage(AnsiColor::Green.on_default() | Effects::BOLD)
22 .literal(AnsiColor::Cyan.on_default() | Effects::BOLD)
23 .placeholder(AnsiColor::Cyan.on_default())
24}
25
26#[derive(Subcommand)]
28pub enum Commands {
29 #[command(display_order = 1)]
34 #[command(
35 long_about = "Creates a highly compressed, encrypted, and deduplicated archive from a disk image or memory dump.\n\nIt uses Content-Defined Chunking (CDC) to ensure that only changed weights are stored when archiving multiple versions of a model. This is the primary way to ingest data into Hexz."
36 )]
37 #[command(after_help = "hexz pack model.hxz --disk ./model.bin --compression zstd --cdc")]
38 Pack {
39 output: PathBuf,
41
42 #[arg(long)]
44 disk: Option<PathBuf>,
45
46 #[arg(long)]
48 memory: Option<PathBuf>,
49
50 #[arg(long, default_value = "lz4")]
52 compression: String,
53
54 #[arg(long)]
56 encrypt: bool,
57
58 #[arg(long)]
60 train_dict: bool,
61
62 #[arg(long, default_value_t = 65536, value_parser = clap::value_parser!(u32).range(1..))]
64 block_size: u32,
65
66 #[arg(long)]
68 cdc: bool,
69
70 #[arg(long, default_value_t = 16384, value_parser = clap::value_parser!(u32).range(1..))]
72 min_chunk: u32,
73
74 #[arg(long, default_value_t = 65536, value_parser = clap::value_parser!(u32).range(1..))]
76 avg_chunk: u32,
77
78 #[arg(long, default_value_t = 131072, value_parser = clap::value_parser!(u32).range(1..))]
80 max_chunk: u32,
81
82 #[arg(long, short)]
84 silent: bool,
85 },
86
87 #[command(display_order = 2)]
89 #[command(
90 long_about = "Reads the header and index of a Hexz archive without decompressing the full body.\n\nUse this to verify archive integrity, check compression ratios, or view metadata about the stored snapshot."
91 )]
92 #[command(after_help = "hexz inspect ./model.hxz --json")]
93 Inspect {
94 snap: PathBuf,
96
97 #[arg(long)]
99 json: bool,
100 },
101
102 #[command(display_order = 3)]
104 #[command(
105 long_about = "Compares the BLAKE3 block hashes of two Hexz archives.\n\nReports how much data is shared between them, unique to each, and the storage savings achieved through deduplication. Useful for understanding how much a fine-tuned checkpoint differs from its base."
106 )]
107 #[command(after_help = "hexz diff base.hxz finetuned.hxz")]
108 Diff {
109 a: PathBuf,
111
112 b: PathBuf,
114 },
115
116 #[command(display_order = 4)]
118 #[command(
119 long_about = "Scans a directory for .hxz archives and renders their parent-child relationships as a tree.\n\nParent links are read from each archive's header. Archives whose declared parent lives outside the scanned directory are annotated as external."
120 )]
121 #[command(after_help = "hexz ls ./checkpoints/")]
122 Ls {
123 dir: PathBuf,
125 },
126
127 #[cfg(feature = "diagnostics")]
129 #[command(display_order = 30)]
130 #[command(
131 long_about = "Analyzes the overlay file created by a FUSE read-write mount.\n\nShows which 4 KiB blocks were written during the session and the total amount of changed data."
132 )]
133 #[command(after_help = "hexz overlay vm-state.overlay --blocks")]
134 Overlay {
135 overlay: PathBuf,
137
138 #[arg(long)]
140 blocks: bool,
141
142 #[arg(long)]
144 files: bool,
145 },
146
147 #[command(display_order = 4)]
149 #[command(
150 long_about = "Recursively builds a Hexz archive from a local directory structure.\n\nUnlike 'pack' which handles raw disk images, 'build' is designed for file-system level packing."
151 )]
152 #[command(after_help = "hexz build ./checkpoints/ archive.hxz")]
153 Build {
154 source: PathBuf,
156
157 output: PathBuf,
159
160 #[arg(long)]
162 memory: Option<PathBuf>,
163
164 #[arg(long)]
166 profile: Option<String>,
167
168 #[arg(long)]
170 encrypt: bool,
171
172 #[arg(long)]
174 cdc: bool,
175 },
176
177 #[cfg(feature = "diagnostics")]
179 #[command(display_order = 5)]
180 #[command(
181 long_about = "Performs a deep structural analysis of the archive format.\n\nUsed primarily for debugging corruption issues or optimizing block alignment strategies."
182 )]
183 #[command(after_help = "hexz analyze ./corrupt_image.hxz")]
184 Analyze {
185 input: PathBuf,
187 },
188
189 #[command(display_order = 6)]
191 #[command(
192 long_about = "Ingests external formats like tar, HDF5, or WebDataset into a Hexz snapshot.\n\nThis allows legacy datasets to benefit from Hexz's random access and deduplication features."
193 )]
194 #[command(after_help = "hexz convert tar data.tar data.hxz")]
195 Convert {
196 format: String,
198
199 input: PathBuf,
201
202 output: PathBuf,
204
205 #[arg(long, default_value = "lz4")]
207 compression: String,
208
209 #[arg(long, default_value_t = 65536)]
211 block_size: u32,
212
213 #[arg(long)]
215 profile: Option<String>,
216
217 #[arg(long, short)]
219 silent: bool,
220 },
221
222 #[cfg(feature = "fuse")]
227 #[command(display_order = 10)]
228 #[command(
229 long_about = "Boots a transient Virtual Machine directly from a Hexz snapshot.\n\nThe VM uses a copy-on-write overlay, meaning the original snapshot remains immutable. Changes are lost on shutdown unless --persist is used."
230 )]
231 #[command(after_help = "hexz boot ubuntu.hxz --ram 4G --no-graphics")]
232 Boot {
233 snap: String,
235
236 #[arg(long)]
238 ram: Option<String>,
239
240 #[arg(long)]
242 no_kvm: bool,
243
244 #[arg(long, default_value = "user")]
246 network: String,
247
248 #[arg(long, default_value = "qemu")]
250 backend: String,
251
252 #[arg(long)]
254 persist: Option<PathBuf>,
255
256 #[arg(long)]
258 qmp_socket: Option<PathBuf>,
259
260 #[arg(long)]
262 no_graphics: bool,
263
264 #[arg(long)]
266 vnc: bool,
267 },
268
269 #[cfg(feature = "fuse")]
271 #[command(display_order = 11)]
272 #[command(
273 long_about = "Runs an OS installer from an ISO and captures the result into a new Hexz snapshot.\n\nThis automates the process of creating base images for VMs."
274 )]
275 #[command(after_help = "hexz install alpine.iso alpine-base.hxz")]
276 Install {
277 iso: PathBuf,
279
280 output: PathBuf,
282
283 #[arg(long, default_value = "10G")]
285 primary_size: String,
286
287 #[arg(long, default_value = "4G")]
289 ram: String,
290
291 #[arg(long)]
293 no_graphics: bool,
294
295 #[arg(long)]
297 vnc: bool,
298
299 #[arg(long)]
301 cdc: bool,
302 },
303
304 #[cfg(unix)]
306 #[command(display_order = 12)]
307 #[command(
308 long_about = "Triggers a live snapshot of a running VM via the QMP socket.\n\nThis allows for capturing the state of a running system without shutting it down."
309 )]
310 #[command(after_help = "hexz snap /tmp/qmp.sock base.hxz overlay.bin live.hxz")]
311 Snap {
312 socket: PathBuf,
314
315 base: PathBuf,
317
318 overlay: PathBuf,
320
321 output: PathBuf,
323 },
324
325 #[command(display_order = 13)]
327 #[command(
328 long_about = "Finalizes a writable overlay into a new immutable snapshot.\n\nSupports 'thin' snapshots which only store the deltas referencing the parent, ideal for iterative model fine-tuning."
329 )]
330 #[command(after_help = "hexz commit base.hxz overlay.bin new_model.hxz --thin")]
331 Commit {
332 base: PathBuf,
334
335 overlay: PathBuf,
337
338 output: PathBuf,
340
341 #[arg(long, default_value = "lz4")]
343 compression: String,
344
345 #[arg(long, default_value_t = 65536, value_parser = clap::value_parser!(u32).range(1..))]
347 block_size: u32,
348
349 #[arg(long)]
351 keep_overlay: bool,
352
353 #[arg(long)]
355 flatten: bool,
356
357 #[arg(long)]
359 message: Option<String>,
360
361 #[arg(long)]
363 thin: bool,
364 },
365
366 #[cfg(feature = "fuse")]
368 #[command(display_order = 14)]
369 #[command(
370 long_about = "Mounts a Hexz snapshot as a FUSE filesystem.\n\nAllows standard tools to read data from the snapshot as if it were a normal directory."
371 )]
372 #[command(after_help = "hexz mount model.hxz /mnt/model --rw")]
373 Mount {
374 snap: String,
376
377 mountpoint: PathBuf,
379
380 #[arg(long)]
382 overlay: Option<PathBuf>,
383
384 #[arg(short, long)]
386 daemon: bool,
387
388 #[arg(long)]
390 rw: bool,
391
392 #[arg(long)]
394 cache_size: Option<String>,
395
396 #[arg(long, default_value_t = 1000)]
398 uid: u32,
399
400 #[arg(long, default_value_t = 1000)]
402 gid: u32,
403
404 #[arg(long)]
406 nbd: bool,
407 },
408
409 #[cfg(feature = "fuse")]
411 #[command(display_order = 15)]
412 #[command(long_about = "Unmounts a previously mounted Hexz filesystem.")]
413 #[command(after_help = "hexz unmount /mnt/model")]
414 Unmount {
415 mountpoint: PathBuf,
417 },
418
419 #[cfg(feature = "diagnostics")]
424 #[command(display_order = 20)]
425 #[command(
426 long_about = "Checks the system for compatibility with Hexz features (FUSE, KVM, AVX2, etc.)."
427 )]
428 #[command(after_help = "hexz doctor")]
429 Doctor,
430
431 #[cfg(feature = "diagnostics")]
433 #[command(display_order = 21)]
434 #[command(
435 long_about = "Runs read/write benchmarks on a specific archive to test throughput and latency."
436 )]
437 #[command(after_help = "hexz bench model.hxz --threads 4")]
438 Bench {
439 image: PathBuf,
441
442 #[arg(long)]
444 block_size: Option<u32>,
445
446 #[arg(long)]
448 duration: Option<u64>,
449
450 #[arg(long)]
452 threads: Option<usize>,
453 },
454
455 #[cfg(feature = "server")]
457 #[command(display_order = 22)]
458 #[command(
459 long_about = "Starts an HTTP/S3 compatible server to stream the snapshot over the network.\n\nClients can fetch specific byte ranges efficiently."
460 )]
461 #[command(after_help = "hexz serve model.hxz --port 8080")]
462 Serve {
463 snap: String,
465
466 #[arg(long, default_value_t = 8080)]
468 port: u16,
469
470 #[arg(short, long)]
472 daemon: bool,
473
474 #[arg(long)]
476 nbd: bool,
477
478 #[arg(long)]
480 s3: bool,
481 },
482
483 #[cfg(feature = "signing")]
485 #[command(display_order = 23)]
486 #[command(long_about = "Generates an Ed25519 keypair for signing Hexz archives.")]
487 #[command(after_help = "hexz keygen --output-dir ~/.hexz/keys")]
488 Keygen {
489 #[arg(short, long)]
491 output_dir: Option<PathBuf>,
492 },
493
494 #[cfg(feature = "signing")]
496 #[command(display_order = 24)]
497 #[command(long_about = "Cryptographically signs a Hexz archive using a private key.")]
498 #[command(after_help = "hexz sign private.pem model.hxz")]
499 Sign {
500 key: PathBuf,
502
503 image: PathBuf,
505 },
506
507 #[cfg(feature = "signing")]
509 #[command(display_order = 25)]
510 #[command(
511 long_about = "Verifies the cryptographic signature of an archive using a public key."
512 )]
513 #[command(after_help = "hexz verify public.pem model.hxz")]
514 Verify {
515 key: PathBuf,
517
518 image: PathBuf,
520 },
521}