# System Architecture This document provides a detailed overview of the `circles` project architecture. The project is composed of two core library crates, `server_ws` and `client_ws`, and a convenient `launcher` utility. ## 1. High-Level Overview The `circles` project provides the core components for a client-server system designed to execute Rhai scripts in isolated environments. The `launcher` application is a utility that demonstrates how to use the `server_ws` and `client_ws` libraries to manage multiple server instances, but the libraries themselves are the fundamental building blocks. The core functionality revolves around: - **Orchestration**: The `launcher` starts and stops multiple, independent WebSocket servers. - **Client-Server Communication**: A JSON-RPC 2.0 API over WebSockets allows clients to execute scripts and authenticate. - **Authentication**: An optional, robust `secp256k1` signature-based authentication mechanism secures the script execution endpoint. ## 2. Component Architecture ### 2.1. `server_ws` (Library) The `server_ws` crate provides the WebSocket server that handles client connections and API requests. Its key features include: - **Web Framework**: Built using `Actix`, a powerful actor-based web framework for Rust. - **WebSocket Handling**: Uses `actix-web-actors` to manage individual WebSocket sessions. Each client connection is handled by a `CircleWs` actor, ensuring that sessions are isolated from one another. - **JSON-RPC API**: Exposes a JSON-RPC 2.0 API with methods for script execution (`play`) and authentication (`fetch_nonce`, `authenticate`). - **Authentication Service**: The authentication flow is handled entirely within the WebSocket connection using the dedicated JSON-RPC methods. ### 2.2. `client_ws` (Library) The `client_ws` crate is a WebSocket client library designed for interacting with the `server_ws`. It is engineered to be cross-platform: - **Native**: For native Rust applications, it uses `tokio-tungstenite` for WebSocket communication. - **WebAssembly (WASM)**: For browser-based applications, it uses `gloo-net` to integrate with the browser's native WebSocket API. - **API**: Provides a flexible builder pattern for client construction and a high-level API (`CircleWsClient`) that abstracts the complexities of the WebSocket connection and the JSON-RPC protocol. ### 2.3. `launcher` (Utility) The `launcher` is a command-line utility that demonstrates how to use the `server_ws` library. It is responsible for: - **Configuration**: Reading a `circles.json` file that defines a list of Circle instances to run. - **Orchestration**: Spawning a dedicated `server_ws` instance for each configured circle. - **Lifecycle Management**: Managing the lifecycle of all spawned servers and their associated Rhai workers. ### 2.2. `server_ws` The `server_ws` crate provides the WebSocket server that handles client connections and API requests. Its key features include: - **Web Framework**: Built using `Actix`, a powerful actor-based web framework for Rust. - **WebSocket Handling**: Uses `actix-web-actors` to manage individual WebSocket sessions. Each client connection is handled by a `CircleWs` actor, ensuring that sessions are isolated from one another. - **JSON-RPC API**: Exposes a JSON-RPC 2.0 API with methods for script execution (`play`) and authentication (`fetch_nonce`, `authenticate`). - **Authentication Service**: The authentication flow is handled entirely within the WebSocket connection using the dedicated JSON-RPC methods. ### 2.3. `client_ws` The `client_ws` crate is a WebSocket client library designed for interacting with the `server_ws`. It is engineered to be cross-platform: - **Native**: For native Rust applications, it uses `tokio-tungstenite` for WebSocket communication. - **WebAssembly (WASM)**: For browser-based applications, it uses `gloo-net` to integrate with the browser's native WebSocket API. - **API**: Provides a flexible builder pattern for client construction and a high-level API (`CircleWsClient`) that abstracts the complexities of the WebSocket connection and the JSON-RPC protocol. ## 3. Communication and Protocols ### 3.1. JSON-RPC 2.0 All client-server communication, including authentication, uses the JSON-RPC 2.0 protocol over the WebSocket connection. This provides a unified, lightweight, and well-defined structure for all interactions. The formal API contract is defined in the [openrpc.json](openrpc.json) file. ### 3.2. Authentication Flow The authentication mechanism is designed to verify that a client possesses the private key corresponding to a given public key, without ever exposing the private key. The entire flow happens over the established WebSocket connection. **Sequence of Events:** 1. **Keypair**: The client is instantiated with a `secp256k1` keypair. 2. **Nonce Request**: The client sends a `fetch_nonce` JSON-RPC request containing its public key. 3. **Nonce Issuance**: The server generates a unique, single-use nonce, stores it in the actor's state, and returns it to the client in a JSON-RPC response. 4. **Signature Creation**: The client signs the received nonce with its private key. 5. **Authentication Request**: The client sends an `authenticate` JSON-RPC message, containing the public key and the generated signature. 6. **Signature Verification**: The server's WebSocket actor retrieves the stored nonce for the given public key and cryptographically verifies the signature. 7. **Session Update**: If verification is successful, the server marks the client's WebSocket session as "authenticated," granting it access to protected methods like `play`. ## 4. Diagrams ### 4.1. System Component Diagram ```mermaid graph TD subgraph "User Machine" Launcher[🚀 launcher] CirclesConfig[circles.json] Launcher -- Reads --> CirclesConfig end subgraph "Spawned Processes" direction LR subgraph "Circle 1" Server1[🌐 server_ws on port 9001] end subgraph "Circle 2" Server2[🌐 server_ws on port 9002] end end Launcher -- Spawns & Manages --> Server1 Launcher -- Spawns & Manages --> Server2 subgraph "Clients" Client1[💻 client_ws] Client2[💻 client_ws] end Client1 -- Connects via WebSocket --> Server1 Client2 -- Connects via WebSocket --> Server2 ``` ### 4.2. Authentication Sequence Diagram ```mermaid sequenceDiagram participant Client as client_ws participant WsActor as CircleWs Actor (WebSocket) Client->>Client: Instantiate with keypair Note over Client: Has public_key, private_key Client->>+WsActor: JSON-RPC "fetch_nonce" (pubkey) WsActor->>WsActor: generate_nonce() WsActor->>WsActor: store_nonce(pubkey, nonce) WsActor-->>-Client: JSON-RPC Response ({"nonce": "..."}) Client->>Client: sign(nonce, private_key) Note over Client: Has signature Client->>+WsActor: JSON-RPC "authenticate" (pubkey, signature) WsActor->>WsActor: retrieve_nonce(pubkey) WsActor->>WsActor: verify_signature(nonce, signature, pubkey) alt Signature is Valid WsActor->>WsActor: Set session as authenticated WsActor-->>-Client: JSON-RPC Response ({"authenticated": true}) else Signature is Invalid WsActor-->>-Client: JSON-RPC Error (Invalid Credentials) end Note over WsActor: Subsequent "play" requests will include the authenticated public key.