# WebSocket Signing Server Architecture Plan Based on my analysis of the existing Actix application structure, I've designed a comprehensive architecture for implementing a WebSocket server that handles signing operations. This server will integrate seamlessly with the existing hostbasket application. ## 1. Overview The WebSocket Signing Server will: - Accept WebSocket connections from clients - Allow clients to identify themselves with a public key - Provide a `send_to_sign()` function that takes a public key and a message - Forward the message to the appropriate client for signing - Wait for a signed response (with a 1-minute timeout) - Verify the signature using the client's public key - Return the response message and signature ## 2. Component Architecture ```mermaid graph TD A[Actix Web Server] --> B[WebSocket Manager] B --> C[Connection Registry] B --> D[Message Handler] D --> E[Signature Verifier] F[Client] <--> B G[Controllers] --> H[SigningService] H --> B ``` ### Key Components: 1. **WebSocket Manager** - Handles WebSocket connections - Manages connection lifecycle - Routes messages to appropriate handlers 2. **Connection Registry** - Maps public keys to active WebSocket connections - Handles connection tracking and cleanup - Provides lookup functionality 3. **Message Handler** - Processes incoming messages - Implements the message protocol - Manages timeouts for responses 4. **Signature Verifier** - Verifies signatures using public keys - Implements cryptographic operations - Ensures security of the signing process 5. **Signing Service** - Provides a clean API for controllers to use - Abstracts WebSocket complexity from business logic - Handles error cases and timeouts ## 3. Directory Structure ``` src/ ├── websocket/ │ ├── mod.rs # Module exports │ ├── manager.rs # WebSocket connection manager │ ├── registry.rs # Connection registry │ ├── handler.rs # Message handling logic │ ├── protocol.rs # Message protocol definitions │ ├── crypto.rs # Cryptographic operations │ └── service.rs # Service API for controllers ├── controllers/ │ └── [existing controllers] │ └── websocket.rs # WebSocket controller (if needed) └── routes/ └── mod.rs # Updated to include WebSocket routes ``` ## 4. Data Flow ```mermaid sequenceDiagram participant Client participant WebSocketManager participant Registry participant Controller participant SigningService Client->>WebSocketManager: Connect Client->>WebSocketManager: Introduce(public_key) WebSocketManager->>Registry: Register(connection, public_key) Controller->>SigningService: send_to_sign(public_key, message) SigningService->>Registry: Lookup(public_key) Registry-->>SigningService: connection SigningService->>WebSocketManager: Send message to connection WebSocketManager->>Client: Message to sign Client->>WebSocketManager: Signed response WebSocketManager->>SigningService: Forward response SigningService->>SigningService: Verify signature SigningService-->>Controller: Return verified response ``` ## 5. Message Protocol We'll define a simple JSON-based protocol for communication: ```json // Client introduction { "type": "introduction", "public_key": "base64_encoded_public_key" } // Sign request { "type": "sign_request", "message": "base64_encoded_message", "request_id": "unique_request_id" } // Sign response { "type": "sign_response", "request_id": "unique_request_id", "message": "base64_encoded_message", "signature": "base64_encoded_signature" } ``` ## 6. Required Dependencies We'll need to add the following dependencies to the project: ```toml # WebSocket support actix-web-actors = "4.2.0" # Cryptography ed25519-dalek = "2.0.0" # For Ed25519 signatures base64 = "0.21.0" # For encoding/decoding rand = "0.8.5" # For generating random data ``` ## 7. Implementation Details ### 7.1 WebSocket Manager The WebSocket Manager will handle the lifecycle of WebSocket connections: ```rust pub struct WebSocketManager { registry: Arc>, } impl Actor for WebSocketManager { type Context = ws::WebsocketContext; fn started(&mut self, ctx: &mut Self::Context) { // Handle connection start } fn stopped(&mut self, ctx: &mut Self::Context) { // Handle connection close and cleanup } } impl StreamHandler> for WebSocketManager { fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { // Handle different types of WebSocket messages } } ``` ### 7.2 Connection Registry The Connection Registry will maintain a mapping of public keys to active connections: ```rust pub struct ConnectionRegistry { connections: HashMap>, } impl ConnectionRegistry { pub fn register(&mut self, public_key: String, addr: Addr) { self.connections.insert(public_key, addr); } pub fn unregister(&mut self, public_key: &str) { self.connections.remove(public_key); } pub fn get(&self, public_key: &str) -> Option<&Addr> { self.connections.get(public_key) } } ``` ### 7.3 Signing Service The Signing Service will provide a clean API for controllers: ```rust pub struct SigningService { registry: Arc>, } impl SigningService { pub async fn send_to_sign(&self, public_key: &str, message: &[u8]) -> Result<(Vec, Vec), SigningError> { // 1. Find the connection for the public key let connection = self.registry.read().await.get(public_key).cloned(); if let Some(conn) = connection { // 2. Generate a unique request ID let request_id = Uuid::new_v4().to_string(); // 3. Create a response channel let (tx, rx) = oneshot::channel(); // 4. Register the response channel self.pending_requests.write().await.insert(request_id.clone(), tx); // 5. Send the message to the client let sign_request = SignRequest { request_id: request_id.clone(), message: message.to_vec(), }; conn.do_send(sign_request); // 6. Wait for the response with a timeout match tokio::time::timeout(Duration::from_secs(60), rx).await { Ok(Ok(response)) => { // 7. Verify the signature if self.verify_signature(&response.signature, message, public_key) { Ok((response.message, response.signature)) } else { Err(SigningError::InvalidSignature) } }, Ok(Err(_)) => Err(SigningError::ChannelClosed), Err(_) => Err(SigningError::Timeout), } } else { Err(SigningError::ConnectionNotFound) } } } ``` ## 8. Error Handling We'll define a comprehensive error type for the signing service: ```rust #[derive(Debug, thiserror::Error)] pub enum SigningError { #[error("Connection not found for the provided public key")] ConnectionNotFound, #[error("Timeout waiting for signature")] Timeout, #[error("Invalid signature")] InvalidSignature, #[error("Channel closed unexpectedly")] ChannelClosed, #[error("WebSocket error: {0}")] WebSocketError(#[from] ws::ProtocolError), #[error("Serialization error: {0}")] SerializationError(#[from] serde_json::Error), } ``` ## 9. Security Considerations 1. **Public Key Validation**: Validate public keys upon connection to ensure they are properly formatted 2. **Message Authentication**: Consider adding a nonce or timestamp to prevent replay attacks 3. **Rate Limiting**: Implement rate limiting to prevent DoS attacks 4. **Connection Timeouts**: Automatically close inactive connections 5. **Error Logging**: Log errors but avoid exposing sensitive information 6. **Input Validation**: Validate all inputs to prevent injection attacks ## 10. Testing Strategy 1. **Unit Tests**: Test individual components in isolation 2. **Integration Tests**: Test the interaction between components 3. **End-to-End Tests**: Test the complete flow from client connection to signature verification 4. **Load Tests**: Test the system under high load to ensure stability 5. **Security Tests**: Test for common security vulnerabilities ## 11. Integration with Existing Controllers Controllers can use the SigningService through dependency injection: ```rust pub struct SomeController { signing_service: Arc, } impl SomeController { pub async fn some_action(&self, public_key: String, message: Vec) -> HttpResponse { match self.signing_service.send_to_sign(&public_key, &message).await { Ok((response, signature)) => { HttpResponse::Ok().json(json!({ "response": base64::encode(response), "signature": base64::encode(signature), })) }, Err(e) => { HttpResponse::InternalServerError().json(json!({ "error": e.to_string(), })) } } } } ``` ## 12. Deployment Considerations 1. **Scalability**: The WebSocket server should be designed to scale horizontally 2. **Monitoring**: Implement metrics for connection count, message throughput, and error rates 3. **Logging**: Log important events for debugging and auditing 4. **Documentation**: Document the API and protocol for client implementers