decdn_common/cli/mod.rs
1//! CLI argument parsing for the `decdn` binary.
2//!
3//! [`RunArgs`] is also re-used (flattened) by [`ConfigValidateArgs`] —
4//! `decdn config validate` runs the same resolver `decdn-node run`
5//! does, against the same `DECDN_*` env vars, without binding ports
6//! or starting any tasks. The daemon's own clap shell lives in
7//! `crates/node/src/main.rs` and is not part of this module.
8
9pub mod appeal;
10pub mod byte_size;
11pub mod common;
12pub mod config_cmd;
13pub mod fetch;
14pub mod gateway;
15pub mod get;
16pub mod key_gen;
17pub mod node;
18pub mod origin;
19pub mod pool;
20pub mod probe;
21pub mod publish;
22pub mod run;
23pub mod self_update;
24pub mod setup;
25pub mod whoami;
26
27pub use appeal::{AppealArgs, AppealCommand, AppealSlashArgs};
28pub use byte_size::parse_byte_size;
29pub use common::{
30 CommonChainArgs, ConfigPathSource, LogFormat, default_client_data_dir, default_config_path,
31 default_data_dir,
32};
33pub use config_cmd::{ConfigArgs, ConfigCommand, ConfigInitArgs, ConfigValidateArgs};
34pub use fetch::ClientFetchArgs;
35pub use gateway::GatewayArgs;
36pub use get::GetArgs;
37pub use key_gen::KeyGenArgs;
38pub use node::{
39 BondArgs, ChainArgs, DeregisterArgs, DoctorArgs, DrainArgs, EvictArgs, HealthArgs, LanesArgs,
40 LookupArgs, NodeArgs, NodeCommand, PoolsArgs, RegisterArgs, ReloadArgs, RotateKeyArgs,
41 RotateKeyTarget, SlashesArgs, StatusArgs, TopArgs, UnbondArgs, UpdateMultiaddrsArgs,
42 UpdateRegionArgs,
43};
44pub use origin::{OriginArgs, OriginCommand, OriginImportArgs};
45pub use pool::{
46 PoolArgs, PoolAssignArgs, PoolChainArgs, PoolCloseArgs, PoolCommand, PoolListArgs,
47 PoolOpenArgs, PoolReclaimArgs, PoolTopUpArgs,
48};
49pub use probe::ProbeArgs;
50pub use publish::{
51 AssignArgs, NamespaceArgs, NamespaceCommand, NamespaceCreateArgs, PublishArgs,
52 PublishChainArgs, PublishCommand, RevokeArgs,
53};
54pub use run::RunArgs;
55pub use self_update::SelfUpdateArgs;
56pub use setup::SetupArgs;
57pub use whoami::WhoamiArgs;
58
59use std::path::PathBuf;
60
61use clap::{Parser, Subcommand};
62
63/// User-facing deCDN CLI. The daemon lives in the separate `decdn-node`
64/// binary; this CLI is what you type from a terminal — probe, get, pools,
65/// publishing, node admin.
66#[derive(Parser, Debug)]
67#[command(
68 name = "decdn",
69 version,
70 about = "deCDN user CLI — probe, get, pools, publishing, node admin",
71 long_about = "User-facing deCDN CLI. Pairs with the `decdn-node` daemon: \
72 this binary carries every command a human types in a \
73 terminal (probe, get, payment pools, publishing, \
74 origin import, node admin, key-gen, config). The \
75 `node` subcommand group talks to a running daemon over \
76 the loopback admin RPC surface \
77 (`appendix-local-admin-http.md`) and fails cleanly on a \
78 host with no daemon running."
79)]
80pub struct Cli {
81 /// Path to TOML config file [default: ~/.decdn/node.toml].
82 #[arg(long, global = true, value_name = "PATH")]
83 pub config: Option<PathBuf>,
84
85 /// Enable client-side logging at this level. Off by default (the CLI is a
86 /// terminal UI); `RUST_LOG` overrides this when set. Useful for diagnosing
87 /// a stalled `decdn get` from the client side. Deliberately not bound to
88 /// an environment variable: the CLI stays silent unless logging is asked
89 /// for on this invocation, even when the daemon's log-level env is exported.
90 #[arg(long, global = true, value_name = "LEVEL")]
91 pub log_level: Option<common::LogLevel>,
92
93 /// Enable client-side logging by count: `-v` is `info`, `-vv` is `debug`,
94 /// `-vvv` is `trace`. Shorthand for `--log-level`, which wins when both are
95 /// given; `RUST_LOG` wins over both.
96 #[arg(short = 'v', long, global = true, action = clap::ArgAction::Count)]
97 pub verbose: u8,
98
99 /// Subcommand to execute.
100 #[command(subcommand)]
101 pub command: Command,
102}
103
104/// Available subcommands. There is intentionally no `run` subcommand —
105/// running the daemon is `decdn-node run` (issue #421). For a dry-run
106/// resolution of the daemon's startup config without binding ports,
107/// see [`ConfigValidateArgs`] (`decdn config validate`), which
108/// flattens the same [`RunArgs`] the daemon parses.
109#[derive(Subcommand, Debug)]
110pub enum Command {
111 /// Generate a new Ed25519 node key and Ethereum keystore.
112 KeyGen(KeyGenArgs),
113 /// Print the local identity read-only: iroh node id, eth address, and the
114 /// resolved key paths. Generates and rotates nothing.
115 Whoami(WhoamiArgs),
116 /// Manage configuration files (`init`, `validate`).
117 Config(ConfigArgs),
118 /// Probe a running node over the `cdn/probe/v1` ALPN.
119 Probe(ProbeArgs),
120 /// Serve `decdn:` URIs to a browser from a loopback HTTP gateway, paid
121 /// with the scoped capabilities pages and the token holder register.
122 Gateway(Box<GatewayArgs>),
123 /// Pull what a `decdn:` URI names: a blob (`decdn:<hash>`), a whole bundle
124 /// (`decdn:<hash>/`), or one bundle entry (`decdn:<hash>/<path>`), over the
125 /// paid `cdn/client/v1` path, paying per-MB from a `PaymentPool`.
126 Get(Box<GetArgs>),
127 /// Guided node onboarding (ADR 019 Phases 1–2): pre-flight checks,
128 /// key generation, bond, and on-chain registration, with a final
129 /// readiness summary. Thin orchestration over `key-gen` / `node bond` /
130 /// `node register` — submits no transaction the primitives don't.
131 Setup(SetupArgs),
132 /// Operator-local admin commands that query a running node
133 /// over its loopback HTTP surface (`appendix-local-admin-http.md`).
134 Node(NodeArgs),
135 /// Seed a cache origin store from local content (`import`) — offline,
136 /// config-free filesystem work that writes each blob's sharded data object
137 /// and its `{hex}.obao4` outboard in the layout the daemon reads (ADR 037).
138 Origin(OriginArgs),
139 /// Client-side payment-pool lifecycle (`list`/`status`, `open`, `top-up`,
140 /// `close`, `reclaim`, `assign`).
141 Pool(PoolArgs),
142 /// Publisher control plane: create namespaces, and seat or unseat
143 /// authorized origins on-chain (issues #1029 / #1491).
144 /// Content is bound to a namespace off-chain at fetch time, so there is no
145 /// per-hash on-chain claim.
146 Publish(PublishArgs),
147 /// File a slash appeal, posting the appeal bond (ADR 028).
148 Appeal(AppealArgs),
149 /// Replace this binary with the latest signed release. The release's
150 /// `SHA256SUMS.minisig` must verify against a minisign key this build
151 /// embeds; a binary `cargo install` placed, or one in a directory this
152 /// user cannot write, is refused with the command to run instead.
153 SelfUpdate(SelfUpdateArgs),
154}
155
156/// Whether the parsed command asked for JSON output, at whatever depth its
157/// `--json` flag sits. A command with no `--json` flag never asks.
158#[must_use]
159pub fn json_requested(matches: &clap::ArgMatches) -> bool {
160 matches!(matches.try_get_one::<bool>("json"), Ok(Some(true)))
161 || matches
162 .subcommand()
163 .is_some_and(|(_, sub)| json_requested(sub))
164}
165
166#[cfg(test)]
167mod tests;