Indiana c372427ced feat: matrix/graph data views, wire-generated anomalies, http cookie fix
- auth: session cookie Secure only over https — plain-http LAN access was
  silently dropping the cookie, killing WS auth ('connection unstable')
- wire ghost: server-side spike detection on jitter baseline (3σ + 2.5×mean,
  20KB/s floor, 20s throttle) — wire anomalies now flood every session with
  zero hardware, pushed to clients as {type:'anomaly'} frames
- telemetry cadence 3-5s for live graphs; ambient whispers unchanged
- frontend: MATRIX view (data-rain interleaved with live utterances/anomaly/
  telemetry strings), GRAPHS view (scrolling jitter/variance/dns lines +
  anomaly markers + counters), BOARD/MATRIX/GRAPHS switcher
- connection banner: 'connecting' is now neutral 'tuning the veil…', only
  unstable/closed warns
- db: recreated quantumancy(+_test) as UTF8 (was SQL_ASCII — crashed on
  non-ASCII spirit text); README quickstart updated
- i18n: seance.views.* EN/ES
2026-07-21 00:02:41 +00:00

QUANTUMANCY

an instrument for speaking with the dead bandwidth

Quantumancy is a self-hosted, gothic-hacker web experience that lets visitors talk to spirits through four channels, each grounded in a real paranormal- investigation technique or a real data source. Real anomalies — RF power spikes, voice-band blips, network jitter — are detected in genuine sensor and telemetry streams, and a locally-run LLM gives the presence behind them a voice. A local Piper TTS then renders that voice through a static-choked spirit-box effects chain.

This is an interactive horror art installation. The spirits are fiction; the static is real. Every "entity" is a persona generated by a language model on your own hardware. Nothing leaves your network: no cloud APIs, no third-party inference, no telemetry of ours — only yours.

The Four Modes

Mode Vessel What actually happens
Spirit Radio RTL-SDR dongle (WebUSB, Chromium only) The browser sweeps the FM band (88–108 MHz) computing FFT power per bin; spikes above the rolling noise floor become anomaly events that summon one-word fragments, Ovilus-style.
EVP Listening Microphone (getUserMedia) A Web Audio AnalyserNode watches the voice band (~300 Hz–3.4 kHz) for brief deviations ≥ 8 dB above the room's rolling silence — the classic "record quiet, review for voices" technique.
The Wire Ghost Nothing — works for everyone The backend samples real, non-content network telemetry from the host (interface throughput jitter, TCP connect latency variance, DNS hesitation) and a fragmented consciousness whispers about it every few seconds. Packet payloads are never inspected — a hard privacy boundary.
Ouija / Direct Contact The shared front door A canvas planchette drifts, then spells the spirit's words letter by letter. Ask a free-text question and the conversational model streams a full reply token by token while the planchette works.

