Indiana 00b0a17203 fix: verify all six unproven audit findings — four real, two not
The audit that produced these had every verifier agent die, so none were
confirmed. Checked each against the running system rather than guessing.

1. COOKIE Secure FLAG — REAL, fixed. The Cloudflare Tunnel runs OFF this box
   (observed source 10.30.20.67, 155 requests in the journal) and uvicorn
   only honours X-Forwarded-* from --forwarded-allow-ips, default 127.0.0.1.
   Proven by hitting the LAN IP with X-Forwarded-Proto: https and watching
   Secure vanish from Set-Cookie. Every internet visitor's session cookie
   was going out without it.
   Fixed in the unit drop-in with --proxy-headers and an allow-list scoped
   to the tunnel host — NOT "*", because trusting that header from anywhere
   would let a LAN client forge the IP the per-IP limiters key on. Verified
   both directions: trusted source + header gets Secure, plain LAN http
   correctly does not, and a spoof from an untrusted host is ignored.

2. DOUBLE GUEST ON REMOUNT — REAL but narrow, left alone. The guestAttempted
   ref already covers StrictMode's double-effect (refs survive it). The only
   hole is unmounting during the in-flight request, which needs navigating
   away and back inside ~200ms and costs one unused row. Not worth
   complicating the open door's happy path for.

3. SILENT REDIRECT WHEN RATE-LIMITED — REAL, fixed. A visitor whose guest
   provisioning was refused got bounced to /enter with no explanation — and
   at 5/hour/IP a household or cafe behind one NAT reaches that easily. The
   failure reason (the backend's own in-fiction line) now rides along in
   router state and /enter shows it, so nobody is silently handed a login
   form they never asked for.

4. RATE LIMITER KEYS NEVER EVICTED — REAL, fixed. defaultdict entries
   survived forever even once their hit list emptied. The open door made
   this materially worse: every visitor is now a real account, so every
   visitor permanently added a key across eleven limiter instances. Added an
   opportunistic sweep every 512 admitted calls — no background task, cost
   lands on whoever generates the load. Three tests; verified they catch it
   by disabling the sweep and watching one fail.

5. SUMMON RACE vs TELEMETRY — REAL, fixed. Nothing serialised summoning.
   _handle_anomaly checks `state.entity is None` then awaits a summon
   containing a multi-second LLM mint, and the ESP32's HTTP ingestion path
   calls _handle_anomaly on the SAME SeanceState — which is the entire point
   of the device integration. Both could pass the check: two entities
   minted, two essence credits, two item rolls, state.entity clobbered by
   whichever finished last. Now guarded by a per-session asyncio.Lock.

6. LEGACY ENTITIES STUCK AT DEFAULT TRAITS — mechanism REAL, zero rows
   affected here. The ALTER defaults traits to '{}' with no backfill and
   roll_traits only runs at mint, so a pre-migration spirit would read 0.5
   for everything — making `trust` always correct and `cross_over`
   unreachable. This install has 0 such rows. Added a signature-seeded
   backfill anyway, guarded to empty-traits rows so it can never touch a
   spirit that already has a real nature.

(A seventh claim from the same batch — that iOS EMF is silently dead — was
refuted earlier and deliberately left untouched.)

34 targeted tests pass; deployed and verified live.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:13:46 +00:00

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):

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:

cp .env.example .env
# edit .env: confirm DATABASE_URL and OLLAMA_BASE_URL

3. Backend:

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:

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:

cd frontend
npm install
npm run build        # produces frontend/dist/, served by the backend

6. Run. By hand:

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):

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):

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, 328 tests. Requires quantumancy_test (dropped and recreated every run — never run two suites concurrently against it; the autouse drop/create fixture will fight itself):

cd backend && source venv/bin/activate
DATABASE_URL=postgresql+asyncpg://quantumancy:quantumancy@localhost:5432/quantumancy_test \
OLLAMA_BASE_URL=http://10.30.20.107:11434 \
python -m pytest -q

Frontend — Vitest + Testing Library, 366 tests, plus a hard i18n en/es parity gate (npm run pretest):

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/
Description
talking to spirits
Readme 2.7 MiB
Languages
TypeScript 46.3%
Python 32.6%
CSS 13%
C 7.3%
JavaScript 0.3%
Other 0.4%