circles/docs/ARCHITECTURE.md
2025-06-19 05:17:14 +03:00

137 lines
7.3 KiB
Markdown

# 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.