Skip to main content

iroh_services/
protocol.rs

1use anyhow::Result;
2use irpc::{
3    channel::{mpsc, oneshot},
4    rpc_requests,
5};
6use rcan::Rcan;
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9
10use crate::{caps::Caps, net_diagnostics::DiagnosticsReport};
11
12/// The main ALPN for connecting from the client to the cloud node.
13pub const ALPN: &[u8] = b"/iroh/n0des/1";
14
15pub type IrohServicesClient = irpc::Client<IrohServicesProtocol>;
16
17#[rpc_requests(message = ServicesMessage)]
18#[derive(Debug, Serialize, Deserialize)]
19#[allow(clippy::large_enum_variant)]
20pub enum IrohServicesProtocol {
21    #[rpc(tx=oneshot::Sender<()>)]
22    Auth(Auth),
23    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
24    PutMetrics(PutMetrics),
25    #[rpc(tx=oneshot::Sender<Pong>)]
26    Ping(Ping),
27
28    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
29    PutNetworkDiagnostics(PutNetworkDiagnostics),
30
31    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
32    GrantCap(GrantCap),
33
34    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
35    NameEndpoint(NameEndpoint),
36    #[rpc(tx=oneshot::Sender<RemoteResult<Option<SetLogLevel>>>)]
37    GetLogLevel(GetLogLevel),
38}
39
40/// Dedicated protocol for cloud-to-endpoint callbacks (net diagnostics, log
41/// level overrides).
42#[rpc_requests(message = ClientHostMessage)]
43#[derive(Debug, Serialize, Deserialize)]
44#[allow(clippy::large_enum_variant)]
45pub enum ClientHostProtocol {
46    #[rpc(tx=oneshot::Sender<()>)]
47    Auth(Auth),
48    #[rpc(tx=oneshot::Sender<RemoteResult<DiagnosticsReport>>)]
49    RunNetworkDiagnostics(RunNetworkDiagnostics),
50    #[rpc(tx=oneshot::Sender<RemoteResult<()>>)]
51    SetLogLevel(SetLogLevel),
52    #[rpc(tx=mpsc::Sender<RemoteResult<Vec<u8>>>)]
53    FetchLogs(FetchLogs),
54}
55
56pub type RemoteResult<T> = Result<T, RemoteError>;
57
58#[derive(Clone, Serialize, Deserialize, thiserror::Error, Debug)]
59pub enum RemoteError {
60    #[error("Missing capability: {}", _0.to_strings().join(", "))]
61    MissingCapability(Caps),
62    #[error("Unauthorized: {}", _0)]
63    AuthError(String),
64    #[error("Internal server error")]
65    InternalServerError,
66}
67
68/// Authentication on first request
69#[derive(Debug, Serialize, Deserialize)]
70pub struct Auth {
71    pub caps: Rcan<Caps>,
72}
73
74/// Request to store the given metrics data
75#[derive(Debug, Serialize, Deserialize)]
76pub struct PutMetrics {
77    pub session_id: Uuid,
78    pub update: iroh_metrics::encoding::Update,
79}
80
81/// Simple ping requests
82#[derive(Debug, Serialize, Deserialize)]
83pub struct Ping {
84    pub req_id: [u8; 16],
85}
86
87/// Simple ping response
88#[derive(Debug, Serialize, Deserialize)]
89pub struct Pong {
90    pub req_id: [u8; 16],
91}
92
93/// Publishing network diagnostics
94#[derive(Debug, Serialize, Deserialize)]
95pub struct PutNetworkDiagnostics {
96    pub report: crate::net_diagnostics::DiagnosticsReport,
97}
98
99/// ask this node to run diagnostics & return the result.
100/// present even without the net_diagnostics feature flag because the request
101/// struct is empty in both cases
102#[derive(Debug, Serialize, Deserialize)]
103pub struct RunNetworkDiagnostics;
104
105/// Grant a capability token to the remote endpoint. The remote should store
106/// the RCAN and use it when dialing back to authorize its requests.
107#[derive(Debug, Serialize, Deserialize)]
108pub struct GrantCap {
109    pub cap: Rcan<Caps>,
110}
111
112/// Label the client endpoint cloud-side with a string identifier.
113#[derive(Debug, Serialize, Deserialize)]
114pub struct NameEndpoint {
115    pub name: String,
116}
117
118/// Ask the client to stream the contents of its currently-active rolling
119/// log file. The client picks the newest file under its configured
120/// log directory matching the configured filename prefix.
121#[derive(Debug, Serialize, Deserialize)]
122pub struct FetchLogs {
123    /// Stop after this many bytes have been streamed. `None` means stream
124    /// the whole current file. The cloud caller is expected to enforce its
125    /// own plan-tier cap on top of this.
126    #[serde(default)]
127    pub max_bytes: Option<u64>,
128}
129
130/// Log-level filter settings. Used in two directions:
131/// - As a cloud-to-client push (via the [`crate::ClientHost`] callback)
132///   to apply a new override mid-session.
133/// - As the response payload to [`GetLogLevel`] so the client can pull
134///   the persisted setting on connect.
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct SetLogLevel {
137    /// `EnvFilter`-compatible directive string (for example
138    /// `"info,iroh=trace,iroh_blobs=debug"`).
139    pub directives: String,
140    /// If `Some`, the client reverts after this many seconds. If `None`, the
141    /// override is permanent until the next call.
142    pub expires_in_secs: Option<u64>,
143    /// Directives to revert to when the TTL fires. When `None`, the client
144    /// reverts to its install-time default. The cloud sends the project-wide
145    /// default here so per-endpoint overrides decay back to project policy
146    /// rather than to the client's own startup setting.
147    #[serde(default)]
148    pub revert_to: Option<String>,
149}
150
151/// Client-initiated request for the cloud's current log-level settings.
152/// Sent right after auth so the client lands on the correct filter
153/// without waiting for the cloud to push.
154#[derive(Debug, Serialize, Deserialize)]
155pub struct GetLogLevel;