Metadata-Version: 2.1
Name: wj-diode-quantum
Version: 1.0.0
Summary: Quantum random numbers from ANU, pushed across a data diode and served on the other side
Home-page: https://github.com/WaterJuice/wj-diode-quantum
Author: WaterJuice
License: Unlicense
Requires-Python: >=3.7
Classifier: Programming Language :: Go
Description-Content-Type: text/markdown
# wj-diode-quantum

Collects quantum random numbers from the Australian National University, streams them across a data
diode, and serves them one at a time on the protected side. Each value is 1024 bits.

A data diode only carries data one way, so a machine behind one cannot ask ANU for random numbers
itself. This program splits the job in two: one side collects and pushes, the other stockpiles what
arrives and hands it out on request.

## Quick start

Two machines, one command each.

### Machine 1 — internet side

```sh
uv tool install wj-diode-quantum
wj-diode-quantum setup send
wj-diode-quantum collect -c config.json
```

### Machine 2 — protected side

```sh
uv tool install wj-diode-quantum
wj-diode-quantum setup recv
wj-diode-quantum serve -c config.json
```

### Check it works

```sh
curl localhost:8099/api/status    # numbers are arriving
curl localhost:8099/api/random    # take one
```

```json
{
  "value": "74b033a4332c7112cf0c3ff5...67f6b30ee",
  "timestamp": "2026-08-02T04:14:58Z",
  "source": "quantumnumbers",
  "bits": 1024
}
```

`value` is your 1024-bit random number, 256 lowercase hex characters. `timestamp` is when it was
collected. You will never be given that number again.

Or without the JSON around it — each of these carries the same metadata in `X-Quantum-*` headers:

```sh
curl localhost:8099/api/random/hex               # 256 hex characters
curl localhost:8099/api/random/base64            # the same value, base64
curl -o key.bin localhost:8099/api/random/binary # exactly 128 raw bytes
```

Open `http://localhost:8099/` in a browser for the same thing with live pool figures, or
`curl localhost:8099/api/help` for it as plain text. There is an OpenAPI description at
`/api/spec`.

Full documentation: [docs.waterjuice.org/wj-diode-quantum](https://docs.waterjuice.org/wj-diode-quantum/latest/)

## Features

- **The numbers are never written in the clear.** The collecting side writes nothing at all. The
  serving side can keep its pool across restarts, but only encrypted, under a passphrase held in
  the environment and never stored beside the file.
- **Both ANU APIs** — the current keyed service and the older unkeyed one, each on its own schedule
  sized to its rate limit. Enable either or both.
- **Any diode tool** — the transfer is a configured external command reading and writing a stream.
  Built against `diodetool`, but nothing about it is hard-coded.
- **Never serves the same number twice** — including across a restart, when the pool is persisted. A
  value is recorded as served on disk before it reaches the client, so a crash mid-request can only
  lose it, never hand it out again.
- **Four output formats** — JSON, hex, base64 and raw bytes, with the metadata in response headers.
- **Bounded, self-trimming pool** — newest served first, oldest discarded at capacity.
- **One process per machine** — the receiving side runs the diode tool for you.
- **Damage is contained** — a diode cannot retransmit, so a corrupted value is dropped and the
  stream resynchronises rather than losing everything behind it.
- **Zero dependencies** — a single static binary, Go standard library only.

## Requirements

- A tool that can move a file across your diode, on both sides.
- An ANU API key from [quantumnumbers.anu.edu.au](https://quantumnumbers.anu.edu.au/api-key), free
  with an account. Optional if you only use the legacy endpoint.

## Building

```sh
make build        # wheels for all platforms, plus documentation
make check        # tests, format check, and vet
make dev          # run from source via a .venv launcher
```

## How it fits together

```
  internet side                     diode                  protected side
  ─────────────                     ─────                  ──────────────
  ANU ──> collect ──> [ stream-send ] ══════> [ stream-recv ] ──> serve ──> GET /api/random
                            stdin                  stdout          │
                                                                   v
                                                          pool (encrypted on
                                                           disk, or memory)
```

Nothing comes back, so the collector never learns whether anything arrived. On the sending side
nothing is stored at all: a transfer the diode will not take is retried and then discarded. On the
serving side the pool may be kept across restarts, encrypted; without that it is lost on restart and
refills from the far side's next cycle.

## Security

There is **no authentication on the API**. Anyone who can reach the port can drain the pool. The
default binds to `127.0.0.1` — keep it on loopback or a trusted network, or put a reverse proxy in
front of it. Supply is finite, so a rate limit matters as much as authentication.

Diode passwords are written to a file with mode `0600` and passed with `--password-file` rather than
on a command line, where `ps` would expose them.

The pool file is AES-256-GCM under a key stretched from `$WJ_POOL_KEY` with PBKDF2-HMAC-SHA256, and
is written mode `0600`. That protects it where it travels without the key — backups, volume
snapshots, a disk pulled out of a machine — not against something that can already read both the
file and the process environment. **Lose the key and the pool is gone**: there is no escrow, and a
diode carries nothing back to ask for the numbers again.

Because a diode carries nothing back, the collector cannot be told the server is running low. Watch
`/api/status`, the root page, or `wj-diode-quantum stats` on the protected side.

## Licence

Released under the [Unlicense](https://unlicense.org/) — public domain.

