Skip to main content

decdn_common/cli/
run.rs

1//! Arguments for `decdn-node run` (the daemon's only subcommand).
2//!
3//! `RunArgs` is also flattened into `decdn config validate` on the user
4//! CLI for env-var parity — operators get one set of `DECDN_*` env
5//! mappings whether they're starting the daemon or dry-running the
6//! resolver.
7
8use std::path::PathBuf;
9
10use clap::Args;
11
12use super::common::{LogFormat, LogLevel};
13
14/// Run the deCDN node.
15#[derive(Args, Debug)]
16pub struct RunArgs {
17    /// Identity and data storage options.
18    #[command(flatten)]
19    pub identity: IdentityArgs,
20
21    /// Network binding and relay options.
22    #[command(flatten)]
23    pub network: NetworkArgs,
24
25    /// Blockchain / EVM connection options.
26    #[command(flatten)]
27    pub blockchain: BlockchainArgs,
28
29    /// Cache storage options.
30    #[command(flatten)]
31    pub cache: CacheArgs,
32
33    /// Payment rate options.
34    #[command(flatten)]
35    pub payment: PaymentArgs,
36
37    /// Logging and metrics options.
38    #[command(flatten)]
39    pub observability: ObservabilityArgs,
40}
41
42/// Node identity and data storage.
43#[derive(Args, Debug)]
44#[command(next_help_heading = "Identity")]
45pub struct IdentityArgs {
46    /// Directory for node data (keys, cache, state) [default: ~/.decdn].
47    #[arg(long, value_name = "DIR", env = "DECDN_DATA_DIR")]
48    pub data_dir: Option<PathBuf>,
49
50    /// ISO 3166-1 alpha-2 region code (e.g. US, DE, SG).
51    #[arg(long, value_name = "CODE", env = "DECDN_REGION")]
52    pub region: Option<String>,
53}
54
55/// Network binding and relay configuration.
56#[derive(Args, Debug)]
57#[command(next_help_heading = "Network")]
58pub struct NetworkArgs {
59    /// QUIC bind port [default: 4433].
60    #[arg(long, value_name = "PORT", env = "DECDN_BIND_PORT")]
61    pub bind_port: Option<u16>,
62
63    /// iroh relay URL for NAT traversal.
64    #[arg(long, value_name = "URL", env = "DECDN_RELAY_URL")]
65    pub relay_url: Option<String>,
66}
67
68/// Blockchain / EVM connection settings.
69#[derive(Args, Debug)]
70#[command(next_help_heading = "Blockchain")]
71pub struct BlockchainArgs {
72    /// Arbitrum Sepolia JSON-RPC endpoint URL.
73    #[arg(long, value_name = "URL", env = "DECDN_RPC_URL")]
74    pub rpc_url: Option<String>,
75
76    /// Path to Ethereum keystore file (JSON).
77    #[arg(long, value_name = "PATH", env = "DECDN_ETH_KEYSTORE")]
78    pub eth_keystore: Option<PathBuf>,
79
80    /// File whose contents are the keystore password. Consulted after the
81    /// `DECDN_KEYSTORE_PASSWORD` env var and before an interactive prompt on a
82    /// TTY. A single trailing newline is stripped; a file that is empty after
83    /// that strip is a deliberate empty password, not an absent source. A path
84    /// that does not exist falls through; one that exists but cannot be read is
85    /// an error.
86    #[arg(long, value_name = "PATH", env = "DECDN_KEYSTORE_PASSWORD_FILE")]
87    pub keystore_password_file: Option<PathBuf>,
88
89    /// `PaymentPool` contract address (0x-prefixed hex).
90    #[arg(long, value_name = "ADDR", env = "DECDN_PAYMENT_POOL_ADDRESS")]
91    pub payment_pool_address: Option<String>,
92
93    /// `CapacityBond` contract address (0x-prefixed hex).
94    #[arg(long, value_name = "ADDR", env = "DECDN_CAPACITY_BOND_ADDRESS")]
95    pub capacity_bond_address: Option<String>,
96
97    /// `OriginAssignment` contract address (0x-prefixed hex). Optional:
98    /// enables the chain-backed origin directory backing the pull-through gate
99    /// and `FIND_VALUE` fallback (ADR 022), resolving a request's namespace via
100    /// `getOrigins`. When unset the origin directory is empty (deny-all): the
101    /// pull-through authorized-origin gate and the `FIND_VALUE` last-resort origin
102    /// fallback both find no on-chain origins. `publisher_registry_address` is
103    /// independent and not consumed by the node runtime.
104    #[arg(long, value_name = "ADDR", env = "DECDN_ORIGIN_ASSIGNMENT_ADDRESS")]
105    pub origin_assignment_address: Option<String>,
106
107    /// `PublisherRegistry` contract address (0x-prefixed hex). Optional; the
108    /// publish CLI's `namespace create`/transfer target (ADR 022).
109    ///
110    /// **Validate-only on the daemon.** The `decdn-node run` daemon never reads
111    /// this — its origin directory keys solely on `origin_assignment_address`,
112    /// and the `decdn publish` CLI resolves the registry through its own
113    /// `PublishChainArgs`, not `RunArgs`. It is accepted (and zero-rejected)
114    /// here purely so `decdn config validate` gives operators `DECDN_*` env-var
115    /// parity over the same `[blockchain]` table the publish CLI file consumes.
116    #[arg(long, value_name = "ADDR", env = "DECDN_PUBLISHER_REGISTRY_ADDRESS")]
117    pub publisher_registry_address: Option<String>,
118
119    /// `SlashJudge` contract address (0x-prefixed hex) — EIP-712
120    /// `verifyingContract` for probe `slash_sig` (ADR 014).
121    #[arg(long, value_name = "ADDR", env = "DECDN_SLASH_JUDGE_ADDRESS")]
122    pub slash_judge_address: Option<String>,
123
124    /// Required `ContentBlacklist` contract address (0x-prefixed hex). Startup
125    /// waits for its initial compliance sync before accepting any ALPN.
126    #[arg(long, value_name = "ADDR", env = "DECDN_CONTENT_BLACKLIST_ADDRESS")]
127    pub content_blacklist_address: Option<String>,
128
129    /// EIP-712 chain id for the `slash_sig` domain [default: 421614].
130    #[arg(long, value_name = "ID", env = "DECDN_CHAIN_ID")]
131    pub chain_id: Option<u64>,
132}
133
134/// Cache storage configuration.
135///
136/// Origin selection (HTTP / filesystem / S3-compatible)
137/// lives only in the config-file `[cache.origin]` table — there are no
138/// CLI flags for it. The S3 backend has many fields (bucket, region,
139/// endpoint, credentials) that don't fit cleanly on a command line, and
140/// keeping all three backends file-only avoids the trap of a
141/// CLI-vs-TOML mismatch silently picking the wrong backend.
142#[derive(Args, Debug)]
143#[command(next_help_heading = "Cache")]
144pub struct CacheArgs {
145    /// Directory for cached blobs \[default: \<data-dir\>/cache\].
146    #[arg(long, value_name = "DIR", env = "DECDN_CACHE_DIR")]
147    pub cache_dir: Option<PathBuf>,
148
149    /// Maximum cache size in megabytes [default: 102400 (100 GB)].
150    #[arg(long, value_name = "MB", env = "DECDN_CACHE_SIZE_MB")]
151    pub cache_size_mb: Option<u64>,
152
153    /// Maximum single blob size in megabytes [default: 51200 (50 GB), capped to
154    /// `cache_size_mb`]. Must not exceed `cache_size_mb`; `0` = unlimited.
155    #[arg(long, value_name = "MB", env = "DECDN_MAX_BLOB_SIZE_MB")]
156    pub max_blob_size_mb: Option<u64>,
157
158    /// Buyer-side ABSOLUTE per-MB rate ceiling for paid cache-miss pulls, in USDC
159    /// base units (same units as `--rate-per-mb`) [default: 0 = unlimited]. This
160    /// node refuses a provider quote above the lower of this and the rate the
161    /// chosen candidate advertised at probe, before paying (#1375). Distinct from
162    /// the seller-side `--delivery-ceiling` clamp.
163    #[arg(long, value_name = "UNITS", env = "DECDN_MAX_RATE_PER_MB")]
164    pub max_rate_per_mb: Option<u64>,
165
166    /// Max concurrently held (eviction-exempt) blobs for the probe hold
167    /// (ADR 005 §Hold budget) [default: 256]. `0` disables `has_blob: true`.
168    #[arg(long, value_name = "N", env = "DECDN_MAX_PROBE_HOLDS")]
169    pub max_probe_holds: Option<u64>,
170
171    /// Probe-hold slots reserved for the stake lane — registered
172    /// node-to-node cache-miss probes (#757, ADR 003 §Admission and
173    /// Priority) [default: 0 = off]. Under hold-budget pressure end-client
174    /// probes are shed once usage reaches `max_probe_holds - N`, reserving
175    /// the last `N` slots for node-to-node probes.
176    #[arg(long, value_name = "N", env = "DECDN_STAKE_LANE_RESERVED_HOLDS")]
177    pub stake_lane_reserved_holds: Option<u64>,
178}
179
180/// Payment rate configuration.
181#[derive(Args, Debug, Clone)]
182#[command(next_help_heading = "Payment")]
183pub struct PaymentArgs {
184    /// Rate per MB in USDC base units (6 decimals; 10 = $0.00001/MB) [default: 10].
185    #[arg(long, value_name = "UNITS", env = "DECDN_RATE_PER_MB")]
186    pub rate_per_mb: Option<u64>,
187}
188
189/// Observability settings (logging, metrics).
190#[derive(Args, Debug, Clone)]
191#[command(next_help_heading = "Observability")]
192pub struct ObservabilityArgs {
193    /// Log verbosity level [default: info].
194    #[arg(long, value_name = "LEVEL", env = "DECDN_LOG_LEVEL")]
195    pub log_level: Option<LogLevel>,
196
197    /// Log output format [default: pretty].
198    #[arg(long, value_name = "FORMAT", env = "DECDN_LOG_FORMAT")]
199    pub log_format: Option<LogFormat>,
200
201    /// HTTP port for the Prometheus /metrics endpoint [default: 9090].
202    #[arg(long, value_name = "PORT", env = "DECDN_METRICS_PORT")]
203    pub metrics_port: Option<u16>,
204
205    /// IP address to bind the metrics HTTP server on [default: 127.0.0.1].
206    /// Set to 0.0.0.0 for containerised deployments where the Prometheus
207    /// scraper runs on a different host.
208    #[arg(long, value_name = "ADDR", env = "DECDN_METRICS_BIND")]
209    pub metrics_bind: Option<std::net::IpAddr>,
210
211    /// Loopback admin HTTP port (`appendix-local-admin-http.md`); `0` disables [default: 9191].
212    #[arg(long, value_name = "PORT", env = "DECDN_ADMIN_PORT")]
213    pub admin_port: Option<u16>,
214
215    /// OTLP gRPC collector endpoint URL (`http://` only); setting it enables span export.
216    #[arg(long, value_name = "URL", env = "DECDN_OTLP_ENDPOINT")]
217    pub otlp_endpoint: Option<String>,
218}