strix/README.md

88 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# STRIX
### Satellite True Random Information eXtractor
[![Arduino MKR Vidor 4000](https://img.shields.io/badge/Arduino-MKR%20Vidor%204000-00878F?logo=arduino&logoColor=white)](https://docs.arduino.cc/hardware/mkr-vidor-4000/)
[![Built with Devbox](https://www.jetify.com/img/devbox/shield_galaxy.svg)](https://github.com/jetify-com/devbox/)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue)](LICENSE)
[![AI-DECLARATION: copilot](https://img.shields.io/badge/䷼%20AI--DECLARATION-copilot-fee2e2?labelColor=fee2e2)](https://ai-declaration.md)
A portable wireless True Random Number Generator built on an Arduino MKR Vidor 4000.
## Design
Entropy comes from FPGA ring oscillators whose sampled timing jitter is XOR-combined into output bits, with no pseudo-random post-processing. A SAMD21 serves the results over USB, over HTTP on its own WiFi access point (stationary mode, on a charger), or over Bluetooth LE (mobile mode, on battery).
- 4 independent generators × 90 ring oscillators. Each generator XORs its rings every 120 MHz clock (spatial) and then several consecutive bits (temporal, default 5): about 24 Mbps per generator.
- Separate stream per generator, plus a combined stream of all healthy generators
- On-device health tests per generator: NIST SP 800-90B repetition count and adaptive proportion, plus a dead-ring monitor
- Live statistics per trial: bias, z-score, random walk and lag-1 autocorrelation, with rolling and cumulative windows
- Configurable random-walk / majority-vote bias amplifier on a bias path and an autocorrelation path, with sub-trial voting
- Von Neumann extractor that can be toggled on the stream, statistics and amplifier paths
- Per-second trials in Global Consciousness Project format (200-bit XOR-masked sums per generator, plus full-rate sums), with Stouffer Z and network variance
- Raw ring captures and frequency measurement for characterization
```mermaid
flowchart LR
subgraph FPGA["FPGA: Cyclone 10 LP, 120 MHz from a PLL on the SAMD21's 48 MHz clock"]
direction LR
subgraph GEN["4 generators"]
direction TB
RO["90 ring oscillators<br/>3 taps XORed per ring"] --> SMP["2-flip-flop sampling"]
SMP --> SX["spatial XOR<br/>of all rings"]
SX --> TX["temporal XOR<br/>of 1-8 bits"]
end
SMP --> HM["health monitors<br/>repetition count, adaptive<br/>proportion, dead rings"]
SX --> HM
TX --> SEL["healthy generators<br/>g0-g3 and combined XOR"]
HM -->|healthy| SEL
SEL --> ST["entropy stream<br/>4 KB FIFO"]
SEL --> STATS["statistics<br/>per-trial counts"]
SEL --> AMP["bias amplifier<br/>2 paths, 3 stages"]
SEL --> GCP["GCP trials<br/>per second"]
GEN -.-> CAP["raw capture,<br/>frequency counter"]
ST & STATS & AMP & GCP & CAP & HM --> REG["SPI registers"]
PT["WiFi module<br/>pin pass-through"]
end
REG <-->|SPI 8 MHz| MCU["SAMD21 firmware"]
MCU <--> PT
PT <--> NINA["NINA-W102<br/>WiFi and BLE"]
MCU --> USB["USB serial"]
NINA --> HTTP["HTTP on its own<br/>access point"]
NINA --> BLE["BLE GATT service"]
```
The USB, HTTP and BLE API is documented in [arduino/strix/API.md](arduino/strix/API.md).
## Command-line client
`tools/strix.py` talks to the device over USB; run it with `devbox run strix <command>` or `uv run tools/strix.py <command>`. It finds the board by its USB ID (override with `--port` or `STRIX_PORT`).
| Command | Does |
|---|---|
| `id`, `health`, `amp`, `get` | Prints the device's reply |
| `stats [--reset]` | Prints the statistics, or restarts them |
| `set KEY VALUE` | Changes a setting (keys in the API documentation) |
| `entropy N [--out FILE]` | N entropy bytes as hex, or binary into a file |
| `watch stats\|amp` | Prints every new trial until Ctrl-C |
| `gcp [--log FILE] [--full] [--seconds N]` | Follows the per-second GCP-format trials |
| `send LINE` | Sends any command line and prints the reply |
`gcp` prints one line per second: UTC time, second number, the four 200-bit sums, Stouffer Z, Z², and the cumulative Z² − 1 with its p = 0.05 envelope (`*` when outside). `--full` tracks the full-rate trials instead. It reconnects after USB dropouts and fills missed seconds from the device's 60-second history. `--log` appends GCP-style CSV rows, `13,<unix time>,<UTC date-time>,<g0>,<g1>,<g2>,<g3>` (empty for unhealthy generators), after a `12,gmtime,Date/Time,g0,g1,g2,g3` header.
## HTTP server for a USB-connected STRIX
`tools/strix_serve.py` (`devbox run serve`) serves the device's WiFi HTTP API routes for a board on USB, so the same clients work with either. Options: `--listen ADDRESS:PORT` (default `127.0.0.1:8080`, use `0.0.0.0:8080` for the network), `--port` for the serial port, `--gcp-log FILE` to append every per-second trial to a JSON-lines file, and `--set-token-file FILE` to require that file's token for `/set` (header `Authorization: Bearer <token>`), for servers reachable by others.
Since only one program can use the USB port, the server follows the GCP trials itself: `/gcp` serves the last hour instead of the device's 60 seconds, and every record gets a `time` field (Unix time at the start of the second, from the host clock). `/entropy` accepts up to 1 MiB. The server reconnects after USB dropouts; requests in the meantime get status 503.
The API is described in OpenAPI 3 at `/openapi.json` (`tools/strix_openapi.json`), with an interactive Swagger UI at `/docs` (its scripts load from a CDN).
## TODO
- WiFi credentials stored on the device instead of compile-time defaults
- Battery pack
- 3D case
---
## [Development Notes](NOTES.md)