circles/ARCHITECTURE.md
2025-06-05 00:29:10 +03:00

11 KiB

Architecture: Circle Management System

1. Introduction & Overview

This document outlines the architecture for a system that manages multiple "Circles." Each Circle is an independent entity comprising its own database, a Rhai scripting engine, a dedicated Rhai worker process, and a WebSocket (WS) server for external interaction. A central command-line orchestrator will be responsible for initializing, running, and monitoring these Circles based on a configuration file.

The primary goal is to allow users to define multiple isolated Rhai environments, each accessible via a unique WebSocket endpoint, and for scripts executed within a circle to interact with that circle's dedicated persistent storage.

2. Goals

  • Create a command-line application (circles_orchestrator) to manage the lifecycle of multiple Circles.
  • Each Circle will have:
    • An independent OurDB instance for data persistence.
    • A dedicated Rhai Engine configured with its OurDB.
    • A dedicated Rhai Worker processing scripts for that circle.
    • A dedicated WebSocket Server exposing an endpoint for that circle.
  • Circle configurations (name, ID, port) will be loaded from a circles.json file.
  • The orchestrator will display a status table of all running circles, including their worker queue and WS server URL.
  • Utilize existing crates (ourdb, rhai_engine, rhai_worker, rhai_client, server_ws) with necessary refactoring to support library usage.

3. System Components

  • Orchestrator (circles_orchestrator):
    • Location: /Users/timurgordon/code/git.ourworld.tf/herocode/circles/cmd/src/main.rs
    • Role: Parses circles.json, initializes, spawns, and monitors all components for each defined circle. Displays system status.
  • Circle Configuration (circles.json):
    • Location: e.g., /Users/timurgordon/code/git.ourworld.tf/herocode/circles/cmd/circles.json
    • Role: Defines the set of circles to be managed, including their ID, name, and port.
  • OurDB (Per Circle):
    • Library: /Users/timurgordon/code/git.ourworld.tf/herocode/db/ourdb/src/lib.rs
    • Role: Provides persistent key-value storage for each circle, configured in incremental mode. Instance data stored at ~/.hero/circles/{id}/.
  • Rhai Engine (Per Circle):
    • Library: /Users/timurgordon/code/git.ourworld.tf/herocode/rhailib/src/engine/src/lib.rs
    • Role: Provides the Rhai scripting environment for a circle, configured with the circle's specific OurDB instance.
  • Rhai Worker (Per Circle):
    • Library: /Users/timurgordon/code/git.ourworld.tf/herocode/rhailib/src/worker/src/lib.rs
    • Role: Executes Rhai scripts for a specific circle. Listens on a dedicated Redis queue for tasks and uses the circle's Rhai Engine.
  • Rhai Client (Per Circle WS Server):
    • Library: /Users/timurgordon/code/git.ourworld.tf/herocode/rhailib/src/client/src/lib.rs
    • Role: Used by a Circle WebSocket Server to send script execution tasks to its corresponding Rhai Worker via Redis.
  • Circle WebSocket Server (Per Circle):
    • Library: /Users/timurgordon/code/git.ourworld.tf/herocode/circles/server_ws/src/lib.rs
    • Role: Exposes a WebSocket endpoint for a specific circle, allowing external clients to submit Rhai scripts for execution within that circle.
  • Redis:
    • URL: redis://127.0.0.1:6379
    • Role: Acts as the message broker between Rhai Clients (within WS Servers) and Rhai Workers.

4. High-Level Design Aspects

4.1. Orchestrator Logic (Conceptual)

The circles_orchestrator reads the circles.json configuration. For each defined circle, it:

  1. Determines the OurDB path based on the circle's ID.
  2. Initializes the OurDB instance.
  3. Creates a Rhai Engine configured with this OurDB.
  4. Spawns a Rhai Worker task, providing it with the engine and the circle's identity (for Redis queue naming).
  5. Spawns a Circle WebSocket Server task, providing it with the circle's identity and port. It then monitors these components and displays their status.

4.2. Database Path Convention

  • Base: System user's home directory (e.g., /Users/username or /home/username).
  • Structure: ~/.hero/circles/{CIRCLE_ID}/
  • Example: For a circle with ID 1, the database path would be ~/.hero/circles/1/.

4.3. Configuration File Format (circles.json)

A JSON array of objects. Each object represents a circle:

  • id: u32 - Unique identifier.
  • name: String - Human-readable name.
  • port: u16 - Port for the WebSocket server.
[
  { "id": 1, "name": "Alpha Circle", "port": 8081 },
  { "id": 2, "name": "Beta Circle", "port": 8082 }
]

4.4. Conceptual Output Table Format (Orchestrator)

Circle Name ID Worker Status Worker Queues WS Server URL
Alpha Circle 1 Running rhai_tasks:alpha_circle ws://127.0.0.1:8081/ws

