← back to Constellations

Stack

What's actually running.

The technical substrate behind Constellations — the libraries, the storage, the guardrails, and the deliberate choices about where determinism ends and the model begins.

154
Cinematic universes
1,157
Films & series
25,534
Actors & characters
0
Models to open a card

The stack

Python 3.12
GraphRAG · graph-grounded retrieval-augmented generation
Kuzu graph database · read-only at serve · Cypher
LLM guardrails · pure-Python read-only query validator
Claude Opus · prompt caching · offline generation
FastAPI backend · vanilla-JS canvas cosmos
Live TMDB streaming providers
pytest · 161 tests · CI-blocking safety battery
GitHub Actions CI · uvicorn behind a PHP reverse proxy

The graph engine

The data lives in Kuzu, an embedded graph database opened read-only at serve time and rebuilt from human-editable seed YAML at boot. Universes, films, series, actors, characters, archetypes, and the interpretation layer are all first-class nodes and edges, so a question like "who connects these two worlds" is a graph traversal, not a guess. Editing the cosmos means editing YAML and rebuilding — diffable, reviewable, version-controlled.

The myth underneath

Beneath the franchises runs a tapestry of archetypes — The Chosen One, The Mentor, The Fall — and the arc echoes that rhyme across unrelated universes. Twenty archetypes, the embodiment edges that bind characters to them, and a handful of arc echoes turn "these are different movies" into "these are the same story told again." Every one of those bindings is an interpretation edge, so it's sourced and worn as a reading — the mythology is offered, never asserted.

The cosmos is the front door

The interface isn't a page that describes the app — it is the app. A vanilla-JS canvas renders the orbiting universes with no game engine and no three.js: flick to spin, grab to park, pick a world and a black-hole collapse blooms it open; film-to-film jumps fly a ship along the thread so you travel rather than cut. One law governs everything — click anything and everything else gracefully vanishes, so attention always has a single focus. Persona lenses (Wander · Catch me up · Did you know · Everything) re-weight every card to how you like to read.

Sourced edges — no citation, no thread

Two kinds of connection live in the graph. Fact edges — shared actor, sequel-of, same director — are structural and verifiable. Interpretation edges — a shared archetype, an echoed arc — are the interesting ones, and the dangerous ones, because a model will happily invent them. So each interpretation edge must carry a rationale, a confidence score, and a citation, and it's shown to you as "a reading · 82%," never as fact. The rule is simple: no source, no edge. That single constraint is the whole difference between a knowledge graph and a hallucination with good posture.

Generated once, stored forever

Every synopsis, saga, crucial scene, fun fact, actor bio, and character card is generated once, offline, grounded in the committed corpus, and stored as compressed bundles in the repo. The deployed app reads them straight from disk — it needs no model at runtime. A film card opens with zero network calls and zero token spend. The only place a model lights up live is the optional natural-language "ask," and only when a key is present, capped at three asks per visitor per day. Determinism isn't just a reliability choice here; it's what makes the thing free to run and instant to open.

Actor & character cards

The rabbit holes are where the graph earns its name. Click an actor to see every face they've worn across the entire cosmos; click a character for a sourced bio and their thread through the films. The layer now spans every universe: 10,248 actors and 15,286 characters are first-class nodes — 1,396 in the MCU alone — and more than 3,000 of them carry pre-generated, sourced prose: the bio, the powers, the personality, the arc across their world. Those cards are committed bundles read straight off disk, so falling down a rabbit hole costs zero network calls and zero tokens. The graph was always there; the cards are the doors into it.

The read-only safety gate — LLM guardrails

This is the guardrail layer around the model: when the optional "ask" feature turns a question into a graph query, that query is never trusted on faith. A pure-Python validator runs first: it blanks string literals, checks for smuggled comments, scans for any forbidden write token, rejects multi-statement payloads, and verifies every label against the real schema. Anything that could modify the graph is refused before it touches the database — the model proposes a query, deterministic code decides whether it's allowed to run. The adversarial battery that proves it (block-these, allow-those) is CI-blocking and must pass 100%.