diff --git a/skill.md b/skill.md index 2c5d96a..fc4dcf7 100644 --- a/skill.md +++ b/skill.md @@ -38,28 +38,28 @@ curl "http://localhost:8787/random?bytes=64&hex=true" curl "http://localhost:8787/random?bytes=1024" -o random.bin ``` -### 2. Get Raw Data (`/raw`) - -Get raw LSB (least significant bit) data from camera sensor without cryptographic extraction. Useful for testing or custom post-processing. - -**Parameters:** -- `bytes` (integer, default: 65536, max: 1048576): Number of raw bytes to extract - -**Example:** -```bash -curl "http://localhost:8787/raw?bytes=1024" -o raw.bin -``` - -### 3. Stream Random Bytes (`/stream`) +### 2. Stream Random Bytes (`/stream`) Stream continuous random bytes using Server-Sent Events (SSE) format. Useful for high-throughput applications. **Parameters:** - `bytes` (integer, optional): Total bytes to stream (omit for unlimited) -- `hex` (boolean, default: false): Stream as hexadecimal strings +- `hex` (boolean, default: false): Stream as hexadecimal strings instead of binary **Examples:** ```bash +# Stream unlimited random bytes (binary) +curl -N "http://localhost:8787/stream" + +# Stream exactly 1MB +curl -N "http://localhost:8787/stream?bytes=1048576" + +# Stream as hexadecimal strings +curl -N "http://localhost:8787/stream?hex=true" + +# Stream limited bytes as hex +curl -N "http://localhost:8787/stream?bytes=1024&hex=true" +```bash # Stream unlimited random bytes curl -N "http://localhost:8787/stream" @@ -70,7 +70,7 @@ curl -N "http://localhost:8787/stream?bytes=1048576" curl -N "http://localhost:8787/stream?hex=true" ``` -### 4. List Cameras (`/cameras`) +### 3. List Cameras (`/cameras`) List available camera devices on the system. @@ -93,7 +93,7 @@ curl "http://localhost:8787/cameras" } ``` -### 5. Health Check (`/health`) +### 4. Health Check (`/health`) Check if the TRNG server is running. @@ -113,7 +113,6 @@ The API implements the Model Context Protocol (MCP) specification, allowing AI a The following tools are exposed via MCP: 1. **get-random**: Generate random bytes (cryptographically secure) -2. **get-raw**: Extract raw sensor data 3. **get-stream**: Stream random bytes continuously 4. **list-cameras**: Discover available cameras 5. **health-check**: Verify server status @@ -171,7 +170,7 @@ curl -N "http://localhost:8787/stream" ## Limitations -- Maximum 1MB per request (`/random` and `/raw`) +- Maximum 1MB per request (`/random`) - Maximum 4 concurrent requests - Requires a camera device with covered lens - Performance depends on camera frame rate @@ -180,7 +179,6 @@ curl -N "http://localhost:8787/stream" - No authentication required (intended for local/trusted networks) - Random data is cryptographically secure when using `/random` endpoint -- Raw endpoint (`/raw`) provides unprocessed sensor data - Consider rate limiting in production deployments ## See Also diff --git a/src/main.rs b/src/main.rs index 67e54a2..53e21c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use axum::{ routing::get, Router, }; -use camera_trng::{extract_entropy, extract_raw_lsb, list_cameras, subscribe_entropy, unsubscribe_entropy, ensure_producer_running, test_camera, CameraConfig, CHUNK_SIZE}; +use camera_trng::{extract_entropy, list_cameras, subscribe_entropy, unsubscribe_entropy, ensure_producer_running, test_camera, CameraConfig, CHUNK_SIZE}; use bytes::Bytes; use std::sync::{Arc, Mutex}; use serde_json::json; @@ -32,12 +32,6 @@ struct RandomQuery { fn default_bytes() -> usize { 32 } -#[derive(serde::Deserialize)] -struct RawQuery { - #[serde(default = "default_raw_bytes")] - bytes: usize, -} -fn default_raw_bytes() -> usize { 65536 } #[derive(serde::Deserialize)] struct StreamQuery { @@ -74,10 +68,10 @@ async fn main() -> Result<(), Box> { .route("/", get(index)) .route("/cameras", get(get_cameras)) .route("/random", get(get_random)) - .route("/raw", get(get_raw)) .route("/stream", get(get_stream)) .route("/health", get(health)) .route("/.well-known/mcp.json", get(mcp_wellknown)) + .route("/.well-known/skill.md", get(get_skill_md)) .route("/docs", get(get_docs)) .route("/docs/skill.md", get(get_skill_md)) .route("/docs/mcp.json", get(mcp_wellknown)); @@ -266,9 +260,10 @@ async fn mcp_wellknown(headers: HeaderMap, _uri: Uri) -> Json let origin = format!("{}://{}", scheme, host); // Build URL templates as strings first - let random_url = format!("{}/random?bytes={{bytes}}&hex={{hex}}", origin); - let raw_url = format!("{}/raw?bytes={{bytes}}", origin); - let stream_url = format!("{}/stream?bytes={{bytes}}&hex={{hex}}", origin); + let random_url_example1 = format!("{}/random?bytes=32&hex=true", origin); + let random_url_example2 = format!("{}/random?bytes=64&hex=false", origin); + let stream_url_example1 = format!("{}/stream?hex=true", origin); + let stream_url_example2 = format!("{}/stream?bytes=1024&hex=true", origin); let cameras_url = format!("{}/cameras", origin); let health_url = format!("{}/health", origin); let mcp_url = format!("{}/.well-known/mcp.json", origin); @@ -282,7 +277,8 @@ async fn mcp_wellknown(headers: HeaderMap, _uri: Uri) -> Json { "name": "get-random", "description": "Get cryptographically secure random bytes from camera sensor entropy", - "url_template": random_url, + "url": random_url_example1, + "example": random_url_example2, "capabilities": ["random-generation", "entropy-source", "quantum"], "auth": { "type": "none" }, "parameters": { @@ -290,20 +286,11 @@ async fn mcp_wellknown(headers: HeaderMap, _uri: Uri) -> Json "hex": { "type": "boolean", "default": false, "description": "Return bytes as hexadecimal string instead of binary" } } }, - { - "name": "get-raw", - "description": "Get raw LSB (least significant bit) data from camera sensor without cryptographic extraction", - "url_template": raw_url, - "capabilities": ["entropy-source", "raw-data"], - "auth": { "type": "none" }, - "parameters": { - "bytes": { "type": "integer", "default": 65536, "min": 1, "max": 1048576, "description": "Number of raw bytes to extract (max 1MB)" } - } - }, { "name": "get-stream", "description": "Stream continuous random bytes (SSE format). Use ?bytes=N to limit total bytes, ?hex=true for hex output", - "url_template": stream_url, + "url": stream_url_example1, + "example": stream_url_example2, "capabilities": ["random-generation", "entropy-source", "quantum", "streaming"], "auth": { "type": "none" }, "parameters": { @@ -314,7 +301,7 @@ async fn mcp_wellknown(headers: HeaderMap, _uri: Uri) -> Json { "name": "list-cameras", "description": "List available camera devices", - "url_template": cameras_url, + "url": cameras_url, "capabilities": ["device-discovery"], "auth": { "type": "none" }, "parameters": {} @@ -322,7 +309,7 @@ async fn mcp_wellknown(headers: HeaderMap, _uri: Uri) -> Json { "name": "health-check", "description": "Check if the TRNG server is running", - "url_template": health_url, + "url": health_url, "capabilities": ["health"], "auth": { "type": "none" }, "parameters": {} @@ -379,22 +366,6 @@ async fn get_random(Query(params): Query) -> Response { } -async fn get_raw(Query(params): Query) -> Response { - let bytes = params.bytes.min(MAX_BYTES_PER_REQUEST); - if bytes == 0 { - return (StatusCode::BAD_REQUEST, "bytes must be > 0").into_response(); - } - let config = CameraConfig::from_env(); - match tokio::task::spawn_blocking(move || extract_raw_lsb(bytes, &config)).await { - Ok(Ok(data)) => Response::builder() - .header(header::CONTENT_TYPE, "application/octet-stream") - .header(header::CACHE_CONTROL, "no-store") - .body(Body::from(data)) - .unwrap(), - Ok(Err(e)) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(), - Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), - } -} /// Cryptographically sound continuous random. GET /stream or /stream?bytes=N. /// Multiple streams get different data (each chunk goes to one consumer).