sal-modular/sigsocket_client/README.md
Sameh Abouel-saad 9f143ded9d Implement native and WASM WebSocket client for sigsocket communication
- Added `NativeClient` for non-WASM environments with automatic reconnection and message handling.
- Introduced `WasmClient` for WASM environments, supporting WebSocket communication and reconnection logic.
- Created protocol definitions for `SignRequest` and `SignResponse` with serialization and deserialization.
- Developed integration tests for the client functionality and sign request handling.
- Implemented WASM-specific tests to ensure compatibility and functionality in browser environments.
2025-06-04 13:03:15 +03:00

219 lines
5.5 KiB
Markdown

# SigSocket Client
A WebSocket client library for connecting to sigsocket servers with **WASM-first support**.
## Features
- 🌐 **WASM-first design**: Optimized for browser environments
- 🖥️ **Native support**: Works in native Rust applications
- 🔐 **No signing logic**: Delegates signing to the application
- 👤 **User approval flow**: Notifies applications about incoming requests
- 🔌 **sigsocket compatible**: Fully compatible with sigsocket server protocol
- 🚀 **Async/await**: Modern async Rust API
- 🔄 **Automatic reconnection**: Both platforms support reconnection with exponential backoff
- ⏱️ **Connection timeouts**: Proper timeout handling and connection management
- 🛡️ **Production ready**: Comprehensive error handling and reliability features
## Quick Start
### Native Usage
```rust
use sigsocket_client::{SigSocketClient, SignRequestHandler, SignRequest, Result};
struct MySignHandler;
impl SignRequestHandler for MySignHandler {
fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>> {
// 1. Present request to user
println!("Sign request: {}", request.message);
// 2. Get user approval
// ... your UI logic here ...
// 3. Sign the message (using your signing logic)
let signature = your_signing_function(&request.message_bytes()?)?;
Ok(signature)
}
}
#[tokio::main]
async fn main() -> Result<()> {
// Your public key bytes
let public_key = hex::decode("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388")?;
// Create and configure client
let mut client = SigSocketClient::new("ws://localhost:8080/ws", public_key)?;
client.set_sign_handler(MySignHandler);
// Connect and handle requests
client.connect().await?;
// Client will automatically handle incoming signature requests
// Keep the connection alive...
Ok(())
}
```
### WASM Usage
```rust
use sigsocket_client::{SigSocketClient, SignRequestHandler, SignRequest, Result};
use wasm_bindgen::prelude::*;
struct WasmSignHandler;
impl SignRequestHandler for WasmSignHandler {
fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>> {
// Show request to user in browser
web_sys::window()
.unwrap()
.alert_with_message(&format!("Sign request: {}", request.id))
.unwrap();
// Your signing logic here...
let signature = sign_with_browser_wallet(&request.message_bytes()?)?;
Ok(signature)
}
}
#[wasm_bindgen]
pub async fn connect_to_sigsocket() -> Result<(), JsValue> {
let public_key = get_user_public_key()?;
let mut client = SigSocketClient::new("ws://localhost:8080/ws", public_key)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
client.set_sign_handler(WasmSignHandler);
client.connect().await
.map_err(|e| JsValue::from_str(&e.to_string()))?;
Ok(())
}
```
## Protocol
The sigsocket client implements a simple WebSocket protocol:
### 1. Introduction
Upon connection, the client sends its public key as a hex-encoded string:
```
02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388
```
### 2. Sign Requests
The server sends signature requests as JSON:
```json
{
"id": "req_123",
"message": "dGVzdCBtZXNzYWdl" // base64-encoded message
}
```
### 3. Sign Responses
The client responds with signatures as JSON:
```json
{
"id": "req_123",
"message": "dGVzdCBtZXNzYWdl", // original message
"signature": "c2lnbmF0dXJl" // base64-encoded signature
}
```
## API Reference
### `SigSocketClient`
Main client for connecting to sigsocket servers.
#### Methods
- `new(url, public_key)` - Create a new client
- `set_sign_handler(handler)` - Set the signature request handler
- `connect()` - Connect to the server with automatic reconnection
- `disconnect()` - Disconnect from the server
- `send_sign_response(response)` - Manually send a signature response
- `state()` - Get current connection state
- `is_connected()` - Check if connected
#### Reconnection Configuration (WASM only)
- `set_auto_reconnect(enabled)` - Enable/disable automatic reconnection
- `set_reconnect_config(max_attempts, initial_delay_ms)` - Configure reconnection parameters
**Default settings:**
- Max attempts: 5
- Initial delay: 1000ms (with exponential backoff: 1s, 2s, 4s, 8s, 16s)
- Auto-reconnect: enabled
### `SignRequestHandler` Trait
Implement this trait to handle incoming signature requests.
```rust
trait SignRequestHandler {
fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>>;
}
```
### `SignRequest`
Represents a signature request from the server.
#### Fields
- `id: String` - Unique request identifier
- `message: String` - Base64-encoded message to sign
#### Methods
- `message_bytes()` - Decode message to bytes
- `message_hex()` - Get message as hex string
### `SignResponse`
Represents a signature response to send to the server.
#### Methods
- `new(id, message, signature)` - Create a new response
- `from_request_and_signature(request, signature)` - Create from request and signature bytes
## Examples
Run the basic example:
```bash
cargo run --example basic_usage
```
## Building
### Native Build
```bash
cargo build
cargo test
cargo run --example basic_usage
```
### WASM Build
```bash
wasm-pack build --target web
wasm-pack test --headless --firefox # Run WASM tests
```
## Requirements
### Native
- Rust 1.70+
- tokio runtime
### WASM
- wasm-pack
- Modern browser with WebSocket support
## License
MIT OR Apache-2.0