Add comprehensive curl/stream examples to all code tabs and skill.md

- 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 <cursoragent@cursor.com>
This commit is contained in:
Leopere 2026-02-09 13:50:56 -05:00
parent 5b49685ae9
commit 1ce1c833b6
Signed by: colin
SSH Key Fingerprint: SHA256:nRPCQTeMFLdGytxRQmPVK9VXY3/ePKQ5lGRyJhT5DY8
2 changed files with 283 additions and 62 deletions

191
skill.md
View File

@ -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: 2256): 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

View File

@ -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<dyn std::error::Error>> {\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<dyn std::error::Error>> {`,
` // 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;