# Circles: Peer-Centric Application Layer Architecture This document provides a comprehensive overview of the Circles project architecture - a **distributed, peer-contextual application layer runtime** where peers can send scripts to each other for execution within isolated contexts. ## 1. System Overview ### 1.1. Core Concept Circles implements a **peer-centric backend architecture** where each user is treated as an autonomous execution context rather than a passive database entry. The system enables **cross-peer script execution** where: - **Peer A** sends a script to **Peer B** - The script executes within **Peer B's context** (filesystem namespace, data isolation) - The script runs with awareness that **Peer A** initiated the execution - **Peer B** controls what scripts it accepts and how they execute ### 1.2. Key Characteristics - **Peer-Centric Execution**: Each peer is an isolated context, not a row in a global database - **Context-Based Routing**: Execution flows are scoped by peer identity (public keys) - **Decentralizable**: Circle nodes can be deployed independently and serve separate peer sets - **Script-Triggered Workflows**: Cross-peer interactions happen via script execution - **Namespace Isolation**: Workers enforce isolation through filesystem namespacing ## 2. Core Components ### 2.1. Peer Context A **peer context** represents an isolated execution environment identified by a secp256k1 public key: - **Identity**: Unique secp256k1 public key - **Filesystem Namespace**: Isolated root directory (`DB_PATH/{peer_public_key}/`) - **Execution Environment**: Rhai script engine with peer-specific variables - **Policy Boundaries**: Controls which peers can execute scripts and what operations are allowed ### 2.2. Circle (WebSocket Gateway) The [`server`](src/server) crate provides the WebSocket server that acts as a gateway for peer interactions: - **Peer Authentication**: secp256k1 signature-based authentication via JSON-RPC - **Script Routing**: Routes incoming script execution requests to Redis queues - **Session Management**: Maintains authenticated WebSocket connections per peer - **Multi-Peer Support**: Single server instance can handle multiple peer contexts **Key Features:** - Built on Actix Web with WebSocket actors ([`CircleWs`](src/server/src/lib.rs:100)) - JSON-RPC 2.0 API for authentication and script execution - Redis integration for decoupled worker communication ### 2.3. Worker Engine The [`worker`](src/worker) crate implements the script execution engine: - **Context-Aware Execution**: Loads and executes scripts within specific peer contexts - **Namespace Isolation**: Each peer gets isolated filesystem access - **Caller Awareness**: Scripts execute with knowledge of both caller and target peer - **Multiplexing Support**: Single worker can serve multiple peer contexts **Execution Environment Variables:** ```rhai CALLER_PUBLIC_KEY // The peer who sent the script CIRCLE_PUBLIC_KEY // The peer in whose context the script runs DB_PATH // Namespaced database path for this peer ``` ### 2.4. Client Library The [`client_ws`](src/client_ws) crate provides cross-platform WebSocket client functionality: - **Cross-Platform**: Native (tokio-tungstenite) and WASM (gloo-net) support - **Authentication**: Automated secp256k1 signature-based authentication - **Script Execution**: High-level API for sending scripts to other peers - **Builder Pattern**: Flexible client construction with optional authentication ## 3. Communication Topology ### 3.1. Peer-to-Peer Script Execution Flow ```mermaid sequenceDiagram participant PeerA as Peer A (Client) participant Circle as Circle Node (WebSocket) participant Redis as Redis Queue participant Worker as Worker Engine participant FS as Peer B Filesystem Note over PeerA,FS: Peer A sends script to execute in Peer B's context PeerA->>Circle: WebSocket: JSON-RPC "play" request Note right of PeerA: Script + Target Peer B's public key Circle->>Circle: Validate authentication Circle->>Redis: LPUSH to peer B's queue Note right of Circle: Queue: rhailib:{peer_b_pubkey} Worker->>Redis: BLPOP from peer B's queue Worker->>Worker: Load Peer B's context Note right of Worker: Set CALLER_PUBLIC_KEY=peer_a
Set CIRCLE_PUBLIC_KEY=peer_b
Set DB_PATH=peer_b_namespace/ Worker->>FS: Execute script in Peer B's namespace FS-->>Worker: Script execution result Worker->>Redis: LPUSH result to reply queue Redis-->>Circle: Result notification Circle-->>PeerA: WebSocket: JSON-RPC response ``` ### 3.2. System Component Diagram ```mermaid graph TB subgraph "Peer A Environment" ClientA[Client A
client_ws] KeyA[Private Key A] ClientA -.-> KeyA end subgraph "Peer B Environment" ClientB[Client B
client_ws] KeyB[Private Key B] ClientB -.-> KeyB end subgraph "Circle Infrastructure" Circle[Circle Node
server] Redis[(Redis
Task Queues)] Worker1[Worker Engine
Peer A Context] Worker2[Worker Engine
Peer B Context] Circle <--> Redis Redis <--> Worker1 Redis <--> Worker2 end subgraph "Isolated Storage" FSA[Peer A Namespace
DB_PATH/peer_a/] FSB[Peer B Namespace
DB_PATH/peer_b/] Worker1 <--> FSA Worker2 <--> FSB end ClientA <-->|WebSocket
Authenticated| Circle ClientB <-->|WebSocket
Authenticated| Circle style ClientA fill:#e1f5fe style ClientB fill:#e8f5e8 style Circle fill:#fff3e0 style Worker1 fill:#e1f5fe style Worker2 fill:#e8f5e8 ``` ### 3.3. Multi-Circle Deployment Architecture ```mermaid graph TB subgraph "Launcher Configuration" Launcher[Launcher
circles.json] Config[["OurWorld: port 8090
Threefold: port 8091
Sikana: port 8092
..."]] Launcher --> Config end subgraph "Circle Nodes" Circle1[Circle 1
:8090] Circle2[Circle 2
:8091] Circle3[Circle 3
:8092] end subgraph "Worker Pool" Worker1[Worker
OurWorld Context] Worker2[Worker
Threefold Context] Worker3[Worker
Sikana Context] end subgraph "Shared Infrastructure" Redis[(Redis
Coordination)] Storage[(Namespaced Storage
peer_contexts/)] end Launcher --> Circle1 Launcher --> Circle2 Launcher --> Circle3 Circle1 <--> Redis Circle2 <--> Redis Circle3 <--> Redis Redis <--> Worker1 Redis <--> Worker2 Redis <--> Worker3 Worker1 <--> Storage Worker2 <--> Storage Worker3 <--> Storage ``` ## 4. Authentication and Security ### 4.1. Peer Authentication Flow ```mermaid sequenceDiagram participant Client as Peer Client participant Circle as Circle Node Note over Client: Has secp256k1 keypair Client->>+Circle: JSON-RPC "fetch_nonce" (pubkey) Circle->>Circle: generate_nonce() Circle->>Circle: store_nonce(pubkey, nonce) Circle-->>-Client: nonce + expiration Client->>Client: sign(nonce, private_key) Client->>+Circle: JSON-RPC "authenticate" (pubkey, signature) Circle->>Circle: verify_signature(nonce, signature, pubkey) alt Signature Valid Circle->>Circle: Mark session as authenticated Circle-->>Client: {"authenticated": true} Note over Circle: Subsequent "play" requests include authenticated pubkey else Signature Invalid Circle-->>-Client: JSON-RPC Error: Invalid Credentials Circle->>Circle: Close connection end ``` ### 4.2. Context Isolation Model Each peer context is isolated through: 1. **Filesystem Namespacing**: Each peer gets a dedicated root directory 2. **Environment Variables**: Scripts execute with peer-specific context variables 3. **Redis Queue Isolation**: Each peer has dedicated task queues 4. **Authentication Boundaries**: Only authenticated peers can send scripts ``` worker_rhai_temp_db/ ├── peer_a_public_key/ # Peer A's isolated namespace │ ├── data/ │ ├── config/ │ └── logs/ ├── peer_b_public_key/ # Peer B's isolated namespace │ ├── data/ │ ├── config/ │ └── logs/ └── ... ``` ## 5. Script Execution Model ### 5.1. Cross-Peer Script Execution When Peer A sends a script to Peer B: 1. **Authentication**: Peer A authenticates with their private key 2. **Script Routing**: Circle routes the script to Peer B's Redis queue 3. **Context Loading**: Worker loads Peer B's execution context 4. **Environment Setup**: Worker sets context variables: ```rhai CALLER_PUBLIC_KEY = "peer_a_public_key" CIRCLE_PUBLIC_KEY = "peer_b_public_key" DB_PATH = "/path/to/peer_b_namespace" ``` 5. **Script Execution**: Rhai engine executes script in Peer B's context 6. **Result Return**: Execution result is returned to Peer A via Redis/WebSocket ### 5.2. Example Script Execution ```rhai // Script sent by Peer A to Peer B print("Script running in context of: " + CIRCLE_PUBLIC_KEY); print("Initiated by: " + CALLER_PUBLIC_KEY); // Access Peer B's data (isolated namespace) let peer_b_data = load_data("user_preferences.json"); // Perform operations in Peer B's context let result = process_data(peer_b_data); // Return result to Peer A result ``` ## 6. API Reference ### 6.1. JSON-RPC Methods The Circle WebSocket API provides three core methods: #### `fetch_nonce` Requests a cryptographic nonce for authentication. **Parameters:** - `pubkey` (string): Client's secp256k1 public key **Response:** ```json { "nonce": "base64_encoded_nonce", "expires_at": 1234567890 } ``` #### `authenticate` Authenticates the client using a signed nonce. **Parameters:** - `pubkey` (string): Client's public key - `signature` (string): Nonce signed with client's private key **Response:** ```json { "authenticated": true } ``` #### `play` Executes a script in a target peer's context. **Parameters:** - `script` (string): Rhai script to execute **Response:** ```json { "output": "script_execution_result" } ``` ### 6.2. Client Library Usage ```rust use circle_client_ws::{CircleWsClientBuilder, CircleWsClient}; // Create authenticated client let mut client = CircleWsClientBuilder::new("ws://localhost:8090/peer_b_pubkey".to_string()) .with_keypair(private_key) .build(); // Connect and authenticate client.connect().await?; client.authenticate().await?; // Send script to execute in peer B's context let result = client.play(r#" print("Hello from " + CALLER_PUBLIC_KEY + " to " + CIRCLE_PUBLIC_KEY); "Script executed successfully" "#.to_string()).await?; println!("Result: {}", result.output); ``` ## 7. Deployment Models ### 7.1. Single-Peer Worker Each peer runs in its own dedicated worker process: ```bash # Start worker for specific peer ./worker --circle-public-key peer_a_public_key --redis-url redis://localhost:6379 ``` ### 7.2. Multi-Peer Worker Single worker serves multiple peer contexts with namespace isolation: ```bash # Worker handles multiple peers with context switching ./worker --redis-url redis://localhost:6379 ``` ### 7.3. Launcher-Managed Deployment Use the launcher to manage multiple circles: ```json [ { "name": "OurWorld", "port": 8090, "script_path": "scripts/ourworld.rhai" }, { "name": "Threefold", "port": 8091, "script_path": "scripts/threefold.rhai" } ] ``` ```bash ./launcher --config circles.json ``` ## 8. Technical Implementation Details ### 8.1. Redis Protocol **Task Queue Pattern:** - Queue Key: `rhailib:{peer_public_key}` - Task Details: `rhailib:{task_id}` (hash) - Reply Queue: `rhailib:reply:{task_id}` **Task Lifecycle:** 1. Client submits script → Redis queue 2. Worker picks up task → Updates status to "processing" 3. Worker executes script → Updates status to "completed"/"error" 4. Worker publishes result → Reply queue 5. Client receives result → Task cleanup (optional) ### 8.2. Worker Context Management ```rust // Worker sets up peer context before script execution let mut db_config = rhai::Map::new(); db_config.insert("DB_PATH".into(), db_path.clone().into()); db_config.insert("CALLER_PUBLIC_KEY".into(), caller_id.clone().into()); db_config.insert("CIRCLE_PUBLIC_KEY".into(), circle_public_key.clone().into()); engine.set_default_tag(Dynamic::from(db_config)); ``` ### 8.3. Filesystem Isolation Each peer context gets an isolated filesystem namespace: ```rust // Example namespace structure let peer_namespace = format!("{}/{}", base_db_path, peer_public_key); std::fs::create_dir_all(&peer_namespace)?; // All file operations are relative to this namespace let data_file = format!("{}/data/user_data.json", peer_namespace); ``` ## 9. Comparison to Traditional Architectures | Feature | Traditional Backend | Circles (Peer-Centric) | |---------|-------------------|------------------------| | User Representation | Row in database | Autonomous execution context | | Request Routing | Global endpoints | Peer-scoped script execution | | State Management | Shared application state | Isolated peer contexts | | Communication | HTTP/REST, JSON-RPC | WebSocket + Redis coordination | | Distribution | Centralized | Decentralized, per-peer scalable | | Execution Model | Threaded request handlers | Context-aware script processors | | Data Isolation | Application-level | Filesystem namespacing | | Cross-User Operations | Database transactions | Cross-peer script execution | ## 10. Summary Circles implements a **distributed, peer-contextual application layer runtime** where: - Each peer is an isolated execution context identified by secp256k1 public keys - Peers can send scripts to other peers for execution within the target's context - Workers enforce isolation through filesystem namespacing and environment variables - The system supports both single-peer and multi-peer worker deployments - Communication flows through WebSocket gateways coordinated via Redis queues This architecture enables **decentralized application logic** where peers maintain autonomy over their execution environments while supporting secure cross-peer interactions through script-based workflows.