Skip to main content

hexz_cli/
args.rs

1//! Command-line argument definitions for the Hexz CLI.
2use clap::{Parser, Subcommand};
3use std::path::PathBuf;
4
5/// Hexz - High-performance snapshot and streaming engine
6#[derive(Parser)]
7#[command(name = "hexz", version, about, long_about = None)]
8#[command(disable_help_flag = true)] // We handle help manually
9#[command(styles = get_styles())]
10pub struct Cli {
11    #[arg(short, long, action = clap::ArgAction::SetTrue)]
12    pub help: bool,
13
14    #[command(subcommand)]
15    pub command: Option<Commands>,
16}
17
18fn get_styles() -> clap::builder::Styles {
19    use clap::builder::styling::{AnsiColor, Effects, Styles};
20    Styles::styled()
21        .header(AnsiColor::Yellow.on_default() | Effects::BOLD)
22        .usage(AnsiColor::Green.on_default() | Effects::BOLD)
23        .literal(AnsiColor::Cyan.on_default() | Effects::BOLD)
24        .placeholder(AnsiColor::Cyan.on_default())
25}
26
27/// Top-level command categories
28#[derive(Subcommand)]
29pub enum Commands {
30    // ------------------------------------------------------------------------
31    // Archive Operations
32    // ------------------------------------------------------------------------
33    /// Pack data into a Hexz archive
34    #[command(display_order = 1)]
35    #[command(
36        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."
37    )]
38    #[command(
39        after_help = "hexz pack --disk ./model.bin --output model.hxz --compression zstd --cdc"
40    )]
41    Pack {
42        /// Path to disk image to pack
43        #[arg(long)]
44        disk: Option<PathBuf>,
45
46        /// Path to memory dump to pack
47        #[arg(long)]
48        memory: Option<PathBuf>,
49
50        /// Output archive path (.hxz)
51        #[arg(short, long)]
52        output: PathBuf,
53
54        /// Compression algorithm (lz4, zstd, none)
55        #[arg(long, default_value = "lz4")]
56        compression: String,
57
58        /// Enable encryption
59        #[arg(long)]
60        encrypt: bool,
61
62        /// Train compression dictionary
63        #[arg(long)]
64        train_dict: bool,
65
66        /// Block size in bytes (must be > 0)
67        #[arg(long, default_value_t = 65536, value_parser = clap::value_parser!(u32).range(1..))]
68        block_size: u32,
69
70        /// Enable content-defined chunking (CDC)
71        #[arg(long)]
72        cdc: bool,
73
74        /// Minimum chunk size for CDC
75        #[arg(long, default_value_t = 16384, value_parser = clap::value_parser!(u32).range(1..))]
76        min_chunk: u32,
77
78        /// Average chunk size for CDC
79        #[arg(long, default_value_t = 65536, value_parser = clap::value_parser!(u32).range(1..))]
80        avg_chunk: u32,
81
82        /// Maximum chunk size for CDC
83        #[arg(long, default_value_t = 131072, value_parser = clap::value_parser!(u32).range(1..))]
84        max_chunk: u32,
85
86        /// Suppress all output and progress bars
87        #[arg(long, short)]
88        silent: bool,
89    },
90
91    /// Inspect archive metadata
92    #[command(display_order = 2)]
93    #[command(
94        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."
95    )]
96    #[command(after_help = "hexz info ./model.hxz --json")]
97    Info {
98        /// Path to archive
99        snap: PathBuf,
100
101        /// Output as JSON
102        #[arg(long)]
103        json: bool,
104    },
105
106    /// Show differences in overlay
107    #[cfg(feature = "diagnostics")]
108    #[command(display_order = 3)]
109    #[command(
110        long_about = "Analyzes the differences between a base image and an overlay.\n\nThis is useful for auditing what changed in a fine-tuning run or verifying that a thin snapshot only contains the expected deltas."
111    )]
112    #[command(after_help = "hexz diff --overlay finetuned.hxz --blocks")]
113    Diff {
114        /// Path to overlay
115        overlay: PathBuf,
116
117        /// Show block-level differences
118        #[arg(long)]
119        blocks: bool,
120
121        /// Show file-level differences
122        #[arg(long)]
123        files: bool,
124    },
125
126    /// Build archive from source directory
127    #[command(display_order = 4)]
128    #[command(
129        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."
130    )]
131    #[command(after_help = "hexz build --source ./checkpoints/ --output archive.hxz")]
132    Build {
133        /// Source directory
134        #[arg(long)]
135        source: PathBuf,
136
137        /// Optional memory dump
138        #[arg(long)]
139        memory: Option<PathBuf>,
140
141        /// Output archive path
142        #[arg(short, long)]
143        output: PathBuf,
144
145        /// Build profile
146        #[arg(long)]
147        profile: Option<String>,
148
149        /// Enable encryption
150        #[arg(long)]
151        encrypt: bool,
152
153        /// Enable CDC
154        #[arg(long)]
155        cdc: bool,
156    },
157
158    /// Analyze archive structure
159    #[cfg(feature = "diagnostics")]
160    #[command(display_order = 5)]
161    #[command(
162        long_about = "Performs a deep structural analysis of the archive format.\n\nUsed primarily for debugging corruption issues or optimizing block alignment strategies."
163    )]
164    #[command(after_help = "hexz analyze ./corrupt_image.hxz")]
165    Analyze {
166        /// Archive to analyze
167        input: PathBuf,
168    },
169
170    /// Convert external formats to Hexz snapshot
171    #[command(display_order = 6)]
172    #[command(
173        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."
174    )]
175    #[command(after_help = "hexz convert --format tar --input data.tar --output data.hxz")]
176    Convert {
177        /// Source format (tar, hdf5, webdataset)
178        format: String,
179
180        /// Input file path
181        input: PathBuf,
182
183        /// Output snapshot path (.hxz)
184        output: PathBuf,
185
186        /// Compression algorithm (lz4, zstd)
187        #[arg(long, default_value = "lz4")]
188        compression: String,
189
190        /// Block size in bytes
191        #[arg(long, default_value_t = 65536)]
192        block_size: u32,
193
194        /// Build profile (ml, eda, embedded, generic, archival)
195        #[arg(long)]
196        profile: Option<String>,
197
198        /// Suppress output
199        #[arg(long, short)]
200        silent: bool,
201    },
202
203    // ------------------------------------------------------------------------
204    // Virtual Machine Operations
205    // ------------------------------------------------------------------------
206    /// Boot a virtual machine from snapshot
207    #[cfg(feature = "fuse")]
208    #[command(display_order = 10)]
209    #[command(
210        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."
211    )]
212    #[command(after_help = "hexz boot --snap ubuntu.hxz --ram 4G --no-graphics")]
213    Boot {
214        /// Snapshot to boot from
215        snap: String,
216
217        /// RAM size (e.g., "4G")
218        #[arg(long)]
219        ram: Option<String>,
220
221        /// Disable KVM acceleration
222        #[arg(long)]
223        no_kvm: bool,
224
225        /// Network mode (user, bridge, none)
226        #[arg(long, default_value = "user")]
227        network: String,
228
229        /// Hypervisor backend (qemu, firecracker)
230        #[arg(long, default_value = "qemu")]
231        backend: String,
232
233        /// Persistent overlay path
234        #[arg(long)]
235        persist: Option<PathBuf>,
236
237        /// QMP socket path for control
238        #[arg(long)]
239        qmp_socket: Option<PathBuf>,
240
241        /// Disable graphics (headless mode)
242        #[arg(long)]
243        no_graphics: bool,
244
245        /// Enable VNC server
246        #[arg(long)]
247        vnc: bool,
248    },
249
250    /// Install OS from ISO to snapshot
251    #[cfg(feature = "fuse")]
252    #[command(display_order = 11)]
253    #[command(
254        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."
255    )]
256    #[command(after_help = "hexz install --iso alpine.iso --output alpine-base.hxz")]
257    Install {
258        /// Path to ISO image
259        #[arg(long)]
260        iso: PathBuf,
261
262        /// Virtual disk size (e.g., "10G")
263        #[arg(long, default_value = "10G")]
264        primary_size: String,
265
266        /// RAM size (e.g., "4G")
267        #[arg(long, default_value = "4G")]
268        ram: String,
269
270        /// Output snapshot path
271        #[arg(short, long)]
272        output: PathBuf,
273
274        /// Disable graphics
275        #[arg(long)]
276        no_graphics: bool,
277
278        /// Enable VNC
279        #[arg(long)]
280        vnc: bool,
281
282        /// Enable CDC
283        #[arg(long)]
284        cdc: bool,
285    },
286
287    /// Create snapshot via QMP
288    #[cfg(unix)]
289    #[command(display_order = 12)]
290    #[command(
291        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."
292    )]
293    #[command(after_help = "hexz snap --socket /tmp/qmp.sock --base base.hxz --output live.hxz")]
294    Snap {
295        /// QMP socket path
296        #[arg(long)]
297        socket: PathBuf,
298
299        /// Base snapshot
300        #[arg(long)]
301        base: PathBuf,
302
303        /// Overlay path
304        #[arg(long)]
305        overlay: PathBuf,
306
307        /// Output snapshot
308        #[arg(short, long)]
309        output: PathBuf,
310    },
311
312    /// Commit overlay changes to new snapshot
313    #[command(display_order = 13)]
314    #[command(
315        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."
316    )]
317    #[command(after_help = "hexz commit base.hxz overlay.bin new_model.hxz --thin")]
318    Commit {
319        /// Base snapshot
320        base: PathBuf,
321
322        /// Overlay with changes
323        overlay: PathBuf,
324
325        /// Output snapshot
326        output: PathBuf,
327
328        /// Compression algorithm
329        #[arg(long, default_value = "lz4")]
330        compression: String,
331
332        /// Block size (must be > 0)
333        #[arg(long, default_value_t = 65536, value_parser = clap::value_parser!(u32).range(1..))]
334        block_size: u32,
335
336        /// Keep overlay file after commit
337        #[arg(long)]
338        keep_overlay: bool,
339
340        /// Flatten all layers into single archive
341        #[arg(long)]
342        flatten: bool,
343
344        /// Commit message
345        #[arg(long)]
346        message: Option<String>,
347
348        /// Create thin snapshot (reference base)
349        #[arg(long)]
350        thin: bool,
351    },
352
353    /// Mount snapshot as filesystem
354    #[cfg(feature = "fuse")]
355    #[command(display_order = 14)]
356    #[command(
357        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."
358    )]
359    #[command(after_help = "hexz mount model.hxz /mnt/model --rw")]
360    Mount {
361        /// Snapshot to mount
362        snap: String,
363
364        /// Mount point directory
365        mountpoint: PathBuf,
366
367        /// Overlay for writes
368        #[arg(long)]
369        overlay: Option<PathBuf>,
370
371        /// Run as daemon
372        #[arg(short, long)]
373        daemon: bool,
374
375        /// Enable read-write mode
376        #[arg(long)]
377        rw: bool,
378
379        /// Cache size (e.g., "1G")
380        #[arg(long)]
381        cache_size: Option<String>,
382
383        /// User ID for files
384        #[arg(long, default_value_t = 1000)]
385        uid: u32,
386
387        /// Group ID for files
388        #[arg(long, default_value_t = 1000)]
389        gid: u32,
390
391        /// Export as NBD device
392        #[arg(long)]
393        nbd: bool,
394    },
395
396    /// Unmount filesystem
397    #[cfg(feature = "fuse")]
398    #[command(display_order = 15)]
399    #[command(long_about = "Unmounts a previously mounted Hexz filesystem.")]
400    #[command(after_help = "hexz unmount /mnt/model")]
401    Unmount {
402        /// Mount point to unmount
403        mountpoint: PathBuf,
404    },
405
406    // ------------------------------------------------------------------------
407    // System & Diagnostics
408    // ------------------------------------------------------------------------
409    /// Run system diagnostics
410    #[cfg(feature = "diagnostics")]
411    #[command(display_order = 20)]
412    #[command(
413        long_about = "Checks the system for compatibility with Hexz features (FUSE, KVM, AVX2, etc.)."
414    )]
415    #[command(after_help = "hexz doctor")]
416    Doctor,
417
418    /// Benchmark archive performance
419    #[cfg(feature = "diagnostics")]
420    #[command(display_order = 21)]
421    #[command(
422        long_about = "Runs read/write benchmarks on a specific archive to test throughput and latency."
423    )]
424    #[command(after_help = "hexz bench model.hxz --threads 4")]
425    Bench {
426        /// Archive to benchmark
427        image: PathBuf,
428
429        /// Block size for testing
430        #[arg(long)]
431        block_size: Option<u32>,
432
433        /// Duration in seconds
434        #[arg(long)]
435        duration: Option<u64>,
436
437        /// Number of threads
438        #[arg(long)]
439        threads: Option<usize>,
440    },
441
442    /// Serve archive over network
443    #[cfg(feature = "server")]
444    #[command(display_order = 22)]
445    #[command(
446        long_about = "Starts an HTTP/S3 compatible server to stream the snapshot over the network.\n\nClients can fetch specific byte ranges efficiently."
447    )]
448    #[command(after_help = "hexz serve model.hxz --port 8080")]
449    Serve {
450        /// Snapshot to serve
451        snap: String,
452
453        /// Server port
454        #[arg(long, default_value_t = 8080)]
455        port: u16,
456
457        /// Run as daemon
458        #[arg(short, long)]
459        daemon: bool,
460
461        /// Enable NBD protocol
462        #[arg(long)]
463        nbd: bool,
464
465        /// Enable S3-compatible API
466        #[arg(long)]
467        s3: bool,
468    },
469
470    /// Generate signing keys
471    #[cfg(feature = "signing")]
472    #[command(display_order = 23)]
473    #[command(long_about = "Generates an Ed25519 keypair for signing Hexz archives.")]
474    #[command(after_help = "hexz keygen --output-dir ~/.hexz/keys")]
475    Keygen {
476        /// Output directory for keys
477        #[arg(short, long)]
478        output_dir: Option<PathBuf>,
479    },
480
481    /// Sign archive
482    #[cfg(feature = "signing")]
483    #[command(display_order = 24)]
484    #[command(long_about = "Cryptographically signs a Hexz archive using a private key.")]
485    #[command(after_help = "hexz sign --key private.pem model.hxz")]
486    Sign {
487        /// Private key path
488        #[arg(long)]
489        key: PathBuf,
490
491        /// Archive to sign
492        image: PathBuf,
493    },
494
495    /// Verify archive signature
496    #[cfg(feature = "signing")]
497    #[command(display_order = 25)]
498    #[command(
499        long_about = "Verifies the cryptographic signature of an archive using a public key."
500    )]
501    #[command(after_help = "hexz verify --key public.pem model.hxz")]
502    Verify {
503        /// Public key path
504        #[arg(long)]
505        key: PathBuf,
506
507        /// Archive to verify
508        image: PathBuf,
509    },
510}