sentinel_agent_protocol/
protocol.rs

1//! Agent protocol types and constants.
2//!
3//! This module defines the wire protocol types for communication between
4//! the proxy dataplane and external processing agents.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Agent protocol version
10pub const PROTOCOL_VERSION: u32 = 1;
11
12/// Maximum message size (10MB)
13pub const MAX_MESSAGE_SIZE: usize = 10 * 1024 * 1024;
14
15/// Agent event type
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18pub enum EventType {
19    /// Request headers received
20    RequestHeaders,
21    /// Request body chunk received
22    RequestBodyChunk,
23    /// Response headers received
24    ResponseHeaders,
25    /// Response body chunk received
26    ResponseBodyChunk,
27    /// Request/response complete (for logging)
28    RequestComplete,
29}
30
31/// Agent decision
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33#[serde(rename_all = "snake_case")]
34pub enum Decision {
35    /// Allow the request/response to continue
36    Allow,
37    /// Block the request/response
38    Block {
39        /// HTTP status code to return
40        status: u16,
41        /// Optional response body
42        body: Option<String>,
43        /// Optional response headers
44        headers: Option<HashMap<String, String>>,
45    },
46    /// Redirect the request
47    Redirect {
48        /// Redirect URL
49        url: String,
50        /// HTTP status code (301, 302, 303, 307, 308)
51        status: u16,
52    },
53    /// Challenge the client (e.g., CAPTCHA)
54    Challenge {
55        /// Challenge type
56        challenge_type: String,
57        /// Challenge parameters
58        params: HashMap<String, String>,
59    },
60}
61
62impl Default for Decision {
63    fn default() -> Self {
64        Self::Allow
65    }
66}
67
68/// Header modification operation
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(rename_all = "snake_case")]
71pub enum HeaderOp {
72    /// Set a header (replace if exists)
73    Set { name: String, value: String },
74    /// Add a header (append if exists)
75    Add { name: String, value: String },
76    /// Remove a header
77    Remove { name: String },
78}
79
80/// Request metadata sent to agents
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct RequestMetadata {
83    /// Correlation ID for request tracing
84    pub correlation_id: String,
85    /// Request ID (internal)
86    pub request_id: String,
87    /// Client IP address
88    pub client_ip: String,
89    /// Client port
90    pub client_port: u16,
91    /// Server name (SNI or Host header)
92    pub server_name: Option<String>,
93    /// Protocol (HTTP/1.1, HTTP/2, etc.)
94    pub protocol: String,
95    /// TLS version if applicable
96    pub tls_version: Option<String>,
97    /// TLS cipher suite if applicable
98    pub tls_cipher: Option<String>,
99    /// Route ID that matched
100    pub route_id: Option<String>,
101    /// Upstream ID
102    pub upstream_id: Option<String>,
103    /// Request start timestamp (RFC3339)
104    pub timestamp: String,
105}
106
107/// Request headers event
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct RequestHeadersEvent {
110    /// Event metadata
111    pub metadata: RequestMetadata,
112    /// HTTP method
113    pub method: String,
114    /// Request URI
115    pub uri: String,
116    /// HTTP headers
117    pub headers: HashMap<String, Vec<String>>,
118}
119
120/// Request body chunk event
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct RequestBodyChunkEvent {
123    /// Correlation ID
124    pub correlation_id: String,
125    /// Body chunk data (base64 encoded for JSON transport)
126    pub data: String,
127    /// Is this the last chunk?
128    pub is_last: bool,
129    /// Total body size if known
130    pub total_size: Option<usize>,
131}
132
133/// Response headers event
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct ResponseHeadersEvent {
136    /// Correlation ID
137    pub correlation_id: String,
138    /// HTTP status code
139    pub status: u16,
140    /// HTTP headers
141    pub headers: HashMap<String, Vec<String>>,
142}
143
144/// Response body chunk event
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct ResponseBodyChunkEvent {
147    /// Correlation ID
148    pub correlation_id: String,
149    /// Body chunk data (base64 encoded for JSON transport)
150    pub data: String,
151    /// Is this the last chunk?
152    pub is_last: bool,
153    /// Total body size if known
154    pub total_size: Option<usize>,
155}
156
157/// Request complete event (for logging/audit)
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct RequestCompleteEvent {
160    /// Correlation ID
161    pub correlation_id: String,
162    /// Final HTTP status code
163    pub status: u16,
164    /// Request duration in milliseconds
165    pub duration_ms: u64,
166    /// Request body size
167    pub request_body_size: usize,
168    /// Response body size
169    pub response_body_size: usize,
170    /// Upstream attempts
171    pub upstream_attempts: u32,
172    /// Error if any
173    pub error: Option<String>,
174}
175
176/// Agent request message
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct AgentRequest {
179    /// Protocol version
180    pub version: u32,
181    /// Event type
182    pub event_type: EventType,
183    /// Event payload (JSON)
184    pub payload: serde_json::Value,
185}
186
187/// Agent response message
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct AgentResponse {
190    /// Protocol version
191    pub version: u32,
192    /// Decision
193    pub decision: Decision,
194    /// Header modifications for request
195    #[serde(default)]
196    pub request_headers: Vec<HeaderOp>,
197    /// Header modifications for response
198    #[serde(default)]
199    pub response_headers: Vec<HeaderOp>,
200    /// Routing metadata modifications
201    #[serde(default)]
202    pub routing_metadata: HashMap<String, String>,
203    /// Audit metadata
204    #[serde(default)]
205    pub audit: AuditMetadata,
206}
207
208impl AgentResponse {
209    /// Create a default allow response
210    pub fn default_allow() -> Self {
211        Self {
212            version: PROTOCOL_VERSION,
213            decision: Decision::Allow,
214            request_headers: vec![],
215            response_headers: vec![],
216            routing_metadata: HashMap::new(),
217            audit: AuditMetadata::default(),
218        }
219    }
220
221    /// Create a block response
222    pub fn block(status: u16, body: Option<String>) -> Self {
223        Self {
224            version: PROTOCOL_VERSION,
225            decision: Decision::Block {
226                status,
227                body,
228                headers: None,
229            },
230            request_headers: vec![],
231            response_headers: vec![],
232            routing_metadata: HashMap::new(),
233            audit: AuditMetadata::default(),
234        }
235    }
236
237    /// Create a redirect response
238    pub fn redirect(url: String, status: u16) -> Self {
239        Self {
240            version: PROTOCOL_VERSION,
241            decision: Decision::Redirect { url, status },
242            request_headers: vec![],
243            response_headers: vec![],
244            routing_metadata: HashMap::new(),
245            audit: AuditMetadata::default(),
246        }
247    }
248
249    /// Add a request header modification
250    pub fn add_request_header(mut self, op: HeaderOp) -> Self {
251        self.request_headers.push(op);
252        self
253    }
254
255    /// Add a response header modification
256    pub fn add_response_header(mut self, op: HeaderOp) -> Self {
257        self.response_headers.push(op);
258        self
259    }
260
261    /// Add audit metadata
262    pub fn with_audit(mut self, audit: AuditMetadata) -> Self {
263        self.audit = audit;
264        self
265    }
266}
267
268/// Audit metadata from agent
269#[derive(Debug, Clone, Default, Serialize, Deserialize)]
270pub struct AuditMetadata {
271    /// Tags for logging/metrics
272    #[serde(default)]
273    pub tags: Vec<String>,
274    /// Rule IDs that matched
275    #[serde(default)]
276    pub rule_ids: Vec<String>,
277    /// Confidence score (0.0 - 1.0)
278    pub confidence: Option<f32>,
279    /// Reason codes
280    #[serde(default)]
281    pub reason_codes: Vec<String>,
282    /// Custom metadata
283    #[serde(default)]
284    pub custom: HashMap<String, serde_json::Value>,
285}