#[cfg(not(feature = "net"))]
fn main() {
println!("hop-relay: compiled without the `net` feature — HTTP frontend disabled.");
}
#[cfg(feature = "net")]
#[tokio::main]
async fn main() {
use std::path::PathBuf;
use std::process;
use clap::Parser;
use tracing_subscriber::EnvFilter;
fn init_tracing() {
let filter = EnvFilter::try_from_env("HOP_LOG")
.or_else(|_| EnvFilter::try_from_default_env())
.unwrap_or_else(|_| EnvFilter::new("info"));
let json = std::env::var("HOP_LOG_JSON")
.ok()
.is_some_and(|v| matches!(v.as_str(), "1" | "true" | "TRUE" | "yes" | "YES"));
let base = tracing_subscriber::fmt()
.with_env_filter(filter)
.with_target(true);
if json {
base.json().init();
} else {
base.init();
}
}
#[derive(Parser, Debug)]
#[command(name = "hop-relay", about = "Hop-Corr relay HTTP server")]
struct Args {
#[arg(long, env = "HOP_LISTEN", default_value = "127.0.0.1:8787")]
listen: String,
#[arg(long, env = "HOP_DATA_DIR", default_value = "./data")]
data_dir: PathBuf,
#[arg(long, env = "HOP_AUTH_TOKEN")]
auth_token: Option<String>,
#[arg(long, env = "HOP_MAX_BODY_BYTES", default_value_t = 1 * 1024 * 1024)]
max_body_bytes: u64,
#[arg(long, env = "HOP_MAX_COEFFS_LEN", default_value_t = 1024)]
max_coeffs_len: usize,
#[arg(long, env = "HOP_MAX_ACTIONS_PER_REQUEST", default_value_t = 4096)]
max_actions_per_request: usize,
#[arg(long, env = "HOP_MAX_ACTION_WORK", default_value_t = 10_000_000)]
max_action_work: u64,
#[arg(long, env = "HOP_MAX_PROVE_ENTRIES", default_value_t = 10_000)]
max_prove_entries: usize,
#[arg(long, env = "HOP_MAX_IN_FLIGHT", default_value_t = 128)]
max_in_flight_requests: usize,
#[arg(long, env = "HOP_REQUEST_TIMEOUT_MS", default_value_t = 30_000)]
request_timeout_ms: u64,
#[arg(long, env = "HOP_RATE_LIMIT_RPS", default_value_t = 0)]
rate_limit_rps: u32,
#[arg(long, env = "HOP_RATE_LIMIT_BURST", default_value_t = 0)]
rate_limit_burst: u32,
#[arg(long, env = "HOP_SHUTDOWN_GRACE_MS", default_value_t = 10_000)]
shutdown_grace_ms: u64,
#[arg(long, env = "HOP_CORS_ANY", default_value_t = false)]
cors_any: bool,
#[arg(long = "cors-origin", env = "HOP_CORS_ORIGINS", value_delimiter = ',')]
cors_origins: Vec<String>,
}
init_tracing();
let args = Args::parse();
if args.auth_token.is_none() {
tracing::warn!("no auth token configured; relay endpoints are unauthenticated");
}
if args.cors_any {
tracing::warn!("CORS any-origin enabled; do not use this in production");
}
if args.rate_limit_rps > 0 && args.rate_limit_burst == 0 {
tracing::warn!("rate limiting enabled but burst is 0; rate limiting will be disabled");
}
if args.rate_limit_burst > 0 && args.rate_limit_rps == 0 {
tracing::warn!("rate limit burst set but rps is 0; rate limiting will be disabled");
}
let cfg = hop_relay::net::ServerConfig {
data_dir: args.data_dir,
auth_token: args.auth_token,
max_body_bytes: args.max_body_bytes,
max_coeffs_len: args.max_coeffs_len,
max_actions_per_request: args.max_actions_per_request,
max_action_work: args.max_action_work,
max_prove_entries: args.max_prove_entries,
max_in_flight_requests: args.max_in_flight_requests,
request_timeout_ms: args.request_timeout_ms,
rate_limit_rps: args.rate_limit_rps,
rate_limit_burst: args.rate_limit_burst,
cors_allow_any_origin: args.cors_any,
cors_allow_origins: args.cors_origins,
shutdown_grace_ms: args.shutdown_grace_ms,
};
tracing::info!(listen = %args.listen, "starting hop-relay HTTP server");
if let Err(err) = hop_relay::net::serve(&args.listen, cfg).await {
tracing::error!(error = %err, "hop-relay server error");
process::exit(1);
}
}