Every session begins unidentified. The backend fingerprints the session's anomaly pattern into a signature; a matching signature re-contacts an existing spirit, a new one gets minted into the Codex — a publicly browsable registry of every spirit ever contacted, shared across all users, with name, epithet, rarity tier, persona lore, sample quotes, voice/visual profiles, and contact counts.

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
                                              ┌───────────▼─────────┐
                                              │ Ollama box          │
                                              │ 10.30.20.107:11434  │
                                              │ CPU-only, 64 GB     │
                                              │ fast + chat models  │
                                              └─────────────────────┘
  • App CT — plain HTTP on port 7777. FastAPI (async, WebSocket-native) serves the built Vite/React SPA as static assets, the auth + Codex REST API, the /ws/session séance WebSocket, and /audio/* spirit-voice WAVs. Postgres holds accounts, sessions, transcripts (events), and the Codex. Piper TTS is invoked locally per utterance, then degraded through a numpy effects chain (rate → pitch → bitcrush → echo → static).
  • Ollama box — reachable over LAN via Ollama's REST API. Two model tiers: a fast model for fragments/ambient whispers/… and a heavier chat model for Direct Contact and entity minting. CPU-only and shared, so all LLM calls flow through a bounded-concurrency queue (LLMQueue); queued requests render in the UI as "the spirits are gathering energy…".
  • Cloudflare Tunnel — managed outside this repo; terminates HTTPS and points at http://<app-ct-ip>:7777. The app never handles TLS. The tunnel's HTTPS origin satisfies browser secure-context requirements for mic and WebUSB.

Repo layout

backend/
  app/
    main.py          FastAPI app: routers, /healthz, /assets + /audio mounts, SPA fallback
    config.py        pydantic-settings (env vars below)
    db.py            async SQLAlchemy 2.0 engine/session (asyncpg)
    deps.py          get_current_user, qm_session cookie
    security.py      argon2 password hashing
    rate_limit.py    fixed-window RateLimiter (per-user LLM limits)
    telemetry.py     Wire Ghost sampler (/proc/net/dev, TCP RTT, DNS timing)
    entities.py      anomaly signatures, profile normalization, procedural fallback
    ws.py            the séance channel: /ws/session protocol + ambient loop
    llm/             OllamaClient, bounded LLMQueue, SpiritService, prompt builders
    tts/             Piper CLI wrapper, voice catalog, numpy effects chain
    models/          users, auth_sessions, contact_sessions, entities,
                     entity_sightings, events
    routes/          /auth/* and /api/codex, /api/stats
  tests/             pytest suite (45 tests)
  voices/            Piper .onnx voice models (gitignored — see setup)
  data/              generated utterance audio, served at /audio/ (gitignored)
deploy/
  quantumancy.service  systemd unit template
frontend/
  src/lib/           types (WS protocol), VeilSocket, audio player, evp, sdr,
                     fft, planchette machine
  src/state/         AuthProvider, SeanceProvider (reducer + socket wiring)
  src/components/    PlanchetteBoard, Transcript, EntityCard, GhostGlyph, …
  src/three/         GhostCanvas + GhostScene (custom-shader 3D spirit)
  src/i18n/          react-i18next init + en.json / es.json
docs/superpowers/
  specs/             the design spec
  plans/             the 7 implementation plans this repo was built from

Quickstart

Prereqs on the App CT: Python 3.11+, Node 18+, Postgres (via 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: set a real SESSION_SECRET, 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; the app expects these voice models there (id → file), downloadable from the rhasspy/piper-voices HuggingFace repo (each .onnx plus its .onnx.json):

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. Either by hand (from backend/, with the .env values in the environment — the app reads .env relative to its working directory, and piper_voices_dir/data_dir are relative to backend/):

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 — auto-restart, boot-start, journalctl -u quantumancy):

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 Spirit Radio and EVP 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, entity minting

Any Ollama tag works — override with OLLAMA_FAST_MODEL / OLLAMA_CHAT_MODEL.

Configuration

All settings live in backend/app/config.py and are read from the environment or .env (repo root when run via systemd; CWD otherwise). Required: DATABASE_URL, OLLAMA_BASE_URL, SESSION_SECRET.

Env var Default Purpose
DATABASE_URL — asyncpg connection string, e.g. postgresql+asyncpg://quantumancy:quantumancy@localhost:5432/quantumancy
OLLAMA_BASE_URL — Ollama REST endpoint, e.g. http://10.30.20.107:11434
SESSION_SECRET — random 64-char string (session cookie signing)
PORT 7777 HTTP listen port
OLLAMA_FAST_MODEL granite4.1:3b fast tier: fragments, wire whispers
OLLAMA_CHAT_MODEL minicpm-v4.5:latest chat tier: direct contact, entity minting
LLM_MAX_CONCURRENCY 2 simultaneous Ollama calls (CPU-only box — keep small)
LLM_MAX_QUEUE_DEPTH 8 queued calls before "too many seekers" rejection
LLM_COOLDOWN_SECONDS 8.0 min gap between ambient whispers; user requests preempt
PIPER_VOICES_DIR voices voice model directory (relative to backend/)
DATA_DIR data generated audio, served at /audio/ (relative to backend/)

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 Server response
ping — pong
set_mode mode: wire|evp|radio|ouija mode echo; persisted on the session row
language language: en|es switches LLM reply language + Piper voice
summon — status: summoning → entity → greeting utterance
anomaly source, frequency, magnitude anomaly_ack; auto-summons once the stream can fingerprint (≥3 anomalies), then fragment utterances
question text (≤500 chars) status: gathering → reply_start → reply_token×N → reply_end → spoken utterance
passive enabled: bool passive ack; starts/stops the ambient Wire Ghost loop

Server → client (all frames flow through a single sender task so concurrent producers never interleave):

Frame Payload Meaning
session id contact session created (sent on connect)
pong — keepalive reply
mode mode mode accepted
status state: attuning|summoning|gathering themed loading states
entity is_new, entity (name, epithet, persona, rarity, voice, visual, quotes, contact_count) a presence has been matched or minted
anomaly_ack count anomalies recorded this session
utterance id, kind: greeting|fragment|ambient|reply, text, entity words from beyond; audio follows
audio id, url (/audio/<event>.wav) TTS + effects chain finished for that utterance
reply_start / reply_token / reply_end token, final id + text token-streamed Direct Contact reply
telemetry jitter_bytes_per_s, latency_variance_ms, latency_mean_ms, dns_ms live Wire Ghost vitals
passive enabled ambient loop state
error code: rate_limited|veil_crowded, message themed rate-limit / queue-full notices

REST (JSON, session-cookie auth where noted; credentials: 'include'):

Endpoint Auth Purpose
POST /auth/register · POST /auth/login · POST /auth/logout · GET /auth/me — argon2 username/password, qm_session cookie (14-day server-side sessions)
GET /api/codex?rarity=&sort=recent|contacted&limit= public the shared spirit registry
GET /api/codex/{entity_id} public full dossier: persona, voice profile, sighting count
GET /api/stats public live veil counters (entities, sessions, utterances, anomalies)
GET /healthz — {"status": "ok"}

Testing

Backend (pytest, 45 tests; requires the quantumancy_test database — dropped and recreated on every run):

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 \
SESSION_SECRET=test-secret \
python -m pytest -v

Covers: auth + session cookies, rate limiter, config, SPA serving, LLM queue, telemetry parsing, entity signatures/profiles, prompt construction (incl. Spanish language clauses), TTS effects chain, Codex REST, and the full WS séance flow (auth, ping/pong, summon/mint/greet, streamed replies, anomaly attunement, re-contact by signature).

Frontend (Vitest + Testing Library — 71 tests across 6 files: the planchette state machine, the EVP detector core, the FFT, the reconnecting VeilSocket, the séance reducer, and the App shell):

cd frontend && npm test

Hardware-in-the-loop (cannot be unit tested, per the spec): the WebUSB RTL-SDR sweep and the microphone EVP flow each need a manual pass with real hardware/permissions before being called done. Spirit Radio is Chromium-only and its driver is marked HARDWARE PASS REQUIRED in frontend/src/lib/sdr.ts. RTL-SDR dongles are often claimed by the OS kernel driver (dvb_usb_rtl28xxu on Linux) before WebUSB can reach them — Windows users with Zadig/WinUSB usually work out of the box; Linux/Mac may need a driver unbind.

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. The UI carries the "this is real" atmosphere; the model instructions carry the fiction.
  • Wire Ghost telemetry reads counters and timings only. Packet payloads are never inspected or logged.
  • If the Ollama box is dark, every spirit channel degrades to curated offline fallbacks — a summoning never visibly fails.

Further Reading

  • Design spec: docs/superpowers/specs/2026-07-20-quantumancy-website-design.md
  • Implementation plans 1–7: 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%