cyphr_cli/lib.rs
1//! Cyphr CLI library components.
2//!
3//! This module exposes the CLI types for potential reuse in tests or other tools.
4
5#![warn(missing_docs)]
6#![warn(rust_2018_idioms)]
7#![forbid(unsafe_code)]
8
9use std::path::PathBuf;
10
11use clap::{Parser, Subcommand, ValueEnum};
12
13pub mod commands;
14pub mod keystore;
15
16/// CLI error type. Replaces `Box<dyn Error>` throughout the CLI crate.
17#[derive(Debug, thiserror::Error)]
18pub enum Error {
19 /// I/O error.
20 #[error("{0}")]
21 Io(#[from] std::io::Error),
22
23 /// Keystore operation failed.
24 #[error("{0}")]
25 Keystore(#[from] keystore::Error),
26
27 /// Core cyphr protocol error.
28 #[error("{0}")]
29 Cyphr(#[from] cyphr::Error),
30
31 /// JSON serialization/deserialization error.
32 #[error("{0}")]
33 Json(#[from] serde_json::Error),
34
35 /// Base64 decoding error.
36 #[error("invalid base64url: {0}")]
37 Base64(#[from] base64ct::Error),
38
39 /// A required field was missing from a JSON object.
40 #[error("missing field: {0}")]
41 MissingField(&'static str),
42
43 /// Invalid argument or option value.
44 #[error("{0}")]
45 InvalidArgument(String),
46
47 /// Cryptographic signing or verification failed.
48 #[error("{0}")]
49 Signing(String),
50
51 /// Storage layer error (file store operations).
52 #[error("{0}")]
53 Storage(String),
54
55 /// File store error.
56 #[error("{0}")]
57 FileStore(#[from] cyphr_storage::FileStoreError),
58
59 /// Load error (importing/replaying commits).
60 #[error("{0}")]
61 Load(#[from] cyphr_storage::LoadError),
62
63 /// Export error (serializing principal to commits).
64 #[error("{0}")]
65 Export(#[from] cyphr_storage::ExportError),
66}
67
68/// CLI result type alias.
69pub type Result<T> = std::result::Result<T, Error>;
70
71/// Cyphr identity protocol CLI.
72#[derive(Parser)]
73#[command(name = "cyphr", version, about, long_about = None)]
74pub struct Cli {
75 /// Storage backend URI (e.g., file:./data)
76 #[arg(long, default_value = "file:./cyphr-data")]
77 pub store: String,
78
79 /// Path to private key storage
80 #[arg(long, default_value = "./cyphr-keys.json")]
81 pub keystore: PathBuf,
82
83 /// Authority domain for transaction typ URIs (e.g., example.com)
84 #[arg(long, default_value = "cyphr.me")]
85 pub authority: String,
86
87 /// Output format
88 #[arg(long, value_enum, default_value_t = OutputFormat::Table)]
89 pub output: OutputFormat,
90
91 /// Subcommand to execute
92 #[command(subcommand)]
93 pub command: Commands,
94}
95
96/// Output format for command results.
97#[derive(Debug, Clone, Copy, ValueEnum)]
98pub enum OutputFormat {
99 /// Human-readable table format
100 Table,
101 /// Machine-parseable JSON
102 Json,
103}
104
105/// Top-level subcommands.
106#[derive(Subcommand)]
107pub enum Commands {
108 /// Create a new identity
109 Init {
110 /// Algorithm for genesis key (ES256, ES384, ES512, Ed25519)
111 #[arg(long, default_value = "ES256")]
112 algo: String,
113
114 /// Use existing key from keystore (by thumbprint)
115 #[arg(long)]
116 key: Option<String>,
117
118 /// Create explicit genesis with multiple keys (comma-separated thumbprints)
119 #[arg(long, value_delimiter = ',')]
120 keys: Option<Vec<String>>,
121 },
122
123 /// Key management operations
124 Key {
125 /// Key subcommand to execute
126 #[command(subcommand)]
127 command: KeyCommands,
128 },
129
130 /// ParsedCoz operations
131 Tx {
132 /// ParsedCoz subcommand to execute
133 #[command(subcommand)]
134 command: TxCommands,
135 },
136
137 /// Display identity state
138 Inspect {
139 /// Principal Root (base64url)
140 #[arg(long)]
141 identity: String,
142 },
143
144 /// Export identity to JSONL file
145 Export {
146 /// Principal Root (base64url)
147 #[arg(long)]
148 identity: String,
149
150 /// Output file path
151 #[arg(long)]
152 output: PathBuf,
153 },
154
155 /// Import identity from JSONL file
156 Import {
157 /// Input file path
158 #[arg(long)]
159 input: PathBuf,
160 },
161}
162
163/// Key management subcommands.
164#[derive(Subcommand)]
165pub enum KeyCommands {
166 /// Generate a new keypair
167 Generate {
168 /// Algorithm (ES256, ES384, ES512, Ed25519)
169 #[arg(long, default_value = "ES256")]
170 algo: String,
171
172 /// Optional tag for the key
173 #[arg(long)]
174 tag: Option<String>,
175 },
176
177 /// Add a key to an identity
178 Add {
179 /// Principal Root (base64url)
180 #[arg(long)]
181 identity: String,
182
183 /// Thumbprint of key to add (if omitted, generates new key)
184 #[arg(long)]
185 key: Option<String>,
186
187 /// Thumbprint of signing key
188 #[arg(long)]
189 signer: String,
190 },
191
192 /// Revoke a key from an identity
193 Revoke {
194 /// Principal Root (base64url)
195 #[arg(long)]
196 identity: String,
197
198 /// Thumbprint of key to revoke
199 #[arg(long)]
200 key: String,
201
202 /// Thumbprint of signing key
203 #[arg(long)]
204 signer: String,
205 },
206
207 /// List keys (from keystore if no identity, from identity if provided)
208 List {
209 /// Principal Root (base64url) - if omitted, lists keystore keys
210 #[arg(long)]
211 identity: Option<String>,
212 },
213}
214
215/// ParsedCoz subcommands.
216#[derive(Subcommand)]
217pub enum TxCommands {
218 /// List cozies for an identity
219 List {
220 /// Principal Root (base64url)
221 #[arg(long)]
222 identity: String,
223 },
224
225 /// Verify coz chain integrity
226 Verify {
227 /// Principal Root (base64url)
228 #[arg(long)]
229 identity: String,
230 },
231}