implement stripe and idenfy webhooks support

This commit is contained in:
Timur Gordon
2025-07-08 22:49:47 +02:00
parent 7dfd54a20a
commit 93977bad7a
129 changed files with 9655 additions and 3640 deletions

View File

@@ -1,137 +0,0 @@
# System Architecture
This document provides a detailed overview of the `circles` project architecture. The project is composed of two core library crates, `server_ws` and `client_ws`, and a convenient `launcher` utility.
## 1. High-Level Overview
The `circles` project provides the core components for a client-server system designed to execute Rhai scripts in isolated environments. The `launcher` application is a utility that demonstrates how to use the `server_ws` and `client_ws` libraries to manage multiple server instances, but the libraries themselves are the fundamental building blocks.
The core functionality revolves around:
- **Orchestration**: The `launcher` starts and stops multiple, independent WebSocket servers.
- **Client-Server Communication**: A JSON-RPC 2.0 API over WebSockets allows clients to execute scripts and authenticate.
- **Authentication**: An optional, robust `secp256k1` signature-based authentication mechanism secures the script execution endpoint.
## 2. Component Architecture
### 2.1. `server_ws` (Library)
The `server_ws` crate provides the WebSocket server that handles client connections and API requests. Its key features include:
- **Web Framework**: Built using `Actix`, a powerful actor-based web framework for Rust.
- **WebSocket Handling**: Uses `actix-web-actors` to manage individual WebSocket sessions. Each client connection is handled by a `CircleWs` actor, ensuring that sessions are isolated from one another.
- **JSON-RPC API**: Exposes a JSON-RPC 2.0 API with methods for script execution (`play`) and authentication (`fetch_nonce`, `authenticate`).
- **Authentication Service**: The authentication flow is handled entirely within the WebSocket connection using the dedicated JSON-RPC methods.
### 2.2. `client_ws` (Library)
The `client_ws` crate is a WebSocket client library designed for interacting with the `server_ws`. It is engineered to be cross-platform:
- **Native**: For native Rust applications, it uses `tokio-tungstenite` for WebSocket communication.
- **WebAssembly (WASM)**: For browser-based applications, it uses `gloo-net` to integrate with the browser's native WebSocket API.
- **API**: Provides a flexible builder pattern for client construction and a high-level API (`CircleWsClient`) that abstracts the complexities of the WebSocket connection and the JSON-RPC protocol.
### 2.3. `launcher` (Utility)
The `launcher` is a command-line utility that demonstrates how to use the `server_ws` library. It is responsible for:
- **Configuration**: Reading a `circles.json` file that defines a list of Circle instances to run.
- **Orchestration**: Spawning a dedicated `server_ws` instance for each configured circle.
- **Lifecycle Management**: Managing the lifecycle of all spawned servers and their associated Rhai workers.
### 2.2. `server_ws`
The `server_ws` crate provides the WebSocket server that handles client connections and API requests. Its key features include:
- **Web Framework**: Built using `Actix`, a powerful actor-based web framework for Rust.
- **WebSocket Handling**: Uses `actix-web-actors` to manage individual WebSocket sessions. Each client connection is handled by a `CircleWs` actor, ensuring that sessions are isolated from one another.
- **JSON-RPC API**: Exposes a JSON-RPC 2.0 API with methods for script execution (`play`) and authentication (`fetch_nonce`, `authenticate`).
- **Authentication Service**: The authentication flow is handled entirely within the WebSocket connection using the dedicated JSON-RPC methods.
### 2.3. `client_ws`
The `client_ws` crate is a WebSocket client library designed for interacting with the `server_ws`. It is engineered to be cross-platform:
- **Native**: For native Rust applications, it uses `tokio-tungstenite` for WebSocket communication.
- **WebAssembly (WASM)**: For browser-based applications, it uses `gloo-net` to integrate with the browser's native WebSocket API.
- **API**: Provides a flexible builder pattern for client construction and a high-level API (`CircleWsClient`) that abstracts the complexities of the WebSocket connection and the JSON-RPC protocol.
## 3. Communication and Protocols
### 3.1. JSON-RPC 2.0
All client-server communication, including authentication, uses the JSON-RPC 2.0 protocol over the WebSocket connection. This provides a unified, lightweight, and well-defined structure for all interactions. The formal API contract is defined in the [openrpc.json](openrpc.json) file.
### 3.2. Authentication Flow
The authentication mechanism is designed to verify that a client possesses the private key corresponding to a given public key, without ever exposing the private key. The entire flow happens over the established WebSocket connection.
**Sequence of Events:**
1. **Keypair**: The client is instantiated with a `secp256k1` keypair.
2. **Nonce Request**: The client sends a `fetch_nonce` JSON-RPC request containing its public key.
3. **Nonce Issuance**: The server generates a unique, single-use nonce, stores it in the actor's state, and returns it to the client in a JSON-RPC response.
4. **Signature Creation**: The client signs the received nonce with its private key.
5. **Authentication Request**: The client sends an `authenticate` JSON-RPC message, containing the public key and the generated signature.
6. **Signature Verification**: The server's WebSocket actor retrieves the stored nonce for the given public key and cryptographically verifies the signature.
7. **Session Update**: If verification is successful, the server marks the client's WebSocket session as "authenticated," granting it access to protected methods like `play`.
## 4. Diagrams
### 4.1. System Component Diagram
```mermaid
graph TD
subgraph "User Machine"
Launcher[🚀 launcher]
CirclesConfig[circles.json]
Launcher -- Reads --> CirclesConfig
end
subgraph "Spawned Processes"
direction LR
subgraph "Circle 1"
Server1[🌐 server_ws on port 9001]
end
subgraph "Circle 2"
Server2[🌐 server_ws on port 9002]
end
end
Launcher -- Spawns & Manages --> Server1
Launcher -- Spawns & Manages --> Server2
subgraph "Clients"
Client1[💻 client_ws]
Client2[💻 client_ws]
end
Client1 -- Connects via WebSocket --> Server1
Client2 -- Connects via WebSocket --> Server2
```
### 4.2. Authentication Sequence Diagram
```mermaid
sequenceDiagram
participant Client as client_ws
participant WsActor as CircleWs Actor (WebSocket)
Client->>Client: Instantiate with keypair
Note over Client: Has public_key, private_key
Client->>+WsActor: JSON-RPC "fetch_nonce" (pubkey)
WsActor->>WsActor: generate_nonce()
WsActor->>WsActor: store_nonce(pubkey, nonce)
WsActor-->>-Client: JSON-RPC Response ({"nonce": "..."})
Client->>Client: sign(nonce, private_key)
Note over Client: Has signature
Client->>+WsActor: JSON-RPC "authenticate" (pubkey, signature)
WsActor->>WsActor: retrieve_nonce(pubkey)
WsActor->>WsActor: verify_signature(nonce, signature, pubkey)
alt Signature is Valid
WsActor->>WsActor: Set session as authenticated
WsActor-->>-Client: JSON-RPC Response ({"authenticated": true})
else Signature is Invalid
WsActor-->>-Client: JSON-RPC Error (Invalid Credentials)
end
Note over WsActor: Subsequent "play" requests will include the authenticated public key.

View 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
View File

@@ -0,0 +1 @@
Docs generated by ai that need to be organized.

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

View 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

59
docs/design.md Normal file
View File

@@ -0,0 +1,59 @@
# Design
## Overview
This document outlines a system design that satisfies the specified requirements for decentralized backend ownership. It describes how to implement core capabilities like isolation, delegation, and open logic control — without introducing tight coupling or central dependencies.
## Design Principles
### 1. **Contextual Execution**
- Define a runtime model where each peer context is a named environment.
- Execution is scoped to a context, and all operations are resolved within it.
**Implementation Strategy:**
- Use a unified worker engine that can load and execute within a namespaced peer context.
- Contexts are mounted via a virtual filesystem abstraction, one directory per peer.
### 2. **Logical Isolation via Filesystem Namespacing**
- Each peer's execution environment is backed by a namespaced root directory.
- All storage operations are relative to that root.
**Advantages:**
- Easy enforcement of data boundaries
- Works across shared processes
### 3. **Script-Based Delegated Execution**
- Scripts are the unit of cross-peer interaction.
- A script includes the `caller` (originating peer), parameters, and logic.
**Design Feature:**
- A script sent to another peer is evaluated with both `caller` and `target` contexts available to the runtime.
- Target peer decides whether to accept and how to interpret it.
### 4. **Policy-Driven Acceptance**
- Each context has policies determining:
- Which peers may send scripts
- Which actions are allowed
**Example:** Policies written as declarative access control rules, tied to peer IDs, namespaces, or capabilities.
### 5. **Open, Modifiable Logic**
- Use an embedded domain-specific language (e.g. Rhai) that allows:
- Peer owners to define and inspect their logic
- Script modules to be composed, extended, or overridden
### 6. **Worker Multiplexing**
- Use a single worker binary that can handle one or many peer contexts.
- The context is dynamically determined at runtime.
**Design Note:**
- All workers enforce namespacing, even when only one peer is active per process.
- Supports both isolated (1 peer per worker) and shared (many peers per worker) deployments.
## Optional Enhancements
- Pluggable transport layer (WebSocket, HTTP/2, NATS, etc.)
- Pluggable storage backends for namespace-mounting (FS, S3, SQLite, etc.)
- Declarative schema binding between DSL and structured data
This design enables decentralized application runtime control while supporting a scalable and secure execution model.

34
docs/rationale.md Normal file
View File

@@ -0,0 +1,34 @@
# Rethinking Backend Ownership
## Motivation
Modern applications are powered by backends that run on infrastructure and systems controlled by centralized entities. Whether it's social platforms, collaboration tools, or data-driven apps, the backend is almost always a black box — hosted, maintained, and operated by someone else.
This has profound implications:
- **Loss of autonomy:** Users are locked out of the logic, rules, and data structures that govern their digital experience.
- **Opaque control:** Application behavior can change without the users consent — and often without visibility.
- **Vendor lock-in:** Switching providers or migrating data is often non-trivial, risky, or impossible.
- **Security and privacy risks:** Centralized backends present single points of failure and attack.
In this model, users are not participants in their computing environment — they are clients of someone else's backend.
## The Vision
The purpose of this initiative is to invert that dynamic. We aim to establish a paradigm where users and organizations **own and control their own backend logic and data**, without sacrificing connectivity, collaboration, or scalability.
This means:
- **Local authority:** Each user or organization should have full control over how their backend behaves — what code runs, what data is stored, and who can access it.
- **Portable and interoperable:** Ownership must not mean isolation. User-owned backends should be able to interact with one another on equal footing.
- **Transparent logic:** Application behavior should be visible, inspectable, and modifiable by the user.
- **Delegation, not dependence:** Users should be able to cooperate and interact by delegating execution to each other — not by relying on a central server.
## What We Stand For
- **Agency:** You control your digital environment.
- **Decentralization:** No central chokepoint for computation or data.
- **Modularity:** Users compose their backend behavior, not inherit it from a monolith.
- **Resilience:** Systems should degrade gracefully, fail independently, and recover without central orchestration.
This is about building a more equitable and open computing model — one where the backend serves you, not the other way around.

View File

@@ -0,0 +1,50 @@
# System Requirements Specification
## Objective
To define the core requirements for a system that fulfills the goals of decentralized backend ownership — enabling individuals and organizations to control, operate, and interact through their own backend environments without relying on centralized infrastructure.
## Functional Requirements
### 1. **Isolated Execution Contexts**
- Each user or peer must operate within a distinct, logically isolated execution context.
- Contexts must not be able to interfere with each other's state or runtime.
### 2. **Cross-Context Communication**
- Peers must be able to initiate interactions with other peers.
- Communication must include origin metadata (who initiated it), and be authorized by the target context.
### 3. **Delegated Execution**
- A peer must be able to send code or instructions to another peer for execution, under the recipient's policies.
- The recipient must treat the execution as contextualized by the caller, but constrained by its own local rules.
### 4. **Ownership of Logic and Data**
- Users must be able to inspect, modify, and extend the logic that governs their backend.
- Data storage and access policies must be under the control of the peer.
### 5. **Composability and Modifiability**
- System behavior must be defined by open, composable modules or scripts.
- Users must be able to override default behavior or extend it with minimal coupling.
## Non-Functional Requirements
### 6. **Security and Isolation**
- Scripts or instructions from external peers must be sandboxed and policy-checked.
- Each execution context must enforce boundaries between data and logic.
### 7. **Resilience and Redundancy**
- Failure of one peer or node must not impact others.
- Communication must be asynchronous and fault-tolerant.
### 8. **Portability**
- A peers logic and data must be portable across environments and host infrastructure.
- No assumption of persistent centralized hosting.
### 9. **Transparency**
- All logic must be auditable by its owner.
- Communications between peers must be observable and traceable.
### 10. **Scalability**
- The system must support large numbers of peer contexts, potentially hosted on shared infrastructure without compromising logical separation.
These requirements define the baseline for any system that claims to decentralize backend control and empower users to operate their own programmable, connected environments.