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

7.3 KiB

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

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

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.