Skip to main content

iroh_services/
protocol.rs

1use anyhow::Result;
2use irpc::{channel::oneshot, rpc_requests};
3use rcan::Rcan;
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7use crate::{caps::Caps, net_diagnostics::DiagnosticsReport};
8
9/// The main ALPN for connecting from the client to the cloud node.
10pub const ALPN: &[u8] = b"/iroh/n0des/1";
11
12pub type IrohServicesClient = irpc::Client<IrohServicesProtocol>;
13
14#[rpc_requests(message = ServicesMessage)]
15#[derive(Debug, Serialize, Deserialize)]
16#[allow(clippy::large_enum_variant)]
17pub enum IrohServicesProtocol {
18    #[rpc(tx=oneshot::Sender<()>)]
19    Auth(Auth),
20    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
21    PutMetrics(PutMetrics),
22    #[rpc(tx=oneshot::Sender<Pong>)]
23    Ping(Ping),
24
25    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
26    PutNetworkDiagnostics(PutNetworkDiagnostics),
27
28    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
29    GrantCap(GrantCap),
30
31    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
32    NameEndpoint(NameEndpoint),
33
34    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
35    PutLogs(PutLogs),
36}
37
38/// Dedicated protocol for cloud-to-endpoint callbacks (net diagnostics, log
39/// level overrides).
40#[rpc_requests(message = ClientHostMessage)]
41#[derive(Debug, Serialize, Deserialize)]
42#[allow(clippy::large_enum_variant)]
43pub enum ClientHostProtocol {
44    #[rpc(tx=oneshot::Sender<()>)]
45    Auth(Auth),
46    #[rpc(tx=oneshot::Sender<RemoteResult<DiagnosticsReport>>)]
47    RunNetworkDiagnostics(RunNetworkDiagnostics),
48    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
49    SetLogLevel(SetLogLevel),
50}
51
52pub type RemoteResult<T> = Result<T, RemoteError>;
53
54#[derive(Clone, Serialize, Deserialize, thiserror::Error, Debug)]
55pub enum RemoteError {
56    #[error("Missing capability: {}", _0.to_strings().join(", "))]
57    MissingCapability(Caps),
58    #[error("Unauthorized: {}", _0)]
59    AuthError(String),
60    #[error("Internal server error")]
61    InternalServerError,
62}
63
64/// Authentication on first request
65#[derive(Debug, Serialize, Deserialize)]
66pub struct Auth {
67    pub caps: Rcan<Caps>,
68}
69
70/// Request to store the given metrics data
71#[derive(Debug, Serialize, Deserialize)]
72pub struct PutMetrics {
73    pub session_id: Uuid,
74    pub update: iroh_metrics::encoding::Update,
75}
76
77/// Simple ping requests
78#[derive(Debug, Serialize, Deserialize)]
79pub struct Ping {
80    pub req_id: [u8; 16],
81}
82
83/// Simple ping response
84#[derive(Debug, Serialize, Deserialize)]
85pub struct Pong {
86    pub req_id: [u8; 16],
87}
88
89/// Publishing network diagnostics
90#[derive(Debug, Serialize, Deserialize)]
91pub struct PutNetworkDiagnostics {
92    pub report: crate::net_diagnostics::DiagnosticsReport,
93}
94
95/// ask this node to run diagnostics & return the result.
96/// present even without the net_diagnostics feature flag because the request
97/// struct is empty in both cases
98#[derive(Debug, Serialize, Deserialize)]
99pub struct RunNetworkDiagnostics;
100
101/// Grant a capability token to the remote endpoint. The remote should store
102/// the RCAN and use it when dialing back to authorize its requests.
103#[derive(Debug, Serialize, Deserialize)]
104pub struct GrantCap {
105    pub cap: Rcan<Caps>,
106}
107
108/// Label the client endpoint cloud-side with a string identifier.
109#[derive(Debug, Serialize, Deserialize)]
110pub struct NameEndpoint {
111    pub name: String,
112}
113
114/// A single structured log line emitted by a client process.
115///
116/// The shape mirrors the JSON format produced by `tracing-subscriber`'s JSON
117/// formatter, with the level, target, and timestamp lifted into top-level
118/// fields so the cloud can index them as columns. The remaining structured
119/// fields and the span stack travel as `Vec<(String, FieldValue)>` so the
120/// schema is closed and `postcard` can encode and decode it without any
121/// `deserialize_any` paths.
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
123pub struct LogLine {
124    /// RFC 3339 timestamp produced at log emission time.
125    pub timestamp: String,
126    /// Log level: TRACE, DEBUG, INFO, WARN, ERROR.
127    pub level: String,
128    /// Log target (typically the originating module path).
129    pub target: String,
130    /// Structured fields attached to the event. By convention, the
131    /// `message` field carries the human-readable text.
132    pub fields: Vec<(String, FieldValue)>,
133    /// Active span stack, outermost first. Empty when no span is in scope.
134    pub spans: Vec<SpanInfo>,
135}
136
137/// A span recorded as part of a [`LogLine`].
138#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
139pub struct SpanInfo {
140    pub name: String,
141    pub fields: Vec<(String, FieldValue)>,
142}
143
144/// Wire-safe representation of a structured tracing field value.
145///
146/// Closed enum so `postcard` can round-trip it without `deserialize_any`.
147/// Anything that is not one of the typed variants (a `Debug`-formatted
148/// value, a non-finite float, a 128-bit integer) is rendered to a string
149/// at the producer with [`FieldValue::Other`].
150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
151pub enum FieldValue {
152    Str(String),
153    I64(i64),
154    U64(u64),
155    F64(f64),
156    Bool(bool),
157    /// Fallback for values that do not fit the typed variants. Carries the
158    /// `Debug`-formatted text.
159    Other(String),
160}
161
162/// A batch of log lines pushed from a client to the cloud.
163#[derive(Debug, Serialize, Deserialize)]
164pub struct PutLogs {
165    pub session_id: Uuid,
166    pub lines: Vec<LogLine>,
167    /// Number of lines dropped on the client since the last successful push,
168    /// either due to the buffer being full or the throttle being exceeded.
169    pub dropped: u32,
170}
171
172/// Cloud-issued instruction to override the client's tracing filter.
173#[derive(Debug, Serialize, Deserialize)]
174pub struct SetLogLevel {
175    /// `EnvFilter`-compatible directive string (for example
176    /// `"info,iroh=trace,iroh_blobs=debug"`).
177    pub directives: String,
178    /// If `Some`, the client reverts after this many seconds. If `None`, the
179    /// override is permanent until the next call.
180    pub expires_in_secs: Option<u64>,
181    /// Directives to revert to when the TTL fires. When `None`, the client
182    /// reverts to its install-time default. The cloud sends the project-wide
183    /// default here so per-endpoint overrides decay back to project policy
184    /// rather than to the client's own startup setting.
185    #[serde(default)]
186    pub revert_to: Option<String>,
187}