Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- README.md +45 -45
- app.py +5 -2
- lr_entropy_api.py +10 -1
README.md
CHANGED
|
@@ -1,45 +1,45 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Light Rider Quantum Entropy
|
| 3 |
-
emoji: ⚛️
|
| 4 |
-
colorFrom: red
|
| 5 |
-
colorTo: yellow
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version: 6.30.0
|
| 8 |
-
app_file: app.py
|
| 9 |
-
pinned: false
|
| 10 |
-
license: apache-2.0
|
| 11 |
-
short_description: Entropy from named quantum sources, with signed receipts
|
| 12 |
-
---
|
| 13 |
-
|
| 14 |
-
# Light Rider Quantum Entropy
|
| 15 |
-
|
| 16 |
-
Draw random bytes from **named quantum sources** (quantum random number generators
|
| 17 |
-
and quantum processors). Every draw comes with a **receipt signed by the Light Rider
|
| 18 |
-
Entropy Management Service (EMS)**: it records which source produced the bytes, how
|
| 19 |
-
they were health-checked and when. The app verifies the receipt's Ed25519 signature
|
| 20 |
-
against EMS's published key and shows the result.
|
| 21 |
-
|
| 22 |
-
This is an alternative entropy source with verifiable provenance. It is not a claim
|
| 23 |
-
that quantum randomness is better than a good classical generator. If a source can't
|
| 24 |
-
serve, you'll see why. Nothing is ever substituted, and nothing is generated locally.
|
| 25 |
-
|
| 26 |
-
## Use it from your code
|
| 27 |
-
|
| 28 |
-
```python
|
| 29 |
-
import lightrider as lr # pip install lightrider (1.6.0+), export LR_API_KEY=lr_live_...
|
| 30 |
-
r = lr.entropy.get(
|
| 31 |
-
print(r.hex, r.request_id, r.sources)
|
| 32 |
-
assert lr.entropy.verify(r.receipt)
|
| 33 |
-
seed = lr.entropy.seed(64) # int seed for numpy / torch
|
| 34 |
-
```
|
| 35 |
-
|
| 36 |
-
Get your Light Rider API key from Light Rider Cloud (Settings → API Keys). It is a
|
| 37 |
-
full-access account key: keep it out of public code and store it as a Space secret.
|
| 38 |
-
|
| 39 |
-
## Space configuration
|
| 40 |
-
|
| 41 |
-
Secrets: `LR_API_KEY` (demo account key), `LR_ENTROPY_BASE_URL`, and
|
| 42 |
-
`LR_VERCEL_BYPASS` (only while pointing at a protected preview deployment).
|
| 43 |
-
|
| 44 |
-
Optional variables: `DEMO_PER_VISITOR_DAILY` (default 5), `DEMO_GLOBAL_DAILY`
|
| 45 |
-
(default 200), `LR_PUBLIC_SITE_URL`, `LR_CLOUD_KEYS_URL`.
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Light Rider Quantum Entropy
|
| 3 |
+
emoji: ⚛️
|
| 4 |
+
colorFrom: red
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 6.30.0
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: apache-2.0
|
| 11 |
+
short_description: Entropy from named quantum sources, with signed receipts
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# Light Rider Quantum Entropy
|
| 15 |
+
|
| 16 |
+
Draw random bytes from **named quantum sources** (quantum random number generators
|
| 17 |
+
and quantum processors). Every draw comes with a **receipt signed by the Light Rider
|
| 18 |
+
Entropy Management Service (EMS)**: it records which source produced the bytes, how
|
| 19 |
+
they were health-checked and when. The app verifies the receipt's Ed25519 signature
|
| 20 |
+
against EMS's published key and shows the result.
|
| 21 |
+
|
| 22 |
+
This is an alternative entropy source with verifiable provenance. It is not a claim
|
| 23 |
+
that quantum randomness is better than a good classical generator. If a source can't
|
| 24 |
+
serve, you'll see why. Nothing is ever substituted, and nothing is generated locally.
|
| 25 |
+
|
| 26 |
+
## Use it from your code
|
| 27 |
+
|
| 28 |
+
```python
|
| 29 |
+
import lightrider as lr # pip install lightrider (1.6.0+), export LR_API_KEY=lr_live_...
|
| 30 |
+
r = lr.entropy.get(16, source="anu") # or sources=["anu", "cisco"]
|
| 31 |
+
print(r.hex, r.request_id, r.sources)
|
| 32 |
+
assert lr.entropy.verify(r.receipt)
|
| 33 |
+
seed = lr.entropy.seed(64) # int seed for numpy / torch
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
Get your Light Rider API key from Light Rider Cloud (Settings → API Keys). It is a
|
| 37 |
+
full-access account key: keep it out of public code and store it as a Space secret.
|
| 38 |
+
|
| 39 |
+
## Space configuration
|
| 40 |
+
|
| 41 |
+
Secrets: `LR_API_KEY` (demo account key), `LR_ENTROPY_BASE_URL`, and
|
| 42 |
+
`LR_VERCEL_BYPASS` (only while pointing at a protected preview deployment).
|
| 43 |
+
|
| 44 |
+
Optional variables: `DEMO_PER_VISITOR_DAILY` (default 5), `DEMO_GLOBAL_DAILY`
|
| 45 |
+
(default 200), `LR_PUBLIC_SITE_URL`, `LR_CLOUD_KEYS_URL`.
|
app.py
CHANGED
|
@@ -115,7 +115,8 @@ def visitor_id(request: gr.Request | None) -> str:
|
|
| 115 |
|
| 116 |
def _label(s: lre.Source) -> str:
|
| 117 |
state = f"{s.bytes_available:,} B on hand" if s.ready else "out of entropy, refill pending"
|
| 118 |
-
|
|
|
|
| 119 |
|
| 120 |
|
| 121 |
def load_sources():
|
|
@@ -251,12 +252,14 @@ export LR_API_KEY=lr_live_... # your Light Rider API key
|
|
| 251 |
```python
|
| 252 |
import lightrider as lr
|
| 253 |
|
| 254 |
-
r = lr.entropy.get(
|
| 255 |
print(r.hex, r.request_id, r.sources)
|
| 256 |
assert lr.entropy.verify(r.receipt) # Ed25519 against EMS's published key
|
| 257 |
seed = lr.entropy.seed(64) # int seed for numpy / torch
|
| 258 |
```
|
| 259 |
|
|
|
|
|
|
|
| 260 |
- **Get your key:** [Light Rider Cloud → API Keys]({CLOUD_KEYS_URL}). It is your account's full-access key, so keep it
|
| 261 |
out of public code and Spaces (store it as a Space secret).
|
| 262 |
- **Docs and the HTTP API:** [{PUBLIC_SITE}/developers]({PUBLIC_SITE}/developers)
|
|
|
|
| 115 |
|
| 116 |
def _label(s: lre.Source) -> str:
|
| 117 |
state = f"{s.bytes_available:,} B on hand" if s.ready else "out of entropy, refill pending"
|
| 118 |
+
name = f"{s.name} ({s.alias})" if s.alias else s.name
|
| 119 |
+
return f"{name} · {s.type} · {state}"
|
| 120 |
|
| 121 |
|
| 122 |
def load_sources():
|
|
|
|
| 252 |
```python
|
| 253 |
import lightrider as lr
|
| 254 |
|
| 255 |
+
r = lr.entropy.get(16, source="anu") # or sources=["anu", "cisco"]
|
| 256 |
print(r.hex, r.request_id, r.sources)
|
| 257 |
assert lr.entropy.verify(r.receipt) # Ed25519 against EMS's published key
|
| 258 |
seed = lr.entropy.seed(64) # int seed for numpy / torch
|
| 259 |
```
|
| 260 |
|
| 261 |
+
- **Sources** by short name: `anu`, `cisco`, `qispace`, `rigetti`, `iqm`, `iqm-qec` (or the full id);
|
| 262 |
+
`lr.entropy.sources()` lists what's ready now. Receipts always record the full id.
|
| 263 |
- **Get your key:** [Light Rider Cloud → API Keys]({CLOUD_KEYS_URL}). It is your account's full-access key, so keep it
|
| 264 |
out of public code and Spaces (store it as a Space secret).
|
| 265 |
- **Docs and the HTTP API:** [{PUBLIC_SITE}/developers]({PUBLIC_SITE}/developers)
|
lr_entropy_api.py
CHANGED
|
@@ -10,7 +10,7 @@ Quantum entropy with signed provenance, billed to your Light Rider credits:
|
|
| 10 |
|
| 11 |
import lightrider as lr
|
| 12 |
|
| 13 |
-
r = lr.entropy.get(
|
| 14 |
r.data, r.hex, r.request_id # the bytes and their receipt id
|
| 15 |
lr.entropy.verify(r.receipt) # True: EMS signed exactly this
|
| 16 |
|
|
@@ -209,6 +209,8 @@ class Source:
|
|
| 209 |
max_single_draw_bytes: int
|
| 210 |
custom_pool_selectable: bool
|
| 211 |
custom_pool_reason: str | None
|
|
|
|
|
|
|
| 212 |
raw: dict = field(repr=False, default_factory=dict)
|
| 213 |
|
| 214 |
@property
|
|
@@ -358,6 +360,7 @@ class EntropyAPI:
|
|
| 358 |
max_single_draw_bytes=int(s.get("max_single_draw_bytes", 0)),
|
| 359 |
custom_pool_selectable=bool((s.get("custom_pool") or {}).get("selectable", False)),
|
| 360 |
custom_pool_reason=(s.get("custom_pool") or {}).get("reason"),
|
|
|
|
| 361 |
raw=s,
|
| 362 |
)
|
| 363 |
for s in body.get("sources", [])
|
|
@@ -374,6 +377,12 @@ class EntropyAPI:
|
|
| 374 |
"""Draw ``n_bytes`` (1-4096) from one source (``source=``) or a custom
|
| 375 |
pool of 2-8 sources (``sources=[...]``).
|
| 376 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
With neither, uses ``default_source`` / ``LR_ENTROPY_SOURCE``, else the
|
| 378 |
first source that is ready right now (the receipt says which).
|
| 379 |
Raises :class:`SourceEmptyError` rather than ever substituting.
|
|
|
|
| 10 |
|
| 11 |
import lightrider as lr
|
| 12 |
|
| 13 |
+
r = lr.entropy.get(16, source="anu") # LR_API_KEY in the environment
|
| 14 |
r.data, r.hex, r.request_id # the bytes and their receipt id
|
| 15 |
lr.entropy.verify(r.receipt) # True: EMS signed exactly this
|
| 16 |
|
|
|
|
| 209 |
max_single_draw_bytes: int
|
| 210 |
custom_pool_selectable: bool
|
| 211 |
custom_pool_reason: str | None
|
| 212 |
+
#: Short name usable as ``source=`` / in ``sources=`` (e.g. ``"anu"``).
|
| 213 |
+
alias: str | None = None
|
| 214 |
raw: dict = field(repr=False, default_factory=dict)
|
| 215 |
|
| 216 |
@property
|
|
|
|
| 360 |
max_single_draw_bytes=int(s.get("max_single_draw_bytes", 0)),
|
| 361 |
custom_pool_selectable=bool((s.get("custom_pool") or {}).get("selectable", False)),
|
| 362 |
custom_pool_reason=(s.get("custom_pool") or {}).get("reason"),
|
| 363 |
+
alias=s.get("alias"),
|
| 364 |
raw=s,
|
| 365 |
)
|
| 366 |
for s in body.get("sources", [])
|
|
|
|
| 377 |
"""Draw ``n_bytes`` (1-4096) from one source (``source=``) or a custom
|
| 378 |
pool of 2-8 sources (``sources=[...]``).
|
| 379 |
|
| 380 |
+
Name sources by alias (``"anu"``, ``"cisco"``, ...; see
|
| 381 |
+
:meth:`sources`) or full id (``"anu_aws_001"``). Names are passed to
|
| 382 |
+
the service as given and resolved there, so new sources need no SDK
|
| 383 |
+
update; an unknown name raises :class:`InvalidRequestError` listing
|
| 384 |
+
the valid ones. Receipts always carry the full id.
|
| 385 |
+
|
| 386 |
With neither, uses ``default_source`` / ``LR_ENTROPY_SOURCE``, else the
|
| 387 |
first source that is ready right now (the receipt says which).
|
| 388 |
Raises :class:`SourceEmptyError` rather than ever substituting.
|