circles/docs/aidocs/IMPLEMENTATION_PLAN.md
2025-07-09 23:39:48 +02:00

351 lines
10 KiB
Markdown

# Multi-Circle Single-Server Implementation Plan
## Overview
Transform the current launcher from a JSON-config based multi-server system to a command-line driven single-server system that handles multiple circles via path-based routing.
## Architecture Changes
### Current Architecture:
```
Launcher → circles.json → Multiple Servers (one per circle, different ports)
```
### New Architecture:
```
Launcher → CLI args → Single Server (one port, path-based routing)
```
## Phase 1: Update Launcher Interface
### 1.1 New Command Line Interface
```bash
# New launcher usage
cargo run --bin launcher -- --port 8080 --public-keys "pk1,pk2,pk3"
# or with multiple -k flags
cargo run --bin launcher -- --port 8080 -k "pk1" -k "pk2" -k "pk3"
```
### 1.2 Updated Args Structure
Replace current `Args` struct with:
```rust
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
pub struct Args {
/// Port for the WebSocket server
#[arg(short, long, default_value = "8080")]
pub port: u16,
/// Circle public keys (hex format, can be specified multiple times)
#[arg(short = 'k', long = "public-key")]
pub public_keys: Vec<String>,
/// Redis URL
#[arg(long, default_value = "redis://127.0.0.1:6379")]
pub redis_url: String,
/// Enable authentication
#[arg(long)]
pub enable_auth: bool,
/// Enable debug mode
#[arg(short, long)]
pub debug: bool,
/// Verbosity level
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
}
```
### 1.3 Remove Obsolete Code
- Remove `CircleConfig` struct
- Remove `SimpleArgs` struct
- Remove `run_simple_launcher` function
- Remove JSON parsing logic from `main.rs`
- Remove `simple.rs` binary entirely
## Phase 2: Multi-Circle Server Architecture
### 2.1 New Server Configuration
Update `ServerConfig` to support multiple circles:
```rust
#[derive(Clone)]
pub struct MultiCircleServerConfig {
pub host: String,
pub port: u16,
pub redis_url: String,
pub circle_public_keys: Vec<String>, // List of allowed circles
pub enable_auth: bool,
pub enable_tls: bool,
pub cert_path: Option<String>,
pub key_path: Option<String>,
}
```
### 2.2 Path-Based Routing
Update WebSocket routing from `/ws` to `/{circle_pk}`:
```rust
// New route pattern in spawn_circle_server
.route("/{circle_pk}", web::get().to(ws_handler))
// Updated handler signature
async fn ws_handler(
req: HttpRequest,
stream: web::Payload,
path: web::Path<String>, // circle_pk from URL
server_config: web::Data<MultiCircleServerConfig>,
) -> Result<HttpResponse, Error>
```
### 2.3 Circle Validation
Add validation logic in `ws_handler`:
```rust
let circle_pk = path.into_inner();
// Validate circle_pk is in allowed list
if !server_config.circle_public_keys.contains(&circle_pk) {
return Ok(HttpResponse::NotFound()
.json(json!({
"error": "Circle not found",
"message": format!("Circle '{}' is not available on this server", circle_pk)
})));
}
```
## Phase 3: Per-Circle Authentication
### 3.1 Update CircleWs Actor
Modify `CircleWs` to work with specific circle from URL:
```rust
struct CircleWs {
circle_public_key: String, // From URL path
redis_url: String,
nonce_store: HashMap<String, NonceResponse>,
auth_enabled: bool,
authenticated_pubkey: Option<String>,
}
impl CircleWs {
fn new_for_circle(
circle_public_key: String,
redis_url: String,
auth_enabled: bool,
) -> Self {
Self {
circle_public_key,
redis_url,
nonce_store: HashMap::new(),
auth_enabled,
authenticated_pubkey: None,
}
}
}
```
### 3.2 Circle-Specific Authentication
Update authentication logic in `handle_authenticate`:
- Authentication challenges are specific to the circle from URL path
- Signature verification uses the circle's public key context
- Each circle maintains separate authentication state
## Phase 4: Worker Communication Updates
### 4.1 Update Launcher Worker Spawning
Modify `setup_and_spawn_circles` to:
- Accept list of public keys instead of CircleConfig
- Spawn one worker per public key
- Launch single server instance with all public keys
### 4.2 Play Request Routing
Update `handle_play` to route to correct worker:
```rust
// Use circle_public_key from URL path for worker routing
rhai_dispatcher
.new_play_request()
.recipient_id(&self.circle_public_key) // From URL path
.script_path(&script_content)
.timeout(TASK_TIMEOUT_DURATION)
.await_response()
.await
```
## Phase 5: Updated Launcher Logic
### 5.1 New Setup Function
Replace `setup_and_spawn_circles` with:
```rust
pub async fn setup_multi_circle_server(
public_keys: Vec<String>,
port: u16,
redis_url: String,
enable_auth: bool,
) -> Result<(Vec<JoinHandle<_>>, ServerHandle), Box<dyn std::error::Error>>
```
### 5.2 Single Server Spawn
Launch one server instance that handles all circles:
```rust
let server_config = MultiCircleServerConfig {
host: "127.0.0.1".to_string(),
port,
redis_url: redis_url.clone(),
circle_public_keys: public_keys.clone(),
enable_auth,
enable_tls: false,
cert_path: None,
key_path: None,
};
let (server_task, server_handle) = spawn_multi_circle_server(server_config)?;
```
### 5.3 Worker Spawning Per Circle
Spawn one worker per public key:
```rust
let mut worker_handles = Vec::new();
for public_key in &public_keys {
let worker_handle = spawn_rhai_worker(
public_key.clone(),
public_key.clone(),
engine.clone(),
redis_url.clone(),
worker_shutdown_rx,
preserve_tasks,
);
worker_handles.push(worker_handle);
}
```
## Phase 6: File Structure Changes
### 6.1 Remove Files
- Delete `src/launcher/src/cmd/simple.rs`
- Remove simple binary from `Cargo.toml`
### 6.2 Update Main Binary
Update `src/launcher/src/cmd/main.rs` to use new CLI args instead of JSON config
### 6.3 Update Library Exports
Remove exports for:
- `SimpleArgs`
- `run_simple_launcher`
- `CircleConfig`
## Architecture Diagram
```mermaid
graph TB
CLI[Launcher CLI<br/>--port 8080<br/>-k pk1 -k pk2 -k pk3<br/>--enable-auth]
CLI --> L[Launcher Process]
L --> W1[Worker: pk1<br/>Redis Queue: rhai_tasks:pk1]
L --> W2[Worker: pk2<br/>Redis Queue: rhai_tasks:pk2]
L --> W3[Worker: pk3<br/>Redis Queue: rhai_tasks:pk3]
L --> S[Single Server Instance<br/>Port 8080<br/>MultiCircleServerConfig]
C1[Client 1] --> |wss://127.0.0.1:8080/pk1| S
C2[Client 2] --> |wss://127.0.0.1:8080/pk2| S
C3[Client 3] --> |wss://127.0.0.1:8080/pk3| S
S --> |Validate pk1 in allowed list| V1[Circle Validation]
S --> |Validate pk2 in allowed list| V2[Circle Validation]
S --> |Validate pk3 in allowed list| V3[Circle Validation]
V1 --> |Create CircleWs for pk1| WS1[CircleWs Actor: pk1]
V2 --> |Create CircleWs for pk2| WS2[CircleWs Actor: pk2]
V3 --> |Create CircleWs for pk3| WS3[CircleWs Actor: pk3]
WS1 --> |Route play requests| W1
WS2 --> |Route play requests| W2
WS3 --> |Route play requests| W3
subgraph "Redis Queues"
R1[rhai_tasks:pk1]
R2[rhai_tasks:pk2]
R3[rhai_tasks:pk3]
end
W1 <--> R1
W2 <--> R2
W3 <--> R3
subgraph "Authentication Per Circle"
A1[Auth State: pk1]
A2[Auth State: pk2]
A3[Auth State: pk3]
end
WS1 <--> A1
WS2 <--> A2
WS3 <--> A3
```
## Implementation Steps Summary
### Step 1: Update Launcher Args
- Replace `Args` struct with new CLI interface
- Remove JSON config dependencies
- Add public key validation
### Step 2: Create Multi-Circle Server Config
- Replace `ServerConfig` with `MultiCircleServerConfig`
- Support list of circle public keys
- Maintain TLS and auth options
### Step 3: Implement Path-Based Routing
- Update WebSocket route from `/ws` to `/{circle_pk}`
- Add circle validation before WebSocket upgrade
- Return HTTP 404 for invalid circles
### Step 4: Update CircleWs Actor
- Extract circle public key from URL path
- Implement per-circle authentication
- Route to correct worker based on circle
### Step 5: Update Launcher Logic
- Remove JSON parsing
- Spawn single server with multiple circles
- Spawn one worker per circle public key
### Step 6: Clean Up Code
- Remove `SimpleArgs`, `CircleConfig`, `run_simple_launcher`
- Delete simple binary
- Update exports and documentation
## Benefits of This Architecture
1. **Simplified Deployment**: Single command, single port, multiple circles
2. **Resource Efficiency**: Shared server infrastructure
3. **Clear Separation**: Per-circle authentication and worker routing
4. **Scalable**: Easy to add/remove circles via CLI
5. **Maintainable**: Less complex than multi-server approach
## Example Usage
```bash
# Launch server with 3 circles on port 8080 with auth enabled
cargo run --bin launcher -- \
--port 8080 \
--enable-auth \
-k "02a1b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789ab" \
-k "03b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789abc1" \
-k "02c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789abcd12"
# Clients connect to specific circles:
# wss://127.0.0.1:8080/02a1b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789ab
# wss://127.0.0.1:8080/03b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789abc1
# wss://127.0.0.1:8080/02c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789abcd12
```
## Key Requirements Satisfied
1.**Remove JSON config functionality** - Completely replaced with CLI args
2.**Accept list of circle public keys** - Via `-k` flags or comma-separated
3.**Single port for multiple circles** - One server handles all circles
4.**Path-based routing** - `wss://127.0.0.1:port/circle_pk`
5.**Per-circle authentication** - Auth against specific circle's public key
6.**Route to appropriate worker** - Based on circle public key from URL
7.**Use hex format everywhere** - Consistent secp256k1 hex encoding
8. ✅ **HTTP 404 for invalid circles** - Before WebSocket upgrade