implement stripe and idenfy webhooks support
This commit is contained in:
351
docs/aidocs/IMPLEMENTATION_PLAN.md
Normal file
351
docs/aidocs/IMPLEMENTATION_PLAN.md
Normal file
@@ -0,0 +1,351 @@
|
||||
# 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_client
|
||||
.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
|
1
docs/aidocs/README.md
Normal file
1
docs/aidocs/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Docs generated by ai that need to be organized.
|
171
docs/aidocs/URL_ROUTING_STRATEGY.md
Normal file
171
docs/aidocs/URL_ROUTING_STRATEGY.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# URL and History Support Implementation Strategy
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the strategy for implementing clean URL and history support in the Circles application, starting with the LibraryView component. The goal is to enable component state changes to trigger URL updates and allow initial component state to be calculated from URLs for link sharing and refresh support.
|
||||
|
||||
## Current State Analysis
|
||||
|
||||
### Strengths
|
||||
- App-level routing already implemented with `AppView::to_path()` and `AppView::from_path()`
|
||||
- Browser history API integration in `App::update()`
|
||||
- Query parameter support for circles context (`?circles=url1,url2`)
|
||||
|
||||
### Gaps
|
||||
- Component-level state changes don't update URLs
|
||||
- Initial component state isn't derived from URLs
|
||||
- No support for nested routing (e.g., `/library/collection/123/item/456`)
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Browser URL] --> B[App Router]
|
||||
B --> C[AppView Route Parser]
|
||||
C --> D[Component Route Parser]
|
||||
D --> E[Component State]
|
||||
E --> F[Component Renders]
|
||||
F --> G[User Interaction]
|
||||
G --> H[State Change]
|
||||
H --> I[URL Update]
|
||||
I --> A
|
||||
|
||||
subgraph "URL Structure"
|
||||
J["/library/collection/123/item/456?circles=ws1,ws2"]
|
||||
K["Path: /library/collection/123/item/456"]
|
||||
L["Query: ?circles=ws1,ws2"]
|
||||
end
|
||||
```
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### Phase 1: Create URL Routing Infrastructure
|
||||
|
||||
#### 1.1 Router Trait Definition
|
||||
Create a reusable `UrlRouter` trait that components can implement:
|
||||
|
||||
```rust
|
||||
pub trait UrlRouter {
|
||||
type RouteState;
|
||||
fn parse_route(path: &str) -> Option<Self::RouteState>;
|
||||
fn build_route(state: &Self::RouteState) -> String;
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 History Manager
|
||||
Centralized history management with:
|
||||
- Prevention of duplicate history entries
|
||||
- Handling of popstate events for back/forward navigation
|
||||
- Smart decision between `pushState` and `replaceState`
|
||||
|
||||
### Phase 2: Extend App-Level Routing
|
||||
|
||||
#### 2.1 Enhanced AppView Routing
|
||||
Modify `AppView::from_path()` to:
|
||||
- Extract base view from path (e.g., `/library` from `/library/collection/123`)
|
||||
- Pass remaining path segments to components
|
||||
- Handle nested route parsing
|
||||
|
||||
#### 2.2 Route Segment Passing
|
||||
Update component props to include:
|
||||
- `initial_route: Option<String>` - route segment for component
|
||||
- `on_route_change: Callback<String>` - notify app of route changes
|
||||
|
||||
### Phase 3: LibraryView URL Integration
|
||||
|
||||
#### 3.1 LibraryRoute Definition
|
||||
```rust
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum LibraryRoute {
|
||||
Collections,
|
||||
Collection { collection_id: String },
|
||||
Item { collection_id: String, item_id: String },
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 URL Pattern Mapping
|
||||
- `/library` → `LibraryRoute::Collections`
|
||||
- `/library/collection/{id}` → `LibraryRoute::Collection`
|
||||
- `/library/collection/{id}/item/{item_id}` → `LibraryRoute::Item`
|
||||
|
||||
#### 3.3 State Synchronization
|
||||
- Parse initial route on component creation
|
||||
- Update URL when `ViewState` changes
|
||||
- Handle browser back/forward navigation
|
||||
|
||||
## Detailed Implementation Plan
|
||||
|
||||
### Step 1: Router Infrastructure
|
||||
**Files to create:**
|
||||
- `src/app/src/routing/mod.rs`
|
||||
- `src/app/src/routing/url_router.rs`
|
||||
- `src/app/src/routing/library_router.rs`
|
||||
- `src/app/src/routing/route_parser.rs`
|
||||
|
||||
### Step 2: App.rs Modifications
|
||||
**Changes to `App`:**
|
||||
1. Enhanced route parsing in `AppView::from_path()`
|
||||
2. Route segment extraction and passing to components
|
||||
3. Popstate event handling for browser navigation
|
||||
4. Updated URL building logic
|
||||
|
||||
### Step 3: LibraryView Transformation
|
||||
**Key changes:**
|
||||
1. Add `current_route: LibraryRoute` to component state
|
||||
2. Initialize state from URL on component creation
|
||||
3. Update URL when state changes via message handlers
|
||||
4. Handle route changes from browser navigation
|
||||
|
||||
### Step 4: Component Props Enhancement
|
||||
**New props structure:**
|
||||
```rust
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct LibraryViewProps {
|
||||
pub ws_addresses: Vec<String>,
|
||||
pub initial_route: Option<String>,
|
||||
pub on_route_change: Callback<String>,
|
||||
}
|
||||
```
|
||||
|
||||
## URL Examples
|
||||
|
||||
### LibraryView Routes
|
||||
- `/library` - Collections view
|
||||
- `/library/collection/ws1_collection123` - Items in collection
|
||||
- `/library/collection/ws1_collection123/item/item456` - Viewing specific item
|
||||
|
||||
### With Context
|
||||
- `/library/collection/ws1_collection123/item/item456?circles=ws1,ws2` - With circles context
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Clean Separation**: Router logic separated from component logic
|
||||
2. **Reusable**: Router trait can be implemented by other views
|
||||
3. **Minimal Code**: Leverages existing URL infrastructure
|
||||
4. **Link Sharing**: Full state encoded in URLs
|
||||
5. **Browser Integration**: Proper back/forward navigation support
|
||||
6. **SEO Friendly**: Meaningful URLs for each state
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
1. **Backward Compatibility**: Existing URLs continue to work
|
||||
2. **Gradual Rollout**: Start with LibraryView, extend to other components
|
||||
3. **Fallback Handling**: Graceful degradation for invalid routes
|
||||
4. **Progressive Enhancement**: Add URL support without breaking existing functionality
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. Create router infrastructure
|
||||
2. Extend App.rs routing capabilities
|
||||
3. Transform LibraryView to be URL-aware
|
||||
4. Test and refine
|
||||
5. Extend pattern to other views (Intelligence, Publishing, etc.)
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
1. **Unit Tests**: Router parsing and building functions
|
||||
2. **Integration Tests**: Component state synchronization with URLs
|
||||
3. **Browser Tests**: Back/forward navigation, refresh behavior
|
||||
4. **Link Sharing Tests**: URLs work when shared and opened in new tabs
|
||||
|
||||
This strategy provides a clean, scalable approach to URL and history support that can be extended to other components in the application.
|
382
docs/aidocs/WSS_IMPLEMENTATION_PLAN.md
Normal file
382
docs/aidocs/WSS_IMPLEMENTATION_PLAN.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# WSS (WebSocket Secure) Implementation Plan
|
||||
|
||||
## Overview
|
||||
This document outlines the complete implementation plan for adding WSS support to both the server and client_ws components, with optional configuration and comprehensive examples.
|
||||
|
||||
## Current State Analysis
|
||||
|
||||
### Server (src/server)
|
||||
- ✅ Basic TLS infrastructure with `rustls` and `rustls-pemfile`
|
||||
- ✅ Certificate loading functionality (`load_rustls_config`)
|
||||
- ✅ Optional TLS configuration in `ServerConfig`
|
||||
- ✅ Existing cert.pem and key.pem files for testing
|
||||
- ⚠️ TLS configuration could be enhanced with better error handling
|
||||
- ⚠️ Missing WSS-specific examples and tests
|
||||
|
||||
### Client (src/client_ws)
|
||||
- ✅ Basic TLS support with `native-tls` and `tokio-native-tls`
|
||||
- ✅ Cross-platform WebSocket support (native + WASM)
|
||||
- ⚠️ TLS connector accepts invalid certificates (development mode)
|
||||
- ⚠️ No automatic WSS URL detection
|
||||
- ⚠️ Missing WSS-specific configuration options
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Server WSS Enhancement ✅ READY TO IMPLEMENT
|
||||
|
||||
#### 1.1 Enhanced ServerConfig
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct ServerConfig {
|
||||
pub circle_name: String,
|
||||
pub circle_public_key: String,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub redis_url: String,
|
||||
pub enable_auth: bool,
|
||||
|
||||
// Enhanced TLS Configuration
|
||||
pub enable_tls: bool, // Explicit TLS enable flag
|
||||
pub cert_path: Option<String>, // Path to certificate file
|
||||
pub key_path: Option<String>, // Path to private key file
|
||||
pub tls_port: Option<u16>, // Optional separate TLS port
|
||||
pub tls_accept_invalid_certs: bool, // For development
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 Improved TLS Loading
|
||||
- Better error messages for certificate loading failures
|
||||
- Validation of certificate and key file existence
|
||||
- Support for different certificate formats
|
||||
- Logging of TLS configuration status
|
||||
|
||||
#### 1.3 Enhanced spawn_circle_server
|
||||
- Clear logging of HTTP vs HTTPS mode
|
||||
- Better error handling for TLS configuration
|
||||
- Support for running both HTTP and HTTPS simultaneously (if needed)
|
||||
|
||||
### Phase 2: Client WSS Enhancement
|
||||
|
||||
#### 2.1 WSS URL Detection
|
||||
```rust
|
||||
impl CircleWsClient {
|
||||
fn is_wss_url(&self) -> bool {
|
||||
self.ws_url.starts_with("wss://")
|
||||
}
|
||||
|
||||
fn requires_tls(&self) -> bool {
|
||||
self.is_wss_url()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 Enhanced TLS Configuration
|
||||
```rust
|
||||
pub struct TlsConfig {
|
||||
pub accept_invalid_certs: bool,
|
||||
pub accept_invalid_hostnames: bool,
|
||||
pub ca_cert_path: Option<String>,
|
||||
pub client_cert_path: Option<String>,
|
||||
pub client_key_path: Option<String>,
|
||||
}
|
||||
|
||||
impl CircleWsClientBuilder {
|
||||
pub fn with_tls_config(mut self, tls_config: TlsConfig) -> Self {
|
||||
self.tls_config = Some(tls_config);
|
||||
self
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 Cross-Platform WSS Support
|
||||
- Native: Enhanced `tokio-tungstenite` with `rustls` support
|
||||
- WASM: Ensure `gloo-net` handles WSS URLs correctly
|
||||
- Consistent error handling across platforms
|
||||
|
||||
### Phase 3: Examples and Testing
|
||||
|
||||
#### 3.1 Basic WSS Example
|
||||
```rust
|
||||
// examples/wss_basic_example.rs
|
||||
// Demonstrates basic WSS connection without authentication
|
||||
```
|
||||
|
||||
#### 3.2 WSS + Authentication Example
|
||||
```rust
|
||||
// examples/wss_auth_example.rs
|
||||
// Demonstrates WSS connection with secp256k1 authentication
|
||||
```
|
||||
|
||||
#### 3.3 End-to-End Secure Example
|
||||
```rust
|
||||
// examples/wss_end_to_end_example.rs
|
||||
// Complete server + client WSS with authentication
|
||||
```
|
||||
|
||||
#### 3.4 Certificate Generation Helper
|
||||
```rust
|
||||
// examples/wss_cert_generation.rs
|
||||
// Helper to generate self-signed certificates for development
|
||||
```
|
||||
|
||||
### Phase 4: Documentation and Integration
|
||||
|
||||
#### 4.1 Update README files
|
||||
- Server WSS configuration guide
|
||||
- Client WSS usage examples
|
||||
- Certificate management instructions
|
||||
|
||||
#### 4.2 Integration Tests
|
||||
- WSS connection establishment
|
||||
- WSS + authentication flow
|
||||
- Certificate validation scenarios
|
||||
|
||||
## Technical Implementation Details
|
||||
|
||||
### Server Enhancements
|
||||
|
||||
#### Enhanced Certificate Loading
|
||||
```rust
|
||||
fn load_rustls_config(
|
||||
cert_path: &str,
|
||||
key_path: &str,
|
||||
) -> Result<RustlsServerConfig, TlsConfigError> {
|
||||
// Validate file existence
|
||||
if !std::path::Path::new(cert_path).exists() {
|
||||
return Err(TlsConfigError::CertificateNotFound(cert_path.to_string()));
|
||||
}
|
||||
|
||||
if !std::path::Path::new(key_path).exists() {
|
||||
return Err(TlsConfigError::PrivateKeyNotFound(key_path.to_string()));
|
||||
}
|
||||
|
||||
// Enhanced error handling for certificate loading
|
||||
// Support for different key formats (PKCS8, RSA, etc.)
|
||||
// Validation of certificate chain
|
||||
}
|
||||
```
|
||||
|
||||
#### TLS Error Types
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TlsConfigError {
|
||||
#[error("Certificate file not found: {0}")]
|
||||
CertificateNotFound(String),
|
||||
#[error("Private key file not found: {0}")]
|
||||
PrivateKeyNotFound(String),
|
||||
#[error("Invalid certificate format: {0}")]
|
||||
InvalidCertificate(String),
|
||||
#[error("Invalid private key format: {0}")]
|
||||
InvalidPrivateKey(String),
|
||||
#[error("TLS configuration error: {0}")]
|
||||
ConfigurationError(String),
|
||||
}
|
||||
```
|
||||
|
||||
### Client Enhancements
|
||||
|
||||
#### WSS Connection Logic
|
||||
```rust
|
||||
impl CircleWsClient {
|
||||
async fn create_tls_connector(&self) -> Result<TlsConnector, CircleWsClientError> {
|
||||
let mut builder = TlsConnector::builder();
|
||||
|
||||
if let Some(tls_config) = &self.tls_config {
|
||||
builder.danger_accept_invalid_certs(tls_config.accept_invalid_certs);
|
||||
builder.danger_accept_invalid_hostnames(tls_config.accept_invalid_hostnames);
|
||||
|
||||
// Load custom CA certificates if provided
|
||||
if let Some(ca_cert_path) = &tls_config.ca_cert_path {
|
||||
// Load and add CA certificate
|
||||
}
|
||||
}
|
||||
|
||||
builder.build().map_err(|e| {
|
||||
CircleWsClientError::ConnectionError(format!("TLS configuration failed: {}", e))
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example Structure
|
||||
|
||||
#### Basic WSS Example
|
||||
```rust
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Start WSS server
|
||||
let config = ServerConfig {
|
||||
enable_tls: true,
|
||||
cert_path: Some("cert.pem".to_string()),
|
||||
key_path: Some("key.pem".to_string()),
|
||||
port: 8443,
|
||||
// ... other config
|
||||
};
|
||||
|
||||
let (server_task, _handle) = spawn_circle_server(config)?;
|
||||
|
||||
// Connect WSS client
|
||||
let mut client = CircleWsClientBuilder::new("wss://localhost:8443/ws".to_string())
|
||||
.with_tls_config(TlsConfig {
|
||||
accept_invalid_certs: true, // For development
|
||||
..Default::default()
|
||||
})
|
||||
.build();
|
||||
|
||||
client.connect().await?;
|
||||
|
||||
// Test basic functionality
|
||||
let result = client.play("print('Hello WSS!')".to_string()).await?;
|
||||
println!("Result: {}", result.output);
|
||||
|
||||
client.disconnect().await;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
#### WSS + Authentication Example
|
||||
```rust
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Start authenticated WSS server
|
||||
let config = ServerConfig {
|
||||
enable_tls: true,
|
||||
enable_auth: true,
|
||||
cert_path: Some("cert.pem".to_string()),
|
||||
key_path: Some("key.pem".to_string()),
|
||||
port: 8443,
|
||||
// ... other config
|
||||
};
|
||||
|
||||
// Connect authenticated WSS client
|
||||
let private_key = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
|
||||
|
||||
let mut client = CircleWsClientBuilder::new("wss://localhost:8443/ws".to_string())
|
||||
.with_keypair(private_key.to_string())
|
||||
.with_tls_config(TlsConfig {
|
||||
accept_invalid_certs: true,
|
||||
..Default::default()
|
||||
})
|
||||
.build();
|
||||
|
||||
client.connect().await?;
|
||||
|
||||
// Authenticate over secure connection
|
||||
match client.authenticate().await? {
|
||||
true => println!("Authenticated successfully over WSS"),
|
||||
false => println!("Authentication failed"),
|
||||
}
|
||||
|
||||
// Test authenticated request over secure connection
|
||||
let result = client.play("print('Authenticated WSS request!')".to_string()).await?;
|
||||
println!("Secure result: {}", result.output);
|
||||
|
||||
client.disconnect().await;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- TLS configuration validation
|
||||
- Certificate loading error handling
|
||||
- WSS URL detection
|
||||
- TLS connector creation
|
||||
|
||||
### Integration Tests
|
||||
- WSS server startup with valid certificates
|
||||
- WSS client connection establishment
|
||||
- WSS + authentication flow
|
||||
- Error scenarios (invalid certificates, connection failures)
|
||||
|
||||
### End-to-End Tests
|
||||
- Complete WSS server + client communication
|
||||
- Authentication over WSS
|
||||
- Multiple concurrent WSS connections
|
||||
- Certificate validation scenarios
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Development vs Production
|
||||
- **Development**: Accept self-signed certificates, invalid hostnames
|
||||
- **Production**: Strict certificate validation, proper CA chains
|
||||
|
||||
### Certificate Management
|
||||
- Clear documentation for certificate generation
|
||||
- Support for Let's Encrypt certificates
|
||||
- Certificate rotation considerations
|
||||
|
||||
### TLS Configuration
|
||||
- Modern TLS versions (1.2+)
|
||||
- Secure cipher suites
|
||||
- HSTS headers for web clients
|
||||
|
||||
## File Changes Required
|
||||
|
||||
### New Files
|
||||
- `examples/wss_basic_example.rs`
|
||||
- `examples/wss_auth_example.rs`
|
||||
- `examples/wss_end_to_end_example.rs`
|
||||
- `examples/wss_cert_generation.rs`
|
||||
- `src/server/src/tls_config.rs` (optional)
|
||||
- `src/client_ws/src/tls_config.rs` (optional)
|
||||
|
||||
### Modified Files
|
||||
- `src/server/src/lib.rs` - Enhanced TLS support
|
||||
- `src/server/cmd/main.rs` - TLS CLI options
|
||||
- `src/client_ws/src/lib.rs` - WSS support
|
||||
- `src/client_ws/Cargo.toml` - Additional TLS dependencies
|
||||
- `src/server/Cargo.toml` - Enhanced TLS dependencies
|
||||
- `src/server/README.md` - WSS documentation
|
||||
- `src/client_ws/README.md` - WSS usage guide
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Server Additional Dependencies
|
||||
```toml
|
||||
# Enhanced TLS support
|
||||
rustls-webpki = "0.103"
|
||||
rustls-native-certs = "0.7"
|
||||
```
|
||||
|
||||
### Client Additional Dependencies
|
||||
```toml
|
||||
# Enhanced TLS support for native
|
||||
rustls = { version = "0.23", optional = true }
|
||||
tokio-rustls = { version = "0.26", optional = true }
|
||||
rustls-native-certs = { version = "0.7", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["crypto"]
|
||||
crypto = ["secp256k1", "sha3"]
|
||||
rustls-tls = ["rustls", "tokio-rustls", "rustls-native-certs"]
|
||||
```
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. **Phase 1a**: Enhance server TLS configuration and error handling
|
||||
2. **Phase 1b**: Create basic WSS server example and test
|
||||
3. **Phase 1c**: Validate server WSS functionality with manual testing
|
||||
4. **Phase 2a**: Enhance client WSS support
|
||||
5. **Phase 2b**: Create client WSS examples
|
||||
6. **Phase 2c**: Test client WSS connectivity
|
||||
7. **Phase 3**: Create end-to-end WSS examples
|
||||
8. **Phase 4**: Integration tests and documentation
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- ✅ Server can start with WSS enabled using existing certificates
|
||||
- ✅ Client can connect to WSS server with proper TLS validation
|
||||
- ✅ Authentication works over WSS connections
|
||||
- ✅ Examples demonstrate all WSS functionality
|
||||
- ✅ Tests validate WSS behavior
|
||||
- ✅ Documentation explains WSS configuration
|
||||
- ✅ Cross-platform compatibility (native + WASM)
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Switch to Code mode for implementation
|
||||
2. Start with Phase 1a: Server TLS enhancements
|
||||
3. Create and test basic WSS server example
|
||||
4. Validate functionality before proceeding to client
|
Reference in New Issue
Block a user