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