Skip to main content

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;