add circles app and libraries
This commit is contained in:
137
docs/ARCHITECTURE.md
Normal file
137
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# 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.
|
126
docs/openrpc.json
Normal file
126
docs/openrpc.json
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"openrpc": "1.2.6",
|
||||
"info": {
|
||||
"title": "Circles RPC",
|
||||
"description": "A JSON-RPC API for interacting with a Circle, allowing script execution and authentication.",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"methods": [
|
||||
{
|
||||
"name": "fetch_nonce",
|
||||
"summary": "Fetches a cryptographic nonce for a given public key.",
|
||||
"params": [
|
||||
{
|
||||
"name": "pubkey",
|
||||
"description": "The client's public key.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "nonce_response",
|
||||
"description": "The cryptographic nonce to be signed.",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NonceResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "authenticate",
|
||||
"summary": "Authenticates the client using a signed nonce.",
|
||||
"params": [
|
||||
{
|
||||
"name": "credentials",
|
||||
"description": "The authentication credentials, including the public key and the signed nonce.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AuthCredentials"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "authentication_status",
|
||||
"description": "The result of the authentication attempt.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authenticated": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["authenticated"]
|
||||
}
|
||||
},
|
||||
"errors": [
|
||||
{
|
||||
"code": -32002,
|
||||
"message": "Invalid Credentials",
|
||||
"description": "The provided credentials were not valid."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "play",
|
||||
"summary": "Executes a Rhai script and returns the result.",
|
||||
"params": [
|
||||
{
|
||||
"name": "script",
|
||||
"description": "The Rhai script to execute.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "play_result",
|
||||
"description": "The output of the executed script.",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"errors": [
|
||||
{
|
||||
"code": -32000,
|
||||
"message": "Execution Error",
|
||||
"description": "The script failed to execute."
|
||||
},
|
||||
{
|
||||
"code": -32001,
|
||||
"message": "Authentication Required",
|
||||
"description": "The client must be authenticated to use this method."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"schemas": {
|
||||
"AuthCredentials": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pubkey": {
|
||||
"type": "string",
|
||||
"description": "The public key of the client."
|
||||
},
|
||||
"signature": {
|
||||
"type": "string",
|
||||
"description": "The nonce signed with the client's private key."
|
||||
}
|
||||
},
|
||||
"required": ["pubkey", "signature"]
|
||||
},
|
||||
"NonceResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nonce": {
|
||||
"type": "string",
|
||||
"description": "The single-use cryptographic nonce."
|
||||
}
|
||||
},
|
||||
"required": ["nonce"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user