From 1ce1c833b60091dc9b14b31b0d89133c59613306 Mon Sep 17 00:00:00 2001 From: Leopere Date: Mon, 9 Feb 2026 13:50:56 -0500 Subject: [PATCH] Add comprehensive curl/stream examples to all code tabs and skill.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Expand index.html code examples (curl, Python, JS, Rust, Go) to include /stream, /dice, /password, /coin, /8ball, and /health endpoints alongside /random — stream examples were previously missing - Fix skill.md: remove duplicated stream code block, add dedicated sections for dice, password, coin, and 8ball endpoints with full curl examples and parameter docs - Add comprehensive "curl (comprehensive)" cheat-sheet section to skill.md covering every endpoint in one block Co-authored-by: Cursor --- skill.md | 191 +++++++++++++++++++++++++++++++++++++------------ src/index.html | 154 +++++++++++++++++++++++++++++++++++---- 2 files changed, 283 insertions(+), 62 deletions(-) diff --git a/skill.md b/skill.md index fc4dcf7..bd6bd51 100644 --- a/skill.md +++ b/skill.md @@ -28,19 +28,22 @@ Generate cryptographically secure random bytes from camera sensor entropy. **Examples:** ```bash -# Get 32 bytes as binary -curl "http://localhost:8787/random?bytes=32" -o random.bin +# Get 32 bytes as hex string +curl -s "http://localhost:8787/random?bytes=32&hex=true" -# Get 64 bytes as hex string -curl "http://localhost:8787/random?bytes=64&hex=true" +# Get 64 raw bytes saved to a file +curl -s "http://localhost:8787/random?bytes=64" -o random.bin -# Get 1KB of random data -curl "http://localhost:8787/random?bytes=1024" -o random.bin +# Get 1 KB and pipe to xxd for hex viewing +curl -s "http://localhost:8787/random?bytes=1024" | xxd + +# Get 1 MB of random data +curl -s "http://localhost:8787/random?bytes=1048576" -o random-1mb.bin ``` ### 2. Stream Random Bytes (`/stream`) -Stream continuous random bytes using Server-Sent Events (SSE) format. Useful for high-throughput applications. +Stream continuous random bytes. Useful for high-throughput applications or feeding entropy to other processes. **Parameters:** - `bytes` (integer, optional): Total bytes to stream (omit for unlimited) @@ -48,26 +51,20 @@ Stream continuous random bytes using Server-Sent Events (SSE) format. Useful for **Examples:** ```bash -# Stream unlimited random bytes (binary) +# Stream unlimited random bytes (binary, Ctrl-C to stop) curl -N "http://localhost:8787/stream" -# Stream exactly 1MB -curl -N "http://localhost:8787/stream?bytes=1048576" +# Stream exactly 1 MB as hex +curl -N "http://localhost:8787/stream?bytes=1048576&hex=true" -# Stream as hexadecimal strings -curl -N "http://localhost:8787/stream?hex=true" +# Stream hex to a file +curl -N "http://localhost:8787/stream?hex=true" > entropy.hex -# 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" +# Pipe stream to another process +curl -N "http://localhost:8787/stream?bytes=4096" | openssl enc -aes-256-cbc -pass pass:test -in /dev/stdin -# Stream exactly 1MB -curl -N "http://localhost:8787/stream?bytes=1048576" - -# Stream as hex -curl -N "http://localhost:8787/stream?hex=true" +# Stream 10 KB of binary entropy, view as hex dump +curl -sN "http://localhost:8787/stream?bytes=10240" | xxd | head -40 ``` ### 3. List Cameras (`/cameras`) @@ -76,7 +73,7 @@ List available camera devices on the system. **Example:** ```bash -curl "http://localhost:8787/cameras" +curl -s "http://localhost:8787/cameras" | jq ``` **Response:** @@ -95,14 +92,81 @@ curl "http://localhost:8787/cameras" ### 4. Health Check (`/health`) -Check if the TRNG server is running. +Check server status, pool level, and harvester state. **Example:** ```bash -curl "http://localhost:8787/health" +curl -s "http://localhost:8787/health" | jq ``` -**Response:** `ok` +**Response:** +```json +{ + "status": "ok", + "pool_bytes_available": 262144, + "total_camera_bytes_harvested": 1048576, + "csprng_seeded_from_camera": true +} +``` + +### 5. Quantum Dice (`/dice`) + +Roll quantum-random dice. + +**Parameters:** +- `d` (integer, default: 6, range: 2–256): Number of sides +- `count` (integer, default: 1, max: 100): Number of dice to roll + +**Examples:** +```bash +# Roll one d6 +curl -s "http://localhost:8787/dice" | jq + +# Roll 5d20 +curl -s "http://localhost:8787/dice?d=20&count=5" | jq +``` + +### 6. Quantum Password Generator (`/password`) + +Generate a cryptographically random password. + +**Parameters:** +- `length` (integer, default: 20, max: 128): Password length +- `charset` (string, default: "alphanumeric"): One of `alphanumeric`, `full`, `hex`, or flag combos (`l`ower, `u`pper, `d`igit, `s`ymbol) +- `exclude_ambiguous` (boolean, default: false): Remove `0 O o I l 1 |` + +**Examples:** +```bash +# Default 20-char alphanumeric password +curl -s "http://localhost:8787/password" | jq -r .password + +# 32-char with full charset, no ambiguous chars +curl -s "http://localhost:8787/password?length=32&charset=full&exclude_ambiguous=true" | jq -r .password + +# Hex-only 64-char token +curl -s "http://localhost:8787/password?length=64&charset=hex" | jq -r .password +``` + +### 7. Quantum Coin Flip (`/coin`) + +Flip a quantum coin. + +**Example:** +```bash +curl -s "http://localhost:8787/coin" | jq -r .result +``` + +### 8. Quantum 8 Ball (`/8ball`) + +Get a quantum-random Magic 8 Ball answer. + +**Parameters:** +- `question` (string, optional): The question to ask + +**Example:** +```bash +curl -s "http://localhost:8787/8ball?question=Will+it+work" | jq +``` ## MCP Integration @@ -113,9 +177,9 @@ 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) -3. **get-stream**: Stream random bytes continuously -4. **list-cameras**: Discover available cameras -5. **health-check**: Verify server status +2. **get-stream**: Stream random bytes continuously +3. **list-cameras**: Discover available cameras +4. **health-check**: Verify server status ### Self-Referencing URLs @@ -135,50 +199,83 @@ This ensures URLs in the MCP response always reference the correct origin, wheth import requests # Get 32 random bytes -response = requests.get("http://localhost:8787/random?bytes=32") -random_bytes = response.content +resp = requests.get("http://localhost:8787/random?bytes=32") +random_bytes = resp.content +print(random_bytes.hex()) -# Get hex string -response = requests.get("http://localhost:8787/random?bytes=64&hex=true") -hex_string = response.text +# Stream random data +with requests.get("http://localhost:8787/stream?hex=true", stream=True) as r: + for chunk in r.iter_content(chunk_size=64): + print(chunk.decode(), end="") + +# Tools +print(requests.get("http://localhost:8787/dice?d=20&count=3").json()) +print(requests.get("http://localhost:8787/password?length=24&charset=full").json()["password"]) +print(requests.get("http://localhost:8787/coin").json()["result"]) +print(requests.get("http://localhost:8787/8ball").json()["answer"]) ``` ### JavaScript/Node.js ```javascript // Get random bytes -const response = await fetch("http://localhost:8787/random?bytes=32"); -const buffer = await response.arrayBuffer(); +const resp = await fetch("http://localhost:8787/random?bytes=32"); +const buffer = await resp.arrayBuffer(); const bytes = new Uint8Array(buffer); // Stream random data -const stream = await fetch("http://localhost:8787/stream?bytes=1024"); +const stream = await fetch("http://localhost:8787/stream?hex=true"); const reader = stream.body.getReader(); -// ... read chunks +const decoder = new TextDecoder(); +while (true) { + const { done, value } = await reader.read(); + if (done) break; + process.stdout.write(decoder.decode(value)); +} + +// Tools +const dice = await (await fetch("http://localhost:8787/dice?d=6&count=5")).json(); +const pw = await (await fetch("http://localhost:8787/password?length=20")).json(); +const coin = await (await fetch("http://localhost:8787/coin")).json(); +const ball = await (await fetch("http://localhost:8787/8ball")).json(); ``` -### curl +### curl (comprehensive) ```bash -# Basic usage -curl "http://localhost:8787/random?bytes=32&hex=true" +# ── Random ──────────────────────────────────────────── +curl -s "http://localhost:8787/random?bytes=32&hex=true" +curl -s "http://localhost:8787/random?bytes=1024" -o random.bin +curl -s "http://localhost:8787/random?bytes=256" | xxd -# Save to file -curl "http://localhost:8787/random?bytes=1024" -o random.bin +# ── Stream ──────────────────────────────────────────── +curl -N "http://localhost:8787/stream" +curl -N "http://localhost:8787/stream?bytes=1024&hex=true" +curl -N "http://localhost:8787/stream?hex=true" > entropy.hex -# Stream -curl -N "http://localhost:8787/stream" +# ── Tools ───────────────────────────────────────────── +curl -s "http://localhost:8787/dice?d=20&count=3" | jq +curl -s "http://localhost:8787/password?length=24" | jq -r .password +curl -s "http://localhost:8787/coin" | jq -r .result +curl -s "http://localhost:8787/8ball?question=Ship+it" | jq + +# ── Health & Discovery ──────────────────────────────── +curl -s "http://localhost:8787/health" | jq +curl -s "http://localhost:8787/cameras" | jq +curl -s "http://localhost:8787/.well-known/mcp.json" | jq ``` ## Limitations -- Maximum 1MB per request (`/random`) +- Maximum 1 MB per request (`/random`) - Maximum 4 concurrent requests - Requires a camera device with covered lens - Performance depends on camera frame rate +- CSPRNG fallback is used when the camera pool is empty (still cryptographically safe, but not quantum) ## Security Notes - No authentication required (intended for local/trusted networks) -- Random data is cryptographically secure when using `/random` endpoint +- Random data is cryptographically secure from both the camera entropy pool and ChaCha20 CSPRNG fallback +- The CSPRNG is periodically re-seeded with real camera entropy - Consider rate limiting in production deployments ## See Also diff --git a/src/index.html b/src/index.html index f32dc59..4963c6f 100644 --- a/src/index.html +++ b/src/index.html @@ -546,22 +546,145 @@ const hexParam = fmt === 'hex' ? '&hex=true' : ''; const url = `${origin}/random?bytes=${bytes}${hexParam}`; - document.getElementById('code-curl').textContent = fmt === 'raw' - ? `# Get ${bytes} raw random bytes\ncurl -s "${origin}/random?bytes=${bytes}" -o random.bin\n\n# Or pipe to xxd for viewing\ncurl -s "${origin}/random?bytes=${bytes}" | xxd` + // ── cURL ────────────────────────────────────────────────── + const curlRandom = fmt === 'raw' + ? `# Get ${bytes} raw random bytes\ncurl -s "${origin}/random?bytes=${bytes}" -o random.bin\n\n# Pipe to xxd for hex viewing\ncurl -s "${origin}/random?bytes=${bytes}" | xxd` : `# Get ${bytes} random bytes as ${fmt}\ncurl -s "${url}"`; - - document.getElementById('code-python').textContent = fmt === 'raw' - ? `import requests\n\nresp = requests.get("${origin}/random?bytes=${bytes}")\nrandom_bytes = resp.content # ${bytes} bytes\nprint(random_bytes.hex())` - : `import requests\n\nresp = requests.get("${url}")\nrandom_${fmt} = resp.text\nprint(random_${fmt})`; - - document.getElementById('code-js').textContent = fmt === 'raw' - ? `const resp = await fetch("${origin}/random?bytes=${bytes}");\nconst buffer = await resp.arrayBuffer();\nconst bytes = new Uint8Array(buffer); // ${bytes} bytes\nconsole.log([...bytes].map(b => b.toString(16).padStart(2,'0')).join(''));` - : `const resp = await fetch("${url}");\nconst random = await resp.text();\nconsole.log(random);`; - - document.getElementById('code-rust').textContent = `use reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let bytes = reqwest::get("${url}")\n .await?.${fmt === 'raw' ? 'bytes' : 'text'}().await?;\n println!("{:?}", bytes);\n Ok(())\n}`; - - document.getElementById('code-go').textContent = `package main\n\nimport (\n "fmt"\n "io"\n "net/http"\n)\n\nfunc main() {\n resp, _ := http.Get("${url}")\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`; - + + const curlStream = [ + `# ── Stream ──────────────────────────────────────────`, + `# Stream continuous random bytes (binary)`, + `curl -N "${origin}/stream"`, + ``, + `# Stream 1 KB as hex`, + `curl -N "${origin}/stream?bytes=1024&hex=true"`, + ``, + `# Stream unlimited hex to a file`, + `curl -N "${origin}/stream?hex=true" > entropy.hex`, + ].join('\n'); + + const curlTools = [ + `# ── Tools ───────────────────────────────────────────`, + `# Roll 3d20`, + `curl -s "${origin}/dice?d=20&count=3"`, + ``, + `# Generate a 24-char password (full charset)`, + `curl -s "${origin}/password?length=24&charset=full"`, + ``, + `# Flip a quantum coin`, + `curl -s "${origin}/coin"`, + ``, + `# Shake the Quantum 8 Ball`, + `curl -s "${origin}/8ball?question=Will+it+work"`, + ].join('\n'); + + const curlHealth = [ + `# ── Health & Discovery ───────────────────────────────`, + `curl -s "${origin}/health" | jq`, + `curl -s "${origin}/cameras" | jq`, + `curl -s "${origin}/.well-known/mcp.json" | jq`, + ].join('\n'); + + document.getElementById('code-curl').textContent = + curlRandom + '\n\n' + curlStream + '\n\n' + curlTools + '\n\n' + curlHealth; + + // ── Python ──────────────────────────────────────────────── + const pyRandom = fmt === 'raw' + ? `import requests\n\n# Random bytes\nresp = requests.get("${origin}/random?bytes=${bytes}")\nrandom_bytes = resp.content # ${bytes} bytes\nprint(random_bytes.hex())` + : `import requests\n\n# Random bytes\nresp = requests.get("${url}")\nprint(resp.text)`; + + const pyStream = [ + `\n# Stream random data`, + `with requests.get("${origin}/stream?hex=true", stream=True) as r:`, + ` for chunk in r.iter_content(chunk_size=64):`, + ` print(chunk.decode(), end="")`, + ].join('\n'); + + const pyTools = [ + `\n# Dice / Password / Coin / 8 Ball`, + `print(requests.get("${origin}/dice?d=6&count=5").json())`, + `print(requests.get("${origin}/password?length=20").json()["password"])`, + `print(requests.get("${origin}/coin").json()["result"])`, + `print(requests.get("${origin}/8ball").json()["answer"])`, + ].join('\n'); + + document.getElementById('code-python').textContent = + pyRandom + '\n\n' + pyStream + '\n\n' + pyTools; + + // ── JavaScript ──────────────────────────────────────────── + const jsRandom = fmt === 'raw' + ? `// Random bytes\nconst resp = await fetch("${origin}/random?bytes=${bytes}");\nconst buf = new Uint8Array(await resp.arrayBuffer());\nconsole.log([...buf].map(b => b.toString(16).padStart(2,'0')).join(''));` + : `// Random bytes\nconst resp = await fetch("${url}");\nconsole.log(await resp.text());`; + + const jsStream = [ + `\n// Stream random data`, + `const stream = await fetch("${origin}/stream?hex=true");`, + `const reader = stream.body.getReader();`, + `const decoder = new TextDecoder();`, + `while (true) {`, + ` const { done, value } = await reader.read();`, + ` if (done) break;`, + ` process.stdout.write(decoder.decode(value));`, + `}`, + ].join('\n'); + + const jsTools = [ + `\n// Dice / Password / Coin / 8 Ball`, + `const dice = await (await fetch("${origin}/dice?d=20&count=3")).json();`, + `const pw = await (await fetch("${origin}/password?length=24&charset=full")).json();`, + `const coin = await (await fetch("${origin}/coin")).json();`, + `const ball = await (await fetch("${origin}/8ball")).json();`, + ].join('\n'); + + document.getElementById('code-js').textContent = + jsRandom + '\n\n' + jsStream + '\n\n' + jsTools; + + // ── Rust ────────────────────────────────────────────────── + document.getElementById('code-rust').textContent = [ + `use reqwest;`, + ``, + `#[tokio::main]`, + `async fn main() -> Result<(), Box> {`, + ` // Random bytes`, + ` let data = reqwest::get("${url}")`, + ` .await?.${fmt === 'raw' ? 'bytes' : 'text'}().await?;`, + ` println!("{:?}", data);`, + ``, + ` // Stream (reads chunks until server closes)`, + ` let mut stream = reqwest::get("${origin}/stream?bytes=1024&hex=true")`, + ` .await?;`, + ` while let Some(chunk) = stream.chunk().await? {`, + ` print!("{}", String::from_utf8_lossy(&chunk));`, + ` }`, + ` Ok(())`, + `}`, + ].join('\n'); + + // ── Go ──────────────────────────────────────────────────── + document.getElementById('code-go').textContent = [ + `package main`, + ``, + `import (`, + ` "fmt"`, + ` "io"`, + ` "net/http"`, + ` "os"`, + `)`, + ``, + `func main() {`, + ` // Random bytes`, + ` resp, _ := http.Get("${url}")`, + ` body, _ := io.ReadAll(resp.Body)`, + ` resp.Body.Close()`, + ` fmt.Println(string(body))`, + ``, + ` // Stream (reads until EOF or byte limit)`, + ` sr, _ := http.Get("${origin}/stream?bytes=1024&hex=true")`, + ` defer sr.Body.Close()`, + ` io.Copy(os.Stdout, sr.Body)`, + `}`, + ].join('\n'); + document.getElementById('mcp-url').href = origin + '/.well-known/mcp.json'; document.getElementById('mcp-url').textContent = origin + '/.well-known/mcp.json'; document.getElementById('code-mcp').textContent = `curl -s "${origin}/.well-known/mcp.json" | jq`; @@ -572,6 +695,7 @@ updateCodeExamples(); + // Click-to-copy for output boxes const copyToast = document.getElementById('copy-toast'); let copyTimeout;