4.5. Interaction Flow (Single Circle)

  1. An external client connects to a specific Circle's WebSocket Server.
  2. The client sends a Rhai script via a JSON-RPC message.
  3. The WS Server uses its embedded Rhai Client to publish the script and task details to a Redis queue specific to that circle (e.g., rhai_tasks:alpha_circle).
  4. The Rhai Worker for that circle picks up the task from its Redis queue.
  5. The Worker uses its Rhai Engine (which is configured with the circle's OurDB) to execute the script.
  6. Any database interactions within the script go through the circle's OurDB.
  7. The Worker updates the task status and result/error in Redis.
  8. The Rhai Client (in the WS Server), which has been polling Redis for the result, receives the update.
  9. The WS Server sends the script's result or error back to the external client via WebSocket.

5. Diagrams

5.1. Component Diagram

graph TD
    UserInterface[User/Admin] -- Manages --> OrchestratorCli{circles_orchestrator}
    OrchestratorCli -- Reads --> CirclesJson[circles.json]

    subgraph Circle 1
        direction LR
        OrchestratorCli -- Spawns/Manages --> C1_OurDB[(OurDB @ ~/.hero/circles/1)]
        OrchestratorCli -- Spawns/Manages --> C1_RhaiEngine[Rhai Engine 1]
        OrchestratorCli -- Spawns/Manages --> C1_RhaiWorker[Rhai Worker 1]
        OrchestratorCli -- Spawns/Manages --> C1_WSServer[WS Server 1 @ Port 8081]

        C1_RhaiEngine -- Uses --> C1_OurDB
        C1_RhaiWorker -- Uses --> C1_RhaiEngine
        C1_WSServer -- Contains --> C1_RhaiClient[Rhai Client 1]
    end

    subgraph Circle 2
        direction LR
        OrchestratorCli -- Spawns/Manages --> C2_OurDB[(OurDB @ ~/.hero/circles/2)]
        OrchestratorCli -- Spawns/Manages --> C2_RhaiEngine[Rhai Engine 2]
        OrchestratorCli -- Spawns/Manages --> C2_RhaiWorker[Rhai Worker 2]
        OrchestratorCli -- Spawns/Manages --> C2_WSServer[WS Server 2 @ Port 8082]

        C2_RhaiEngine -- Uses --> C2_OurDB
        C2_RhaiWorker -- Uses --> C2_RhaiEngine
        C2_WSServer -- Contains --> C2_RhaiClient[Rhai Client 2]
    end

    C1_RhaiWorker -- Listens/Publishes --> Redis[(Redis @ 127.0.0.1:6379)]
    C1_RhaiClient -- Publishes/Subscribes --> Redis
    C2_RhaiWorker -- Listens/Publishes --> Redis
    C2_RhaiClient -- Publishes/Subscribes --> Redis

    ExternalWSClient1[External WS Client] -- Connects --> C1_WSServer
    ExternalWSClient2[External WS Client] -- Connects --> C2_WSServer

5.2. Sequence Diagram (Request Flow for one Circle)

sequenceDiagram
    participant ExtWSClient as External WS Client
    participant CircleWSServer as Circle WS Server (e.g., Port 8081)
    participant RhaiClientLib as Rhai Client Library (in WS Server)
    participant RedisBroker as Redis
    participant RhaiWorker as Rhai Worker (for the circle)
    participant RhaiEngineLib as Rhai Engine Library (in Worker)
    participant CircleOurDB as OurDB (for the circle)

    ExtWSClient ->>+ CircleWSServer: Send Rhai Script (JSON-RPC "play" over WS)
    CircleWSServer ->>+ RhaiClientLib: submit_script_and_await_result(circle_name, script, ...)
    RhaiClientLib ->>+ RedisBroker: LPUSH rhai_tasks:circle_name (task_id)
    RhaiClientLib ->>+ RedisBroker: HSET rhai_task_details:task_id (script details)
    RhaiClientLib -->>- CircleWSServer: Returns task_id (internally starts polling)

    RhaiWorker ->>+ RedisBroker: BLPOP rhai_tasks:circle_name (blocks)
    RedisBroker -->>- RhaiWorker: Returns task_id
    RhaiWorker ->>+ RedisBroker: HGETALL rhai_task_details:task_id
    RedisBroker -->>- RhaiWorker: Returns script details
    RhaiWorker ->>+ RhaiEngineLib: eval_with_scope(script)
    RhaiEngineLib ->>+ CircleOurDB: DB Operations (if script interacts with DB)
    CircleOurDB -->>- RhaiEngineLib: DB Results
    RhaiEngineLib -->>- RhaiWorker: Script Result/Error
    RhaiWorker ->>+ RedisBroker: HSET rhai_task_details:task_id (status="completed/error", output/error)

    RhaiClientLib ->>+ RedisBroker: HGETALL rhai_task_details:task_id (polling)
    RedisBroker -->>- RhaiClientLib: Task details (status, output/error)
    alt Task Completed
        RhaiClientLib -->>- CircleWSServer: Result (output)
        CircleWSServer -->>- ExtWSClient: WS Response (JSON-RPC with result)
    else Task Errored
        RhaiClientLib -->>- CircleWSServer: Error (error message)
        CircleWSServer -->>- ExtWSClient: WS Response (JSON-RPC with error)
    else Timeout
         RhaiClientLib -->>- CircleWSServer: Timeout Error
         CircleWSServer -->>- ExtWSClient: WS Response (JSON-RPC with timeout error)
    end

5.3. Conceptual Directory Structure

/Users/timurgordon/code/git.ourworld.tf/herocode/
├── circles/
│   ├── cmd/  <-- NEW Orchestrator Crate
│   │   ├── Cargo.toml
│   │   ├── circles.json (example config)
│   │   └── src/
│   │       └── main.rs (Orchestrator logic)
│   ├── server_ws/  <-- EXISTING, to be refactored to lib
│   │   ├── Cargo.toml
│   │   └── src/
│   │       └── lib.rs (WebSocket server library logic)
│   ├── ARCHITECTURE.md (This document)
│   └── README.md
├── db/
│   └── ourdb/
│       └── src/lib.rs (Existing OurDB library)
└── rhailib/  <-- Current Workspace (contains other existing libs)
    ├── src/
    │   ├── client/
    │   │   └── src/lib.rs (Existing Rhai Client library)
    │   ├── engine/
    │   │   └── src/lib.rs (Existing Rhai Engine library)
    │   └── worker/
    │       └── src/lib.rs (Existing Rhai Worker library, to be refactored)
    ├── Cargo.toml
    └── ...