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:
parent
5b49685ae9
commit
1ce1c833b6
191
skill.md
191
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
|
||||
|
|
|
|||
154
src/index.html
154
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<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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue