summaryrefslogtreecommitdiffstats
path: root/src/api
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-28 17:40:07 +0100
committertv <tv@krebsco.de>2026-10-28 17:40:07 +0100
commit5c79174b36f7ce077b3d1b9feafe88008696fc31 (patch)
treef0b23629719d6a0f458f1b4d3aa5e675a8f9fa55 /src/api
parentd7959009790a422ebb17d4c4ab1d9be3d48dc511 (diff)
introduce relay only mode
Diffstat (limited to 'src/api')
-rw-r--r--src/api/mod.rs23
-rw-r--r--src/api/stats.rs46
-rw-r--r--src/api/stats/prometheus.rs19
3 files changed, 88 insertions, 0 deletions
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 45eaec4..2cb0415 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -76,6 +76,29 @@ pub(crate) fn router(state: AppState, auth_state: AuthState, debug_api: bool) ->
.with_state(state)
}
+/// The HTTP router of a node that only relays (`--relay-only`): its figures and the log
+/// filter (authenticated), and `/health`.
+#[cfg_attr(not(feature = "libp2p"), allow(dead_code))]
+pub(crate) fn relay_only_router(
+ transport: std::sync::Arc<dyn crate::transport::Transport>,
+ auth_state: AuthState,
+) -> Router {
+ let relay = stats::RelayOnly {
+ transport,
+ started: tokio::time::Instant::now(),
+ };
+ Router::new()
+ .route(
+ "/admin/log",
+ get(log::get_log_filter).put(log::put_log_filter),
+ )
+ .route("/admin/stats", get(stats::get_relay_only_stats))
+ .layer(middleware::from_fn_with_state(auth_state, basic_auth))
+ .route("/health", get(health::health))
+ .layer(middleware::from_fn(json_errors))
+ .with_state(relay)
+}
+
/// An error answer: `{"error": message}` with `details`' fields added (`docs/openapi.yaml`,
/// `Error`).
pub(crate) fn error_response(
diff --git a/src/api/stats.rs b/src/api/stats.rs
index 07521ab..889b675 100644
--- a/src/api/stats.rs
+++ b/src/api/stats.rs
@@ -189,6 +189,52 @@ struct StabilityStats {
observed_secs: u64,
}
+/// What `/admin/stats` of a node that only relays (`--relay-only`) says.
+#[derive(Clone)]
+pub(crate) struct RelayOnly {
+ pub(crate) transport: std::sync::Arc<dyn crate::transport::Transport>,
+ pub(crate) started: tokio::time::Instant,
+}
+
+#[derive(Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub(crate) struct RelayOnlyStats {
+ /// The relay's libp2p peer ID.
+ peer_id: String,
+ /// Where nodes reach it, as addresses to list (`_continuum/relays`).
+ addresses: Vec<String>,
+ uptime_secs: u64,
+ transports: TransportStats,
+}
+
+/// `GET /admin/stats` of a node that only relays.
+#[cfg_attr(not(feature = "libp2p"), allow(dead_code))]
+pub(crate) async fn get_relay_only_stats(
+ State(relay): State<RelayOnly>,
+ headers: HeaderMap,
+) -> Response {
+ let stats = RelayOnlyStats {
+ peer_id: relay.transport.local_id().to_owned(),
+ addresses: relay
+ .transport
+ .local_addresses()
+ .into_iter()
+ .map(|address| format!("libp2p:{address}"))
+ .collect(),
+ uptime_secs: relay.started.elapsed().as_secs(),
+ transports: relay.transport.stats(),
+ };
+ let accept = headers
+ .get(header::ACCEPT)
+ .and_then(|value| value.to_str().ok());
+ if prometheus::wanted(accept) {
+ let body = prometheus::render_relay_only(&stats);
+ ([(header::CONTENT_TYPE, prometheus::CONTENT_TYPE)], body).into_response()
+ } else {
+ Json(stats).into_response()
+ }
+}
+
/// `GET /admin/stats`.
pub(crate) async fn get_stats(State(state): State<AppState>, headers: HeaderMap) -> Response {
let stats = collect(&state).await;
diff --git a/src/api/stats/prometheus.rs b/src/api/stats/prometheus.rs
index 8b37864..690effa 100644
--- a/src/api/stats/prometheus.rs
+++ b/src/api/stats/prometheus.rs
@@ -363,6 +363,25 @@ fn transports(out: &mut Out, stats: &TransportStats) {
}
}
+/// `/admin/stats` of a node that only relays.
+pub(super) fn render_relay_only(stats: &super::RelayOnlyStats) -> String {
+ let mut out = Out::default();
+ out.family(
+ "relay_info",
+ "gauge",
+ "This relay's libp2p peer ID (always 1); it is no node.",
+ );
+ out.sample("relay_info", &[("peer_id", &stats.peer_id)], 1.0);
+ out.single(
+ "uptime_seconds",
+ "gauge",
+ "Seconds since this relay started.",
+ stats.uptime_secs as f64,
+ );
+ transports(&mut out, &stats.transports);
+ out.0
+}
+
/// A per-peer incident counter: its name, its help and how to read it.
type IncidentSeries = (&'static str, &'static str, fn(&Incidents) -> f64);