1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Clap command definitions for the `carbonado` binary.
//!
//! Kept in the library (behind the `cli` feature) so `gen-carbonado-man` and integration tests
//! share the same schema as the shipped binary. Implementation lives in `src/bin/carbonado/`.
use std::path::PathBuf;
use clap::{Parser, Subcommand};
/// Root CLI parser (`carbonado` binary).
#[derive(Parser)]
#[command(
name = "carbonado",
bin_name = "carbonado",
version,
about = "Apocalypse-resistant archival for files and directory trees",
long_about = "Encode and decode Carbonado archives for single files or directory trees.\n\n\
KEY MATERIAL:\n \
First encrypted encode auto-generates a BIP39 mnemonic (24 words), saved in \
plaintext at the path from `carbonado key path` (override: CARBONADO_MNEMONIC_PATH). \
Later encode/decode reuse it unless `--master` is given. Decode never auto-generates.\n\n\
ARTIFACTS:\n \
Single-file inboard: one `{hash}.adam.c{fmt:02x}` (format 14 → `.adam.c0e`).\n \
`--outboard`: `{hash}.c{fmt:02x}` bare + `{hash}.adam.c{fmt:02x}` sidecar \
(starts with ADAMANTINE10\\n). No `.par` / `.dict` siblings.\n \
Directory: inboard Adamantine 1.0 catalog `.adam.c14` (or `.adam.c15` with \
`--encrypted`) and heterogeneous bare segment mains (c12/c14 or c13/c15). Output \
defaults to `{input}-archive/`.\n \
`--zstd-level` is required when the Compression bit is set (including default format 14).\n\n\
See `carbonado <command> --help` for per-command options.",
after_help = "EXAMPLES:\n \
carbonado encode secret.bin --format 15\n \
carbonado encode ./my-project --encrypted -o ./archive-out\n\n \
carbonado encode myfile.bin --outboard --format 14\n \
carbonado encode ./my-project -o ./archive-out\n \
carbonado decode ./archive-out/<catalog>.adam.c14 -o ./restored\n \
carbonado decode ./archive-out/<catalog>.adam.c15 -o ./restored\n\n \
carbonado key path\n \
carbonado key show"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
/// Top-level subcommands.
#[derive(Subcommand)]
pub enum Commands {
/// Manage persisted BIP39 seed (plaintext; see `carbonado key path`)
Key {
#[command(subcommand)]
command: KeyCommands,
},
/// Encode a file or directory into a Carbonado archive
Encode {
/// Input file or directory
input: PathBuf,
/// Format level 0–15 (default 14 = public verifiable; odd values = encrypted)
#[arg(short, long, default_value_t = 14, value_name = "LEVEL")]
format: u8,
/// Single-file only: `{hash}.cXX` + `{hash}.adam.cXX` sidecar (default single-file is inboard).
#[arg(long)]
outboard: bool,
/// Directory only: encrypted catalog c15 and segment formats c13/c15 (auto-creates BIP39 seed if needed)
#[arg(long)]
encrypted: bool,
/// Zstd compression level (required when the Compression bit is set)
#[arg(long, value_name = "LEVEL")]
zstd_level: Option<i32>,
/// Optional RFC 8878 zstd dictionary file (bytes stored in the Adamantine dict section)
#[arg(long, value_name = "PATH")]
zstd_dict: Option<PathBuf>,
/// 32-byte master key as 64 hex chars (overrides stored BIP39 seed)
#[arg(long, value_name = "HEX")]
master: Option<String>,
/// Output directory [single-file default: . ; directory default: {input}-archive/]
#[arg(short, long, value_name = "DIR")]
output: Option<PathBuf>,
},
/// Decode a headered archive, bare outboard main, or Adamantine catalog
Decode {
/// Archive path: headered `.c{fmt:02x}`, bare outboard main, or `.adam.c{N}` directory catalog (decimal N)
input: PathBuf,
/// 32-byte master key as 64 hex chars (default: stored BIP39 seed from `carbonado key path`)
#[arg(long, value_name = "HEX")]
master: Option<String>,
/// Output file or directory [default: recovered.bin or recovered_dir]
#[arg(short, long, value_name = "PATH")]
output: Option<PathBuf>,
/// Bare outboard only: Bao root as 64 hex chars (default: parsed from filename)
#[arg(long, value_name = "HEX")]
hash: Option<String>,
/// Bare outboard only: format level 0–15 when not encoded in filename (rejected on headered inboard)
#[arg(short, long, value_name = "LEVEL")]
format: Option<u8>,
/// Bare outboard only: FEC padding in bytes [default: 0, auto from Adamantine sidecar]
#[arg(long, default_value = "0", value_name = "BYTES")]
padding: u32,
},
}
/// `carbonado key` subcommands.
#[derive(Subcommand)]
pub enum KeyCommands {
/// Generate a new English BIP39 mnemonic and save it (24 words by default)
Init {
/// Word count: 12, 15, 18, 21, or 24
#[arg(long, default_value_t = 24)]
words: usize,
/// Overwrite existing mnemonic at `carbonado key path`
#[arg(long)]
force: bool,
},
/// Import an existing English BIP39 mnemonic (words as separate arguments)
Import {
/// Mnemonic words (e.g. `carbonado key import abandon abandon ... about`)
#[arg(required = true)]
words: Vec<String>,
/// Overwrite existing mnemonic at `carbonado key path`
#[arg(long)]
force: bool,
},
/// Print the persisted mnemonic (sensitive — writes to stdout)
Show,
/// Print the path where the mnemonic is stored
Path,
}