351 lines
10 KiB
Markdown
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 |