Lineage dashboard
The dashboard for a Lineage project: the family's sources, the Familypedia, the genealogy, the
timeline and the stories, in one place, with every fact traceable to its source and nothing
made up by the machine. Stories render as book pages and can be narrated; the printed book is
built from the same record. It runs on your own computer and binds to 127.0.0.1 only.
./lineage # http://127.0.0.1:8777, project ~/lineage-books/my-book
./lineage ~/books/grandma # point it at a project folder
python3 server.py --command bash # use a different terminal command
python3 server.py --demo # invented sample content, with a banner; never the default
Python 3.10+, standard library only. The page loads xterm.js and fonts from a CDN.
Layout of the code
server.py ENGINE (/api/engine/*: sources, stories, narration, timeline, Familypedia,
genealogy, home, requests), dashboard state, AUTHOR-ONLY SURFACE (terminal,
keys, repo, Drive, CLIs, contributors), HTTP (routing, session token, static)
familypedia.py the Familypedia engine: nine article types, infoboxes, tiers, records,
tagging, links, search and the map (server.py routes to it)
static/index.html the page shell
static/css/app.css the styles
static/js/core.js labels, routing, the settings gear, the dock (player + terminal), overlays
static/js/<tab>.js one file per surface: home, sources, familypedia, genealogy, stories,
timeline, settings (the four settings pages), terminal (the drawer)
~/.lineage/ keys (config.json) and the Google token, chmod 600, this machine only
<project>/lineage.json settings
<project>/data/genealogy/ derived.json (approved rebuild), mine.json (my edits),
proposed.json (a rebuild awaiting review), history.json
<project>/data/requests.json what you've asked for, with dates and status
<project>/.lineage/ terminal log, rendered pages, thumbnails, crest attempts
The engine endpoints use plain vocabulary so a simpler front end can sit on the same server later. Everything technical is grouped under the author-only surface, marked as such in the code.
The surfaces
No wizard: every tab works whenever you open it and says plainly what it still needs.
Tabs (the working surfaces):
| Tab | What it's for |
|---|---|
| Home | The landing page. Story of the day (seeded by the date; "Another" steps through), a featured relative (people with material but no story first), Needs you (one-click actions ordered by what they unblock), Request more (question lists built from open questions, gaps and unconfirmed links, saved as asked/answered), counts at a glance, and a plain activity feed. An empty project shows one card: what to add first. |
| Sources | Recordings, scans, letters. Intake runs visibly (Saved → Reading → Understanding → Indexed), with edit, rename, re-ingest, trash and restore, bulk actions. |
| Familypedia | An article for every subject the material names: person · place · event · vessel/vehicle · organization/unit · object · publication · occupation/trade · theme. Each has a lead from the material, a typed infobox, tier sections (witnessed · told · lore · what the records show), the passages that mention it, sources with thumbnails, typed records with archive, number, link and retrieval date, photographs and marked illustrations, stories, related articles, backlinks, open questions and a separate “Beyond the family”. Browse by type, A–Z, most material and needs more; full-text search with type filters; [[links]] across types; a map drawn from the records' own coordinates; Records and Photographs views with bulk tagging. |
| Genealogy | The tree, derived from the sources. Every link carries its quoted evidence; no evidence, no link. Rebuild with a review of what changed (contradictions kept, never resolved silently); pan/zoom tree with descendant, ancestor and hourglass layouts, unknown-parent nodes and line styles by tier; Cast view; merge, split, add links and notes (kept across rebuilds); GEDCOM in and out (imports arrive unconfirmed); SVG, PNG and a printable chart. Living people are left out of exports. |
| Stories | Every story, oldest first in era bands. Read (real book pages through the Typst template) and Listen on the same row; Narrate / Re-narrate with ElevenLabs, a voice per story, stale-audio marks, Narrate all with a character count, a pinned player, audio download. Unapproved bridges are never narrated. "Generate" hands work to the Genealogist. |
| Timeline | A vertical spine with decade bands and a year rail; cards with date and precision, tier, people, place, citations and story links; stars; conflict cards; gap cards with "Add to questions"; an undated drawer; filters and search; SVG, PNG and a printable appendix. |
Behind the gear (#/settings/<section>, each with its own section list; Esc or Done
returns to the tab you came from; a dot on the gear means something needs attention):
| Page | What it holds |
|---|---|
| Family details | Lineage title, family name, subtitle, summary (with a draft button), subject, date range and places, and the crest: None (default) · Generate (four candidates per try, every attempt kept) · Upload (original kept; background removal and one-colour copies), a show toggle per surface, previews in context, provenance. |
| Connectors | Google Drive, GitHub, Anthropic, OpenAI, ElevenLabs, agent CLIs; honest "not wired up" cards. |
| Contributors | People who add material (the family is the subject; contributors are who adds to it): roles, invite links, requests outstanding, the shared folder, the review queue. |
| Project settings | Repo, Drive folder, narration voice, narrator and writing style, story templates, trim and printer, the terminal command, resolved paths. |
The terminal is a drawer, not a tab: the Terminal button in the header (or Ctrl+)
opens it over any tab. It holds the Genealogist, quick prompts, the pipeline actions and the
approvals list. Old addresses (#read,#listen,#transcribe,#family,#connectors,#settings`) redirect to their new homes.
The UI calls the written pieces stories; the printed book still has chapters, and the files
keep their names (chapters/, data/chapters.csv). The mapping lives in one place, the L
labels object in static/js/core.js.
The terminal: security model
The drawer embeds a real terminal (a PTY running claude, or whatever you choose). It is a
shell on your machine, so:
- the server binds to 127.0.0.1 only, and there is no option to change that;
- every /api/* call needs a session token minted at server start and injected into the page;
other websites can't read it;
- requests whose Host header isn't 127.0.0.1 or localhost are refused (DNS rebinding);
- project files are only served from inside the project folder.
Output streams to the browser over Server-Sent Events; keystrokes go up with POST. No websockets, no extra dependencies.
What is real, and what isn't yet
Real and tested: - the server, security model and PTY terminal; - intake end to end (PDF, image, audio, text) with dedupe, trash and restore; - the Familypedia (all nine types), timeline and event pages, built from units, timeline, transcripts, the knowledge graph, records, photo index and routes; tagging of sources, records, photographs and events to any article, with suggestions that need acceptance; the evidence-coded map (tested on a 1,344-article project: index 0.5 s, cached reads 10 ms); - the genealogy rebuild (Claude, evidence checked against the material, no evidence no link), review and apply, my edits, GEDCOM round trip; - Home: story of the day, featured relative, Needs you, requests, counts, activity; - stories with states, rendering to pages, narration script extraction (bridges refused), and what each story rests on: citation chips per paragraph and each image's provenance, with a toolbar; - the Sources gallery and lightbox; illustrations are marked and never used as a person's picture; - settings, identity, stale marks, repo verification, live key checks, voice list.
Written, not tested end to end on the build machine: ElevenLabs narration (no key used in tests), crest generation (OpenAI), Google Drive sign-in and sync.
Not built yet: see ROADMAP.md (annotation and people tagging on photos, the public records
catalogue, the impact pass and "update everything this affects", hyperlinked stories with images
editable in place, "Beyond the family", the contributor-facing page behind an invite link).
Familypedia: what it reads and writes
Articles come only from the project's files. Everything is optional; a project with none of these simply has fewer articles.
| Input | Gives |
|---|---|
content/units/*.md front matter people, places, subjects ("vessel: Hannibal") |
subjects, and which units mention them |
facts/timeline.csv |
events, their people and places, tiers, conflicts |
facts/people/*.md (# Name, Also called:, Relationship to …:, Dates:) |
person profiles and other names |
knowledge/graph.json (or nodes.csv + edges.csv) |
typed subjects (person, place, event, voyage, vessel, organization, unit, object, publication, occupation, theme), records (record, letter, photograph, document) and relations (crew_on, master_of, voyage_of, served_in, held_by, part_of, mentions, …) |
data/archives.csv |
the records catalogue (records-archives skill) |
facts/records/**/sources.csv (id, title, url, holder, type, date_retrieved) and **/sources.json |
research sources and retrieval dates by URL |
facts/**/*track*.csv, *route*.csv (make_route_map.py columns) |
ports and positions; coordinates for the map. A new leg, gap_before, or a row without coordinates is an unrecorded leg |
photos/photo_index.csv |
photographs; rows whose subject starts "Illustration" are marked as generated |
facts/gaps.md, facts/records/**/context_*.md |
open questions; public background offered under "Beyond the family" |
facts/records/_raw/geo/*.geojson (or facts/records/sources/geo/) |
coastlines for the map. No map tiles are ever fetched |
What it writes, all under data/familypedia/ and all the author's own:
<slug>.json (lead, notes, infobox values, other names, type, coordinates, "same as" merges,
background), subjects.json (subjects created with "New subject…"), tags.json (tags on
source:, record:, photo: and event: targets, each with state and evidence), and
routes.json (which article a route belongs to when its file name matches more than one).
Suggested tags come only from names written in the item, with the words that name them; no
faces, no resemblance, nothing tagged until accepted.