The worst of it: the ghost-log HUD is a fixed 300px panel pinned bottom-left on every route. On a 390px screen it covered the seance ask-field and its buttons outright — you could not reach the input to type a question. It now collapses, and spans the width instead of blocking it under 560px. - 16 uses of `100vh` meant every page was taller than iOS Safari's visible viewport, hiding the last row behind the toolbar. Now `100dvh` with the `100vh` line kept first as the fallback. - Six inputs under 16px triggered iOS zoom-on-focus, which never zooms back out — you were left panning a zoomed layout after tapping login. All six at 16px, plus a global floor. - No safe-area insets existed anywhere, so enabling viewport-fit=cover would have put content under the notch and home indicator. Added, including the fixed-position shells that ignore body padding. - The codex voice table forced horizontal page scroll; it scrolls in its own container now. - Tap targets under 44px raised, including a transcript replay button that was ~20x14px and named only by a `title` tooltip, which touch never shows. Decorative micro-labels left alone deliberately — bumping them wholesale would reintroduce the overflow this fixes. README: the documented test command cannot work. conftest APPENDS `_test`, so the documented DATABASE_URL derived `quantumancy_test_test` and errored every test at setup. Corrected, with the two guards explained. Test counts were stale (328/366 -> 403/385). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
436 lines
22 KiB
Markdown
436 lines
22 KiB
Markdown
# QUANTUMANCY
|
||
|
||
*an instrument for speaking with the dead bandwidth*
|
||
|
||
Quantumancy is a self-hosted séance. Point a browser at it and real sensing
|
||
channels — your microphone, your phone's motion sensors and magnetometer, an
|
||
RTL-SDR dongle, your own network's jitter, a paired ESP32 sensor node, or
|
||
just a text box — feed real anomaly detection into a locally-run LLM that
|
||
invents a spirit on the spot: a name, an epithet, a persona, a coherent
|
||
voice, hidden traits, a rarity tier, and a small immortality in a shared
|
||
registry called **the Codex**. A local Piper TTS then gives that spirit an
|
||
actual voice, degraded through a hand-built static-and-echo effects chain
|
||
until it sounds like it's coming through a dying radio.
|
||
|
||
And the physical world genuinely participates: every summon carries entropy
|
||
harvested from your room's own noise floors, tonight's **real moon phase**
|
||
skews who answers, NOAA's **measured geomagnetic K-index** thins the veil,
|
||
and unprompted speech is token-sampled with a seed derived from the room —
|
||
the room literally selects the words.
|
||
|
||
**This is an interactive horror art installation, not a paranormal claim.**
|
||
Every "entity" is fiction generated by a language model on your own
|
||
hardware — the system prompts are explicit about this (see
|
||
`backend/app/llm/prompts.py`). But every *measurement* is real: FFT power
|
||
spectra, magnetometer microtesla, `/proc/net/dev` counters, BLE signal
|
||
attenuation, published lunar ephemerides, NOAA space-weather data. The rule
|
||
(see `docs/CHANNELS.md`): the fiction may interpret a measurement however it
|
||
likes; it may never fabricate one. Nothing leaves your network except a
|
||
courteous 10-minute poll of NOAA's public Kp feed — no cloud LLM, no
|
||
analytics. If the LLM box goes dark mid-séance, a procedural fallback keeps
|
||
every mode answering so a summoning never visibly fails.
|
||
|
||
## The Séance: Ways In
|
||
|
||
Every mode writes into the same pipeline: an `anomaly` event (source,
|
||
frequency, magnitude) feeds a per-session fingerprint; once a session has
|
||
produced at least 3 anomalies of real structure, it auto-summons — either
|
||
re-contacting a Codex entry whose signature matches (a real draw, not a
|
||
guarantee — see *The Room Decides*), or minting someone new.
|
||
|
||
| Mode | Vessel | What actually happens |
|
||
|---|---|---|
|
||
| **Wire Ghost** | Nothing — works for everyone | Real, non-content network telemetry from the host: byte-counter jitter (`/proc/net/dev`), TCP connect-latency variance, DNS timing, against a rolling baseline. Payloads are never inspected — a hard privacy boundary. Passive listening lets the line murmur on its own. |
|
||
| **EVP Listening** | Microphone (`getUserMedia`) | A Web Audio `AnalyserNode` watches the voice band (~300 Hz–3.4 kHz) for deviations ≥ 8 dB over the room's rolling noise floor — live spectrum scope, audible anomalies, and every frame feeds the entropy pool. Works everywhere, including iPhone. |
|
||
| **Spirit Radio** | RTL-SDR dongle (WebUSB, Chromium only) | The browser drives the dongle directly, sweeping FM 88–108 MHz, computing power spectra against a rolling floor. Every USB call is time-boxed (`withTimeout`) so a sulking dongle errors instead of hanging silently. |
|
||
| **Ouija / Direct Contact** | The shared front door | A canvas planchette wanders the full board, brushes letters, visits YES/NO, and rests on GOODBYE every few minutes; questions stream back token by token while it spells. |
|
||
| **EMF / Field Meter** | A phone | Where the Generic Sensor API exists (Chromium/Android), the **actual magnetometer** in microtesla — a true EMF meter, baseline-subtracting Earth's constant field. Everywhere else (including iOS), the motion-fusion fallback: acceleration deviation + orientation angular velocity. Both are real physics; `lib/baseline.ts` gives them one shared rolling-EMA core. |
|
||
| **The Vessel** | ESP32-P4 sensor node (LAN) | Paired hardware POSTs temperature/pressure/mic/radar readings; the Cold Spot Detector and the séance's DeviceWhisper panel render them live, and hardware anomalies can trigger a summon in an open séance. Firmware in `firmware/` — written and reviewed, **never yet flashed to a real board**. |
|
||
|
||
`lib/bluetooth.ts` (BLE RSSI as a presence field — bodies absorb 2.4 GHz)
|
||
is implemented and tested but not yet wired into a séance panel.
|
||
|
||
## The Room Decides
|
||
|
||
`backend/app/entropy.py` · `frontend/src/lib/entropy.ts` ·
|
||
`backend/app/celestial.py` · `backend/app/geomagnetic.py`
|
||
|
||
- **Physical entropy.** The client harvests noise from live sensor frames
|
||
(mic and RF noise floors), Von Neumann–debiases it, SHA-256–conditions
|
||
it, and sends it with every summon. The server treats it as untrusted by
|
||
construction: every draw is `HMAC(fresh server secret, contribution ||
|
||
context)`, so a hostile client can only ever *add* unpredictability,
|
||
never steer an outcome. Whether a channel's familiar spirit answers again
|
||
(`RETURN_CHANCE`) is one of these draws.
|
||
- **Real astronomy.** Moon phase from orbital mechanics (validated against
|
||
published ephemeris dates, not against itself) and true solar midnight
|
||
from the seeker's longitude (only the longitude is kept, never a full
|
||
coordinate). A full moon roughly **triples** rare/mythic mint odds — a
|
||
skew, never a gate — nudges hidden traits stronger and stranger, and
|
||
erodes a familiar spirit's claim on its channel so strangers push
|
||
through on thin nights.
|
||
- **Real space weather.** NOAA SWPC's planetary K-index, cached 10 minutes,
|
||
never blocking: a geomagnetic storm measurably thins the veil, and the
|
||
entity is told `Kp 6.33 — moderate geomagnetic storm` as a fact of its
|
||
room.
|
||
- **Generation from nothing.** `SpiritService.manifest()` produces
|
||
unprompted speech: the prompt contains no seeker input at all — only
|
||
measured room state — and Ollama's sampling `seed` derives from the
|
||
room's entropy. Change the noise, get different words. Rendered in the
|
||
transcript as an intrusion (violet edge), not a reply.
|
||
|
||
## Rituals, Judgment, and the Reliquary
|
||
|
||
Every minted entity carries **hidden traits** (alignment / power /
|
||
volatility / deceptiveness) — signature-seeded ground truth the persona
|
||
prompt never sees, so the LLM cannot leak it. The séance offers:
|
||
|
||
- **The ritual** — a 4-sigil hold-to-charge rite; success reveals the
|
||
entity's true traits, failure reveals nothing. Judgment is never gated on
|
||
it: you may always judge blind.
|
||
- **Judgment** — trust / banish / test / cross over. Correct calls pay
|
||
essence and favor; wrongly trusting a demon sharpens the haunting,
|
||
wrongly banishing a benevolent spirit wounds it, and a correctly-judged
|
||
stuck spirit **crosses over**: at peace, permanently retired from its
|
||
channel, its signature freed for someone new.
|
||
- **The Reliquary** — essence (earned, never bought), item drops on
|
||
milestone moments, purchasable unlocks, and hand-drawn sigils.
|
||
`/shop` remains a waitlist for the **Ultimate Quantum Box** hardware —
|
||
email capture only, no payment taken, and the page says so.
|
||
|
||
A **Ghost Log** HUD whispers entity tells app-wide, and `/log` is the
|
||
recallable record: your last séances with entity, counts, and echo lines.
|
||
|
||
## Coming In From the Cold
|
||
|
||
- **Guests:** `POST /auth/guest` mints a real `wanderer-xxxx` account and a
|
||
normal session — the front door (`/enter`) offers it as *"slip through as
|
||
a wanderer"*, and the séance nudges wanderers to claim a name so their
|
||
codex outlives the mist.
|
||
- **Conditions:** `GET /api/conditions` (and the séance's side-column
|
||
strip) surfaces tonight's moon, veil thinness, and geomagnetic state.
|
||
- **First contact:** each mode shows one in-fiction hint if its sensor sits
|
||
unused, dismissible forever; error copy redirects instead of dead-ending
|
||
(no mic? "the board needs no ear").
|
||
- **PWA:** a `manifest.webmanifest` makes it installable to a home screen
|
||
(standalone, no service worker — an offline séance is meaningless).
|
||
- **Phones:** dedicated ≤560px and ≤380px layouts, 44px touch targets,
|
||
16px inputs (so iOS Safari doesn't zoom-and-stick).
|
||
|
||
## Architecture
|
||
|
||
Two machines on the LAN, no containers anywhere:
|
||
|
||
```
|
||
┌────────────────────────────────────────────┐
|
||
seekers ──HTTPS──▶ Cloudflare Tunnel ──HTTP──▶ App CT :7777 │
|
||
(external machine, FastAPI (uvicorn, │
|
||
terminates TLS) Python venv, │
|
||
systemd service) │
|
||
│ │
|
||
├─ serves built │
|
||
│ React SPA │
|
||
├─ Postgres (apt) │
|
||
├─ Piper TTS + FX │
|
||
└───────┬──────────┘
|
||
│ LAN
|
||
┌──────────────────┐ ┌───────────▼─────────┐
|
||
│ ESP32-P4 node(s) │ │ Ollama box │
|
||
│ (paired, HTTP │ │ 10.30.20.107:11434 │
|
||
│ telemetry in) │ │ CPU-only, 64 GB │
|
||
└──────────────────┘ │ fast + chat models │
|
||
└─────────────────────┘
|
||
...and one WAN egress:
|
||
NOAA SWPC Kp (10-min cache)
|
||
```
|
||
|
||
- **App CT** — FastAPI serves the built Vite/React SPA, the REST API, the
|
||
`/ws/session` séance and `/ws/device-feed` sockets, and `/audio/*` spirit
|
||
WAVs. Postgres holds accounts, sessions, transcripts, the Codex, traits,
|
||
essence/favor, inventory, sigils, devices, and the waitlist. Piper TTS is
|
||
degraded through a numpy chain (rate → pitch → bitcrush → echo → static);
|
||
eight **voice archetypes** (elder/young/child/drowned/burned/distant…)
|
||
covary pitch/rate/noise/echo so two spirits sound like two different dead
|
||
people, not two settings.
|
||
- **Ollama box** — two model tiers behind a bounded-concurrency queue; a
|
||
full queue rejects with a themed `veil_crowded` error rather than hanging.
|
||
- **Cloudflare Tunnel** — terminates HTTPS; its secure context is what
|
||
unlocks mic, WebUSB, and motion/magnetometer permissions.
|
||
- **systemd** — `Restart=always`, no start-limit surrender, wants Postgres:
|
||
the veil survives reboots and crash bursts without a human.
|
||
|
||
### Repo layout
|
||
|
||
```
|
||
backend/
|
||
app/
|
||
main.py routers, idempotent startup migrations, SPA fallback
|
||
ws.py the séance channel + rituals/judgment + manifest
|
||
entropy.py HMAC-conditioned physical randomness (the room decides)
|
||
celestial.py moon phase + true solar midnight (computed, offline)
|
||
geomagnetic.py NOAA SWPC Kp cache (never blocks a summon)
|
||
entities.py signatures, voice archetypes, moon-skewed rarity/traits
|
||
judgment.py verdict logic, tells, favor bias, ritual rolls
|
||
inventory.py essence economy, drop tables, unlocks, sigil validation
|
||
device_anomaly.py ESP32 telemetry -> anomaly/summon bridge
|
||
telemetry.py Wire Ghost sampler llm/ tts/ models/ routes/
|
||
tests/ pytest suite — 328 tests
|
||
firmware/
|
||
esp32p4-sensor-node/ ESP-IDF firmware: BMP280, MEMS mic, RD-03E radar,
|
||
WiFi via ESP32-C6 (UNVERIFIED on real hardware)
|
||
frontend/
|
||
src/pages/ Landing, Enter, Seance, Codex(+Entity), Shop,
|
||
Inventory, Devices, GhostLog
|
||
src/lib/ entropy, baseline (shared EMA core), coldSpot,
|
||
bluetooth, magnetometer, evp, sdr, emf, fft,
|
||
planchette, haunting, deviceFeed, spectrumSonify
|
||
src/components/ PlanchetteBoard, SpectrumScope, VeilConditions,
|
||
DeviceWhisper, ModeHint, Ritual/Judgment/Inventory
|
||
panels, SigilDesigner, ColdSpotPanel, GhostLog HUD…
|
||
docs/
|
||
CHANNELS.md what each channel really measures + verification status
|
||
superpowers/ design specs + implementation plans
|
||
```
|
||
|
||
## Quickstart
|
||
|
||
Prereqs on the App CT: Python 3.11+, Node 18+, Postgres (`apt`), and an
|
||
Ollama box on the LAN with the two model tiers pulled.
|
||
|
||
**1. Postgres** (one-time):
|
||
|
||
```bash
|
||
sudo apt-get update && sudo apt-get install -y postgresql
|
||
sudo -u postgres psql -c "CREATE ROLE quantumancy WITH LOGIN PASSWORD 'quantumancy';"
|
||
sudo -u postgres psql -c "CREATE DATABASE quantumancy OWNER quantumancy ENCODING 'UTF8' TEMPLATE template0;"
|
||
sudo -u postgres psql -c "CREATE DATABASE quantumancy_test OWNER quantumancy ENCODING 'UTF8' TEMPLATE template0;"
|
||
```
|
||
|
||
> `quantumancy_test` is dropped and recreated by every test run. Never point
|
||
> the app's own `DATABASE_URL` at it.
|
||
|
||
**2. Configuration:**
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
# edit .env: confirm DATABASE_URL and OLLAMA_BASE_URL
|
||
```
|
||
|
||
**3. Backend:**
|
||
|
||
```bash
|
||
cd backend
|
||
python3 -m venv venv
|
||
source venv/bin/activate
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
**4. Piper voices.** `backend/voices/` is gitignored; download each `.onnx`
|
||
plus its `.onnx.json` from
|
||
[rhasspy/piper-voices](https://huggingface.co/rhasspy/piper-voices):
|
||
|
||
| id | file | language | character |
|
||
|---|---|---|---|
|
||
| `lessac` | `en_US-lessac-low.onnx` | en | a measured American woman (default) |
|
||
| `amy` | `en_US-amy-low.onnx` | en | a soft American woman |
|
||
| `ryan` | `en_US-ryan-low.onnx` | en | a deep American man |
|
||
| `alan` | `en_GB-alan-low.onnx` | en | a low British man |
|
||
| `hfc_male` | `en_US-hfc_male-medium.onnx` | en | a worn male voice |
|
||
| `hfc_female` | `en_US-hfc_female-medium.onnx` | en | a worn female voice |
|
||
| `davefx` | `es_ES-davefx-medium.onnx` | es | una voz masculina grave (ES default) |
|
||
| `ald` | `es_MX-ald-medium.onnx` | es | una voz masculina seca |
|
||
|
||
**5. Frontend:**
|
||
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
npm run build # produces frontend/dist/, served by the backend
|
||
```
|
||
|
||
**6. Run.** By hand:
|
||
|
||
```bash
|
||
cd backend
|
||
set -a && source ../.env && set +a
|
||
venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 7777
|
||
```
|
||
|
||
or as a service (recommended). The shipped unit
|
||
(`deploy/quantumancy.service`) hardcodes `/root/quantumancy` — edit the two
|
||
paths first if your checkout lives elsewhere. A production install should
|
||
also add the persistence drop-in (`Restart=always`,
|
||
`StartLimitIntervalSec=0`, `Wants=postgresql.service`):
|
||
|
||
```bash
|
||
sudo cp deploy/quantumancy.service /etc/systemd/system/
|
||
sudo systemctl enable --now quantumancy
|
||
```
|
||
|
||
Then visit `http://<host>:7777` — or your Cloudflare Tunnel hostname for the
|
||
secure context that mic, WebUSB, and motion/magnetometer all require.
|
||
|
||
**7. Ollama models** (on the Ollama box, one-time):
|
||
|
||
```bash
|
||
ollama pull granite4.1:3b # fast tier: fragments, ambient whispers
|
||
ollama pull minicpm-v4.5:latest # chat tier: direct contact, minting, manifest
|
||
```
|
||
|
||
Any Ollama tag works — override with `OLLAMA_FAST_MODEL` / `OLLAMA_CHAT_MODEL`.
|
||
|
||
## Configuration
|
||
|
||
All settings live in `backend/app/config.py`, read from the environment or
|
||
`.env`. Required: `DATABASE_URL`, `OLLAMA_BASE_URL`.
|
||
|
||
| Env var | Default | Purpose |
|
||
|---|---|---|
|
||
| `DATABASE_URL` | — (required) | asyncpg connection string |
|
||
| `OLLAMA_BASE_URL` | — (required) | Ollama REST endpoint |
|
||
| `PORT` | `7777` | HTTP listen port |
|
||
| `OLLAMA_FAST_MODEL` | `granite4.1:3b` | fast tier |
|
||
| `OLLAMA_CHAT_MODEL` | `minicpm-v4.5:latest` | chat tier (replies, minting, manifest) |
|
||
| `LLM_MAX_CONCURRENCY` | `2` | simultaneous Ollama calls |
|
||
| `LLM_MAX_QUEUE_DEPTH` | `8` | queued calls before themed rejection |
|
||
| `LLM_COOLDOWN_SECONDS` | `8.0` | min gap between ambient whispers |
|
||
| `PIPER_VOICES_DIR` | `voices` | voice models (relative to `backend/`) |
|
||
| `DATA_DIR` | `data` | generated audio, served at `/audio/` |
|
||
|
||
## The Séance Protocol
|
||
|
||
One WebSocket per contact session: `/ws/session`, authenticated by the
|
||
`qm_session` cookie (close code `4401` otherwise). JSON frames both ways.
|
||
|
||
**Client → server:**
|
||
|
||
| Frame | Payload | Notes |
|
||
|---|---|---|
|
||
| `ping` | — | `pong` |
|
||
| `set_mode` | `mode: wire\|evp\|radio\|ouija\|emf` | persisted on the session |
|
||
| `language` | `en\|es` | switches LLM language + Piper voice |
|
||
| `summon` | optional `entropy` (conditioned room noise), optional `longitude` | rate-limited 4/min/user; the sky + entropy shape who answers |
|
||
| `anomaly` | `source`, `frequency`, `magnitude` | auto-summons at ≥3; then fragments (30/min/user) |
|
||
| `question` | `text` ≤500 | streamed reply (6/min/user) |
|
||
| `passive` | `enabled` | ambient Wire Ghost loop |
|
||
| `ritual_start` / `ritual_step` | `step` | 4 steps complete an attempt (6/min/user) |
|
||
| `judgment` | `verdict: trust\|banish\|test\|cross_over` | 10/min/user |
|
||
|
||
**Server → client** (single sender task; frames never interleave):
|
||
|
||
| Frame | Meaning |
|
||
|---|---|
|
||
| `session`, `pong`, `mode`, `passive` | plumbing |
|
||
| `status` | `attuning\|summoning\|gathering`; summoning carries `entropy_bits`, `sky` (moon/thinness), `geomagnetic` |
|
||
| `entity` | matched or minted (public fields only — hidden traits never leave the server unearned) |
|
||
| `anomaly_ack` / `anomaly` | counts; server-detected events |
|
||
| `utterance` | `kind: greeting\|fragment\|ambient\|reply\|manifest` — manifest is unprompted speech |
|
||
| `audio` | TTS + effects finished for an utterance |
|
||
| `reply_start/token/end` | streamed Direct Contact |
|
||
| `telemetry` | live Wire Ghost vitals |
|
||
| `tell` | a trait-shaped whisper for the Ghost Log |
|
||
| `ritual_complete` | `success`, `revealed` traits on success |
|
||
| `judgment_result` | `correct`, deltas, `at_peace`, `consequence` |
|
||
| `item_drop` | a relic falls |
|
||
| `error` | `rate_limited` / `veil_crowded`, themed |
|
||
|
||
**REST** (session-cookie auth where noted):
|
||
|
||
| Endpoint | Auth | Purpose |
|
||
|---|---|---|
|
||
| `POST /auth/register` · `login` · `logout` · `GET /auth/me` | — | argon2 + `qm_session` cookie |
|
||
| `POST /auth/guest` | public, 5/hr/IP | mint a `wanderer-` account |
|
||
| `GET /api/codex[?rarity,sort,limit]` · `GET /api/codex/{id}` | public | the shared registry / full dossier |
|
||
| `GET /api/stats` | public | live veil counters |
|
||
| `GET /api/conditions[?lon]` | public | moon, veil thinness, geomagnetic |
|
||
| `GET /api/seances/recent` | auth | the Ghost Log: last 12 sessions + echoes |
|
||
| `GET/POST /api/device` · `POST /api/device/telemetry` · WS `/ws/device-feed` | auth / device token | ESP32 pairing + live readings |
|
||
| `GET /api/inventory/{unlocks,items,sigils}` · `POST /api/inventory/unlocks/{key}` · `POST /api/inventory/sigils` | auth | the Reliquary |
|
||
| `POST /api/shop/waitlist` | public, 5/hr/IP | hardware waitlist |
|
||
| `GET /healthz` | — | `{"status": "ok"}` |
|
||
|
||
## Testing
|
||
|
||
**Backend** — pytest, **400 tests**. Pass your NORMAL `DATABASE_URL`;
|
||
`tests/conftest.py` derives the test database itself by appending `_test` to
|
||
the name, so the command below creates and uses `quantumancy_test`. That
|
||
database is dropped and recreated every run — never run two suites
|
||
concurrently against it; the autouse drop/create fixture will fight itself.
|
||
|
||
```bash
|
||
cd backend && source venv/bin/activate
|
||
DATABASE_URL=postgresql+asyncpg://quantumancy:quantumancy@localhost:5432/quantumancy \
|
||
OLLAMA_BASE_URL=http://10.30.20.107:11434 \
|
||
python -m pytest -q
|
||
```
|
||
|
||
Do **not** point `DATABASE_URL` at `quantumancy_test` here — the suffix is
|
||
appended, not checked for, so you would get `quantumancy_test_test`, which
|
||
does not exist, and all 400 tests error at setup. To use a database that
|
||
isn't `<your db>_test`, set `TEST_DATABASE_URL` explicitly instead; its name
|
||
must still end in `_test`.
|
||
|
||
Two guards in `conftest.py` make it safe to name the live database here: the
|
||
derived URL must differ from `DATABASE_URL`, **and** the derived database
|
||
name must end in `_test`. The second exists because the first alone is
|
||
defeated by `localhost` vs `127.0.0.1` addressing the same database. This
|
||
matters — an early version of the suite ran `drop_all` against production
|
||
and destroyed live séance data.
|
||
|
||
**Frontend** — Vitest + Testing Library, **385 tests**, plus a hard i18n
|
||
en/es parity gate (`npm run pretest`):
|
||
|
||
```bash
|
||
cd frontend && npm test
|
||
```
|
||
|
||
**Hardware-in-the-loop** (cannot be unit tested): the RTL-SDR sweep, mic
|
||
permissions, phone sensors, and everything in `firmware/` need real
|
||
hardware passes. `docs/CHANNELS.md` tracks exactly what has and hasn't had
|
||
one.
|
||
|
||
## Current State & Known Gaps
|
||
|
||
Honestly, in order of confidence:
|
||
|
||
- **Solid, verified live.** Auth (incl. guests), the Codex, Wire Ghost,
|
||
EVP + spectrum scope + sonification, TTS + archetype voices, entropy,
|
||
astronomy (validated against published ephemerides), the NOAA feed
|
||
(verified against the live endpoint), rituals/judgment/essence, the
|
||
Ghost Log, conditions, PWA manifest, phone layouts, the haunting layer.
|
||
328 + 366 tests pass.
|
||
- **Should work, unverified on hardware.** `lib/sdr.ts` (WebUSB RTL-SDR) is
|
||
written from librtlsdr register docs and has never met a real dongle;
|
||
every entry point is time-boxed and fails soft. The magnetometer EMF
|
||
path needs an Android device pass. All of `firmware/` compiles-by-eye:
|
||
reviewed twice, never flashed.
|
||
- **Built but not yet reachable.** `lib/bluetooth.ts` (BLE presence field)
|
||
has no séance panel yet.
|
||
- **Known drift.** The landing page's marketing grid still shows four modes
|
||
(`MODE_ORDER` in `LandingPage.tsx`) — EMF and everything since aren't in
|
||
the showcase.
|
||
- **Cosmetic.** One ~930 KB JS chunk; code-splitting would help first paint.
|
||
- **Intentionally unfinished.** The Ultimate Quantum Box is a waitlist —
|
||
no hardware ships, no payment is taken, and the page says so.
|
||
|
||
## The Veil's Honesty Policy
|
||
|
||
- All LLM system prompts frame the entity as **a horror-fiction persona in
|
||
an interactive art installation** — never a genuine paranormal claim.
|
||
- Every number shown to a seeker is a real measurement. The fiction
|
||
interprets; it never fabricates. (`docs/CHANNELS.md` is the ledger.)
|
||
- Wire Ghost telemetry reads counters and timings only; packet payloads are
|
||
never inspected. Only a longitude is ever kept from a location grant.
|
||
- Client entropy is untrusted by construction — it can add unpredictability
|
||
to a draw, never steer one.
|
||
- If the Ollama box is dark, every channel degrades to procedural fallbacks
|
||
— a summoning never visibly fails.
|
||
|
||
## Further Reading
|
||
|
||
- `docs/CHANNELS.md` — every channel, what it really measures, and its
|
||
honest verification status
|
||
- Design specs: `docs/superpowers/specs/` (website, character depth, ESP32
|
||
node, possession, usability wave)
|
||
- Implementation plans: `docs/superpowers/plans/`
|