137 lines
7.3 KiB
Markdown
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. |