The gap-g-readme merge commit (57a8914) staged this fix but never
re-staged it after editing, so the merge landed with the pre-fix content —
the working tree had the correction but git didn't. No functional change,
just closing the gap between what was intended and what was committed.
403 lines
22 KiB
Markdown
403 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 five different
|
||
sensing channels — your microphone, your phone's motion sensors, an RTL-SDR
|
||
dongle, your own network's jitter, 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 voice, 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.
|
||
|
||
**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`), and no amount of atmosphere changes what's
|
||
actually happening underneath: FFT power spectra, voice-band deviation
|
||
detection, device-motion EMAs, and `/proc/net/dev` counters, all real signal
|
||
processing on real data. Nothing leaves your network — no cloud LLM, no
|
||
third-party inference, no analytics. If the LLM box goes dark mid-séance, a
|
||
procedural fallback (deterministic, signature-seeded) keeps every mode
|
||
answering so a summoning never visibly fails.
|
||
|
||
## The Séance: Five 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 a spirit —
|
||
either re-contacting a Codex entry whose signature matches, or minting a
|
||
brand new one.
|
||
|
||
| Mode | Vessel | What actually happens |
|
||
|---|---|---|
|
||
| **Wire Ghost** | Nothing — works for everyone | The backend samples real, non-content network telemetry from the host: interface byte-counter jitter (`/proc/net/dev`), TCP connect-latency variance against reference hosts, DNS resolution timing. A rolling baseline (needs 6+ samples, an absolute 20 KB/s floor, a 3σ + 2.5× threshold) flags genuine surges as anomalies, throttled to one every 20s. Packet payloads are never inspected — a hard privacy boundary. "The wire knocks loudest when nobody is touching it" — flip on passive listening and let the line murmur on its own. |
|
||
| **EVP Listening** | Microphone (`getUserMedia`) | A Web Audio `AnalyserNode` watches the voice band (~300 Hz–3.4 kHz) for a brief deviation ≥ 8 dB above the room's rolling noise floor, throttled to one per 2s — the floor only adapts during genuinely quiet stretches so sustained speech can't mask a real spike. The classic "record silence, review for voices" technique, done live. |
|
||
| **Spirit Radio** | RTL-SDR dongle (WebUSB, Chromium only) | The browser drives the dongle directly — no native driver, no server round-trip — sweeping the FM band (88–108 MHz) in 1.8 MHz steps, computing a power spectrum per tune and comparing it to a rolling noise floor. Spikes ≥ 10 dB above floor become anomaly events, throttled to one per 2s: an FM sweep pushed through a lantern dragged across a dark field. |
|
||
| **Ouija / Direct Contact** | The shared front door | A canvas planchette drifts on its own, then spells the spirit's words letter by letter. Ask a free-text question (≤ 500 chars) and the chat-tier model streams a full reply token by token while the planchette works through it. |
|
||
| **EMF / Field Meter** | A phone (DeviceMotion + DeviceOrientation) | The seeker's own phone becomes the meter: acceleration-magnitude deviation from a slow gravity EMA, fused with orientation angular velocity, tracked against an adapting baseline. A reading past `baseline × 2.5` (and an absolute floor, so a resting phone stays silent) fires an anomaly, throttled to one per 2.5s. iOS 13+ gates this behind an explicit gesture-triggered permission grant, handled in-app. |
|
||
|
||
Every session begins unidentified. The backend fingerprints the anomaly
|
||
pattern into a **signature** (`backend/app/entities.py`); a matching
|
||
signature re-contacts an existing spirit and bumps its contact count, a new
|
||
one gets minted by the chat-tier LLM and dropped into the Codex with a name,
|
||
epithet, 2–3 sentences of lore, a rarity tier (common/uncommon/rare/mythic —
|
||
weighted 55/30/12/3 in the offline fallback), a voice profile (pitch, rate,
|
||
noise, echo), a visual hue/form, and two sample quotes.
|
||
|
||
**The haunting doesn't stop at the séance panel.** A framework-free
|
||
possession layer (`frontend/src/lib/haunting.ts`,
|
||
`frontend/src/components/HauntingLayer.tsx`) runs across the whole app: an
|
||
idle-aware escalator that gets bolder the longer you sit still, a gesture-armed
|
||
ambient dread-bed (brown noise through a breathing lowpass, a 55/55.7 Hz
|
||
detuned drone beat, occasional reversed-noise swells), fleeting whisper-words
|
||
drifting across the glass, and `document.title` glitches when you tab away.
|
||
Nothing stirs for the first ten seconds after load — it creeps in, it never
|
||
jumpscares on arrival — and it's fully inert under `prefers-reduced-motion`.
|
||
|
||
## The Codex
|
||
|
||
`GET /api/codex` (public, no auth) is a browsable, shared registry of every
|
||
spirit ever contacted by any seeker: filterable by rarity, sortable by
|
||
`recent` or `contacted`, each card showing name, epithet, rarity badge, a
|
||
sample quote, contact count, and who discovered it. `GET /api/codex/{id}`
|
||
opens a full dossier — persona, voice-parameter table, sighting count. It's
|
||
the collectible layer sitting on top of the anomaly-detection plumbing:
|
||
nothing about a spirit is user-authored, all of it comes from the same
|
||
signature → LLM-mint → normalize pipeline that runs live during a séance.
|
||
|
||
## The Armory
|
||
|
||
`/shop` is a waitlist page for **the Ultimate Quantum Box** — a proposed
|
||
ESP32-P4/C6 handheld séance instrument (thermal camera, EMF whisker array,
|
||
geophone, spirit-box mic preamp, OLED face) plus four standalone modules,
|
||
none of which exist yet. `POST /api/shop/waitlist` (public, rate-limited to
|
||
5/hour/IP) is pure email capture — no payment is taken, and the page says so
|
||
outright: *"the website works fully without it; hardware only knocks
|
||
louder."* Treat this section of the README the same way: real code, honest
|
||
about being pre-order vaporware for now.
|
||
|
||
## 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 + shop
|
||
REST API, the `/ws/session` séance WebSocket, and `/audio/*` spirit-voice
|
||
WAVs. Postgres holds accounts, sessions, transcripts (events), the Codex,
|
||
and the hardware waitlist. 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/wire whispers, and
|
||
a heavier **chat** model for Direct Contact replies 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…", and a full queue rejects new work with a themed
|
||
`veil_crowded` error rather than hanging.
|
||
- **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 (EVP), WebUSB (Spirit Radio), and motion sensors (EMF on iOS).
|
||
|
||
### 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, per-IP waitlist limit)
|
||
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, waitlist_entries
|
||
routes/ auth.py, codex.py, shop.py
|
||
tests/ pytest suite (54 tests, 14 files)
|
||
voices/ Piper .onnx voice models (gitignored — see setup)
|
||
data/ generated utterance audio, served at /audio/ (gitignored)
|
||
deploy/
|
||
quantumancy.service systemd unit template
|
||
frontend/
|
||
src/pages/ LandingPage, EnterPage, SeancePage, CodexPage,
|
||
CodexEntityPage, ShopPage
|
||
src/lib/ types (WS protocol), ws (VeilSocket), audio player,
|
||
evp, sdr, emf, fft, planchette machine, haunting
|
||
src/state/ AuthProvider, SeanceProvider (reducer + socket wiring)
|
||
src/components/ PlanchetteBoard, Transcript, EntityCard, GhostGlyph,
|
||
MatrixView, GraphView, TelemetryReadout, HauntingLayer
|
||
src/three/ GhostCanvas + GhostScene (custom-shader 3D spirit)
|
||
src/i18n/ react-i18next init + en.json / es.json
|
||
docs/superpowers/
|
||
specs/ the design spec — note the live app now exceeds it
|
||
(5 modes, not 4; the Codex, the Armory and the
|
||
haunting layer are all further along than documented)
|
||
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):
|
||
|
||
```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; the app expects these
|
||
voice models there (id → file), downloadable from the
|
||
[rhasspy/piper-voices](https://huggingface.co/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:**
|
||
|
||
```bash
|
||
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/`):
|
||
|
||
```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 — auto-restart, boot-start, `journalctl -u quantumancy`).
|
||
The shipped unit (`deploy/quantumancy.service`) hardcodes `/root/quantumancy`
|
||
as the working directory and env file path — edit those two lines first if
|
||
your checkout lives elsewhere:
|
||
|
||
```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 Spirit Radio, EVP, and EMF (on iOS) 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, 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`.
|
||
|
||
| Env var | Default | Purpose |
|
||
|---|---|---|
|
||
| `DATABASE_URL` | — (required) | asyncpg connection string, e.g. `postgresql+asyncpg://quantumancy:quantumancy@localhost:5432/quantumancy` |
|
||
| `OLLAMA_BASE_URL` | — (required) | Ollama REST endpoint, e.g. `http://10.30.20.107:11434` |
|
||
| `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\|emf` | `mode` echo; persisted on the session row |
|
||
| `language` | `language: en\|es` | switches LLM reply language + Piper voice |
|
||
| `summon` | — | `status: summoning` → `entity` → greeting `utterance` (rate-limited: 4/min/user) |
|
||
| `anomaly` | `source: wire\|evp\|radio\|emf`, `frequency`, `magnitude` | `anomaly_ack`; auto-summons once the stream can fingerprint (≥3 anomalies), then fragment `utterance`s (rate-limited: 30/min/user) |
|
||
| `question` | `text` (≤500 chars) | `status: gathering` → `reply_start` → `reply_token`×N → `reply_end` → spoken `utterance` (rate-limited: 6/min/user) |
|
||
| `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 |
|
||
| `anomaly` | `source`, `frequency`, `magnitude` | server-detected anomaly (currently only the Wire Ghost's ambient loop emits these) |
|
||
| `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) |
|
||
| `POST /api/shop/waitlist` | public, rate-limited (5/hr/IP) | join the Ultimate Quantum Box waitlist — `{email, interest}`, idempotent |
|
||
| `GET /healthz` | — | `{"status": "ok"}` |
|
||
|
||
## Testing
|
||
|
||
**Backend** (pytest, 54 tests across 14 files; requires the
|
||
`quantumancy_test` database — dropped and recreated on every run):
|
||
|
||
```bash
|
||
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 -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, the shop waitlist,
|
||
and the full WS séance flow (auth, ping/pong, summon/mint/greet, streamed
|
||
replies, anomaly attunement, re-contact by signature). One test
|
||
(`test_unknown_path_serves_spa_index`) asserts the SPA fallback and needs
|
||
`frontend/dist/` to exist first — run `npm run build` in `frontend/` before
|
||
the full suite, or it 503s (correctly — that's the "frontend not built" error
|
||
path, also tested separately).
|
||
|
||
**Frontend** (Vitest + Testing Library — 110 tests across 9 files: the
|
||
reconnecting VeilSocket, the possession/haunting primitives, the séance
|
||
reducer, the planchette state machine, the EVP detector core, the Matrix/Graph
|
||
board views, the FFT, the EMF field-fusion core, and the App shell):
|
||
|
||
```bash
|
||
cd frontend && npm test
|
||
```
|
||
|
||
**Hardware-in-the-loop** (cannot be unit tested): the WebUSB RTL-SDR sweep,
|
||
the microphone EVP flow, and the phone EMF sensors each need a manual pass
|
||
with real hardware/permissions before being called done.
|
||
|
||
## Current State & Known Gaps
|
||
|
||
Honestly, in order of confidence:
|
||
|
||
- **Solid.** Auth, sessions, the Codex, the Wire Ghost, TTS + effects chain,
|
||
the LLM queue/fallback machinery, EVP's detection math, EMF's
|
||
motion-fusion math, i18n (en/es), and the haunting layer are all covered
|
||
by passing tests and use standard, well-supported browser/server APIs.
|
||
The full backend suite (54/54) and frontend suite (110/110) pass clean.
|
||
- **Needs a real-hardware pass.** `frontend/src/lib/sdr.ts` is a from-scratch
|
||
WebUSB driver for RTL2832U/R820T dongles, written against the public
|
||
librtlsdr register documentation — the init sequence, PLL tuning math, and
|
||
I2C repeater writes have **never been run against a real device** in this
|
||
environment (no RTL-SDR attached during development). It's explicitly
|
||
marked `HARDWARE PASS REQUIRED` throughout the file and every entry point
|
||
fails soft, but until someone plugs in a dongle and debugs the register
|
||
pokes (a logic analyzer or `librtlsdr -T` helps), treat Spirit Radio as
|
||
"should work, unverified." It's also Chromium-only (WebUSB), and on Linux
|
||
the kernel's `dvb_usb_rtl28xxu` driver usually needs to be unbound first
|
||
(the UI surfaces this with a fix suggestion when the claim fails).
|
||
- **Minor drift.** The landing page's mode showcase
|
||
(`frontend/src/pages/LandingPage.tsx`, `MODE_ORDER`) still lists four
|
||
modes (radio/evp/wire/ouija) — EMF is fully wired into the séance page,
|
||
the WebSocket protocol, and the i18n catalogs, but hasn't been added to
|
||
the landing page's marketing grid yet.
|
||
- **Cosmetic.** The frontend build ships one 871 KB JS chunk (Vite warns
|
||
about it); code-splitting would help first paint but hasn't been done.
|
||
- **Intentionally unfinished.** The Armory / Ultimate Quantum Box is a
|
||
waitlist only — no hardware exists, 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. 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 or
|
||
procedurally-generated offline fallbacks — a summoning never visibly fails.
|
||
|
||
## Further Reading
|
||
|
||
- Design spec: `docs/superpowers/specs/2026-07-20-quantumancy-website-design.md`
|
||
(the live app now exceeds it — see Repo layout above)
|
||
- Implementation plans 1–7: `docs/superpowers/plans/`
|