Skip to main content

vex_cli/proto/
mod.rs

1use serde::{Deserialize, Serialize};
2
3/// Default port vexd listens on for TLS TCP connections.
4pub const DEFAULT_TCP_PORT: u16 = 7422;
5
6/// Default port vexd listens on for HTTPS (HTTP API) connections.
7pub const DEFAULT_HTTP_PORT: u16 = 7423;
8
9// ── Wire types ────────────────────────────────────────────────────────────────
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(tag = "type", content = "data")]
13pub enum Command {
14    Status,
15    Whoami,
16    PairCreate {
17        label: Option<String>,
18        /// Expiry in seconds from now
19        expire_secs: Option<u64>,
20    },
21    PairList,
22    PairRevoke {
23        id: String,
24    },
25    PairRevokeAll,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29#[serde(tag = "type", content = "data")]
30pub enum Response {
31    Pong,
32    Ok,
33    DaemonStatus(DaemonStatus),
34    ClientInfo(ClientInfo),
35    /// Returned after PairCreate; contains the plaintext secret (one-time)
36    Pair(PairPayload),
37    PairedClient(PairedClient),
38    PairedClients(Vec<PairedClient>),
39    /// Returned by PairRevoke / PairRevokeAll, carrying the revoked count.
40    Revoked(u32),
41    Error(VexProtoError),
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct DaemonStatus {
46    pub uptime_secs: u64,
47    pub connected_clients: u32,
48    pub version: String,
49}
50
51/// Returned by PairCreate — contains the plaintext secret for the new token.
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct PairPayload {
54    pub token_id: String,
55    pub token_secret: String,
56    /// Optional TCP host for encoding into a QR pairing string
57    pub host: Option<String>,
58}
59
60impl PairPayload {
61    /// Returns the pairing string in `<token_id>:<token_secret>` format.
62    pub fn pairing_string(&self) -> String {
63        format!("{}:{}", self.token_id, self.token_secret)
64    }
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct PairedClient {
69    pub token_id: String,
70    pub label: Option<String>,
71    pub created_at: String,
72    pub expires_at: Option<String>,
73    pub last_seen: Option<String>,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ClientInfo {
78    pub token_id: Option<String>,
79    pub is_local: bool,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(rename_all = "snake_case")]
84pub enum Transport {
85    Unix,
86    Tcp,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(tag = "code", content = "message")]
91pub enum VexProtoError {
92    Unauthorized,
93    LocalOnly,
94    NotFound,
95    Internal(String),
96}
97
98/// Sent by the client at the start of every TCP connection before any Command.
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct AuthToken {
101    pub token_id: String,
102    /// Plaintext hex-encoded 32-byte secret
103    pub token_secret: String,
104}
105
106// ── Framing ───────────────────────────────────────────────────────────────────
107
108pub mod framing {
109    use serde::{Deserialize, Serialize};
110    use std::io;
111    use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
112
113    #[derive(Debug)]
114    pub enum VexFrameError {
115        Io(io::Error),
116        Json(serde_json::Error),
117    }
118
119    impl std::fmt::Display for VexFrameError {
120        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121            match self {
122                VexFrameError::Io(e) => write!(f, "IO error: {e}"),
123                VexFrameError::Json(e) => write!(f, "JSON error: {e}"),
124            }
125        }
126    }
127
128    impl std::error::Error for VexFrameError {}
129
130    impl From<io::Error> for VexFrameError {
131        fn from(e: io::Error) -> Self {
132            VexFrameError::Io(e)
133        }
134    }
135
136    impl From<serde_json::Error> for VexFrameError {
137        fn from(e: serde_json::Error) -> Self {
138            VexFrameError::Json(e)
139        }
140    }
141
142    /// Write a length-prefixed JSON frame to `w`.
143    pub async fn send<W, T>(w: &mut W, msg: &T) -> Result<(), VexFrameError>
144    where
145        W: AsyncWrite + Unpin,
146        T: Serialize,
147    {
148        let body = serde_json::to_vec(msg)?;
149        w.write_u32(body.len() as u32).await?;
150        w.write_all(&body).await?;
151        Ok(())
152    }
153
154    /// Read a length-prefixed JSON frame from `r`.
155    pub async fn recv<R, T>(r: &mut R) -> Result<T, VexFrameError>
156    where
157        R: AsyncRead + Unpin,
158        T: for<'de> Deserialize<'de>,
159    {
160        let len = r.read_u32().await?;
161        let mut buf = vec![0u8; len as usize];
162        r.read_exact(&mut buf).await?;
163        Ok(serde_json::from_slice(&buf)?)
164    }
165}