feat: add HTTP server with OpenAPI docs and token-protected settings for a USB-connected STRIX
This commit is contained in:
parent
9a2ec11a4c
commit
1196bbc6af
5 changed files with 934 additions and 1 deletions
675
tools/strix_openapi.json
Normal file
675
tools/strix_openapi.json
Normal file
|
|
@ -0,0 +1,675 @@
|
|||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "STRIX HTTP API",
|
||||
"version": "0.4.0",
|
||||
"description": "HTTP API of a STRIX true random number generator connected over USB, served by tools/strix_serve.py. The routes match the device's own WiFi HTTP API (arduino/strix/API.md); this server adds the `time` field to GCP records, keeps an hour of them, accepts larger entropy requests and answers 503 while the device is unreachable. Device errors are JSON objects with an `error` field and status 200."
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "/"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "entropy",
|
||||
"description": "Random bytes from the entropy stream"
|
||||
},
|
||||
{
|
||||
"name": "device",
|
||||
"description": "Identity, health, statistics and the bias amplifier"
|
||||
},
|
||||
{
|
||||
"name": "settings",
|
||||
"description": "Runtime settings"
|
||||
},
|
||||
{
|
||||
"name": "gcp",
|
||||
"description": "Per-second trials in Global Consciousness Project format"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/entropy": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"entropy"
|
||||
],
|
||||
"summary": "Entropy bytes",
|
||||
"description": "Bytes from the stream source (setting `stream_source`, default: the XOR of all healthy generators). Refused while that source is not healthy.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "bytes",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 1048576
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"hex",
|
||||
"bin"
|
||||
],
|
||||
"default": "hex"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Uppercase hex, raw bytes, or an error",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "3F09A2C47E11D05B"
|
||||
},
|
||||
"application/octet-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"entropy"
|
||||
],
|
||||
"summary": "Entropy bytes as hex (compatibility route)",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "length",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 1048576
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Uppercase hex",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/components/responses/NotFound"
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/id": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"device"
|
||||
],
|
||||
"summary": "Device identity",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Identity",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Id"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"device"
|
||||
],
|
||||
"summary": "Generator health and stream state",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Health",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Health"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/stats": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"device"
|
||||
],
|
||||
"summary": "Live statistics of the latest trial",
|
||||
"description": "Per channel (`g0`-`g3`, `combined`): bits, ones, bias z and lag-1 autocorrelation z for the trial, the rolling window and the cumulative total (with random walk position).",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Statistics",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/amp": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"device"
|
||||
],
|
||||
"summary": "Latest bias amplifier trial",
|
||||
"description": "Per path (`bias`, `autocorr`): amplified bits, ones, z, sub-trial votes (`1`, `0`, `.` for no vote) and the trial result (null on a tie).",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Amplifier trial",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wifi": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"device"
|
||||
],
|
||||
"summary": "WiFi module status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "WiFi status",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/config": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"settings"
|
||||
],
|
||||
"summary": "All settings",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Settings",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Config"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/set": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"settings"
|
||||
],
|
||||
"summary": "Change a setting",
|
||||
"description": "Value ranges are listed in arduino/strix/API.md. Changing `trial_ms` or `stats_vn` restarts the statistics, `amp_*` the amplifier; `stream_*` flushes the stream. When the server runs with --set-token-file, requests need that token as a bearer token.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "key",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"temporal",
|
||||
"generators",
|
||||
"radio_generators",
|
||||
"trial_ms",
|
||||
"stats_vn",
|
||||
"rolling",
|
||||
"stream_source",
|
||||
"stream_vn",
|
||||
"amp_sources",
|
||||
"amp_method",
|
||||
"amp_stages",
|
||||
"amp_sub_ms",
|
||||
"amp_subtrials",
|
||||
"amp_vn"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "5"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Confirmation or error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Ok"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing or wrong token",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"$ref": "#/components/responses/Unavailable"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"setToken": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/gcp": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"gcp"
|
||||
],
|
||||
"summary": "Per-second GCP-format trials",
|
||||
"description": "Without `since`: the latest second. With `since`: every kept second after it, oldest first (the last hour is kept). Polling with `since` set to the last second received loses no seconds. Second numbers restart when the device restarts.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "since",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "One record, a list of records, or an error before the first second",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/GcpRecord"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"records"
|
||||
],
|
||||
"properties": {
|
||||
"records": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GcpRecord"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/openapi.json": {
|
||||
"get": {
|
||||
"summary": "This specification",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OpenAPI document",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"responses": {
|
||||
"Unavailable": {
|
||||
"description": "The device is not connected",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"NotFound": {
|
||||
"description": "Unknown path",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"Error": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Ok": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ok"
|
||||
],
|
||||
"properties": {
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Id": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"device": {
|
||||
"type": "string",
|
||||
"example": "STRIX"
|
||||
},
|
||||
"firmware": {
|
||||
"type": "string",
|
||||
"example": "0.4.0"
|
||||
},
|
||||
"fpga_ready": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"fpga_version": {
|
||||
"type": "integer",
|
||||
"example": 10
|
||||
},
|
||||
"generators": {
|
||||
"type": "integer",
|
||||
"example": 4
|
||||
},
|
||||
"rings_per_generator": {
|
||||
"type": "integer",
|
||||
"example": 90
|
||||
},
|
||||
"reset_cause": {
|
||||
"type": "integer",
|
||||
"description": "SAMD21 PM->RCAUSE: 1 power-on, 4 brown-out, 16 reset pin, 64 software"
|
||||
},
|
||||
"external_power": {
|
||||
"type": "boolean",
|
||||
"description": "Charger or USB power present at boot"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Health": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"healthy": {
|
||||
"type": "integer",
|
||||
"description": "Bit mask of healthy generators"
|
||||
},
|
||||
"generators": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"active": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"swept": {
|
||||
"type": "boolean",
|
||||
"description": "A dead-ring sweep has completed"
|
||||
},
|
||||
"rct_failed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"apt_failed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"rct_events": {
|
||||
"type": "integer"
|
||||
},
|
||||
"apt_events": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dead_rings": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"stream": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"level": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dropped": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"temporal": {
|
||||
"type": "integer"
|
||||
},
|
||||
"generators": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trial_ms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"stats_vn": {
|
||||
"type": "integer"
|
||||
},
|
||||
"rolling": {
|
||||
"type": "integer"
|
||||
},
|
||||
"stream_source": {
|
||||
"type": "integer"
|
||||
},
|
||||
"stream_vn": {
|
||||
"type": "integer"
|
||||
},
|
||||
"amp_sources": {
|
||||
"type": "integer"
|
||||
},
|
||||
"amp_method": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"walk",
|
||||
"vote"
|
||||
]
|
||||
},
|
||||
"amp_stages": {
|
||||
"type": "string",
|
||||
"example": "8,8,4"
|
||||
},
|
||||
"amp_sub_ms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"amp_subtrials": {
|
||||
"type": "integer"
|
||||
},
|
||||
"amp_vn": {
|
||||
"type": "integer"
|
||||
},
|
||||
"radio_generators": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TrialSet": {
|
||||
"type": "object",
|
||||
"description": "Per-generator sums of XOR-masked bits; null for generators not healthy for the whole second",
|
||||
"properties": {
|
||||
"bits": {
|
||||
"type": "integer",
|
||||
"description": "Bits per generator"
|
||||
},
|
||||
"sums": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"z": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number",
|
||||
"nullable": true
|
||||
},
|
||||
"description": "(2 sum - bits) / sqrt(bits)"
|
||||
},
|
||||
"stouffer": {
|
||||
"type": "number",
|
||||
"nullable": true,
|
||||
"description": "Sum of z over sqrt(healthy generators)"
|
||||
},
|
||||
"netvar": {
|
||||
"type": "number",
|
||||
"nullable": true,
|
||||
"description": "stouffer squared, chi-square with one degree of freedom"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GcpRecord": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"time": {
|
||||
"type": "integer",
|
||||
"description": "Unix time at the start of the second, from the host clock"
|
||||
},
|
||||
"second": {
|
||||
"type": "integer",
|
||||
"description": "Seconds since the FPGA started"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "integer",
|
||||
"description": "Bit mask of generators healthy for the whole second"
|
||||
},
|
||||
"sample": {
|
||||
"$ref": "#/components/schemas/TrialSet"
|
||||
},
|
||||
"full": {
|
||||
"$ref": "#/components/schemas/TrialSet"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes": {
|
||||
"setToken": {
|
||||
"type": "http",
|
||||
"scheme": "bearer",
|
||||
"description": "Token from the server's --set-token-file"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue