# Provenance This repository is the Adventure Storyteller production fork. Its application code comes from **AI-DnD**, and its planning package (`planning/`) is original to this project. ## Upstream | | | | --- | --- | | Project | AI-DnD | | Repository | | | Commit | `d72f7c1bda0f34fccd84afb7a25c34eb01c901de` | | Subject | Stop paying twice for a block a retry can still throw away | | Author date | Mon 31 Aug 2026 16:14:24 +0000 | | Position | tip of `upstream/main` on 1 Sep 2026, when the fork was taken | | License | MIT, © 2026 Parth Thakkar | The commit is the one pinned by `planning/DECISIONS/009-ai-dnd-production-base.md` after Phase 0B. It was not substituted for a newer upstream commit. ## How the fork is wired Upstream history is *in* this repository rather than copied out of it. The import is a merge of the pinned commit with `--allow-unrelated-histories`, so: - `git log d72f7c1bda0f34fccd84afb7a25c34eb01c901de` shows the real upstream history, not a squashed snapshot; - upstream code paths are unchanged (`backend/`, `frontend/`, …), so a later upstream commit can still be fetched and cherry-picked against matching files. Upstream's own documentation trees, `plan/` and `docs/`, were removed on 2026-09-03: they described the hosted, scripted, multi-user product this fork is not. They remain in this repository's history and in upstream; - the planning package that predates the fork keeps its own history on the other parent of the merge. To re-verify from a fresh clone: ```bash git remote add upstream https://github.com/parththakkar106/AI-DnD.git git fetch --no-tags upstream git cat-file -t d72f7c1bda0f34fccd84afb7a25c34eb01c901de # -> commit git merge-base --is-ancestor d72f7c1bda0f34fccd84afb7a25c34eb01c901de HEAD && echo "in this history" ``` ## License Upstream is MIT. `LICENSE` is upstream's file, unmodified, and the copyright notice stays with it. The MIT terms require that the notice travel with the code and with substantial portions of it; keep `LICENSE` in place in any redistribution of this fork, including a packaged build. Work done in this repository after the fork is a derivative of that MIT-licensed code. ## Vendored third-party assets Both were added by Milestone M1 to remove a runtime Internet dependency. Each is redistributable and each has a regeneration path in the tree, so neither is an opaque binary nobody can rebuild. ### `backend/app/context/vendor/cl100k_base.tiktoken` The BPE merge table for OpenAI's `cl100k_base` tokenizer, used for context budgeting only — no model of OpenAI's is ever called. - Source: `https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken` - SHA-256: `223921b76ee99bde995b7ff738513eef100fb51d18c93597a113bcffe865b2a7`, which is the digest `tiktoken` itself pins for that URL, and which `backend/app/context/encoding.py` re-checks every time it builds the encoding. - Published by OpenAI for use with `tiktoken` (MIT). ### `frontend/public/fonts/*.woff2` Cinzel, Crimson Pro and Inter, Latin and Latin Extended subsets, as variable fonts. All three are licensed under the SIL Open Font License 1.1; the license text ships beside them as `OFL-cinzel.txt`, `OFL-crimsonpro.txt` and `OFL-inter.txt`, which is what the OFL requires of a redistribution. Regenerate with `python3 frontend/tools/vendor_fonts.py`, which also rewrites `frontend/src/styles/fonts.css`. ## What this fork changed in Milestone M7 M7 is additive. It builds the imported knowledge library the specification asks for as a **separate first-class subsystem**, which is the Phase 0B decision recorded in `planning/IMPORTED-KNOWLEDGE-DESIGN.md` §73: AI-DnD's Story Cards do not carry the classification, provenance, chunking, index, lifecycle or inspection an imported-knowledge system needs, and they were not promoted into one. Story Cards are untouched and still work exactly as upstream left them; nothing in the new subsystem reads or writes one. - `backend/app/knowledge/` (new) — the whole subsystem: the three classes and their prompt framing, a deterministic heading-aware chunker, the SQLite FTS5 lexical index, local Ollama embeddings, hybrid retrieval and reranking, and the budgeted injection into the prompt. - `backend/app/routers/adventures/knowledge.py` (new) — import, list, inspect, reclassify, enable/disable, delete, reindex and status. The import surface is a multipart upload; **no endpoint anywhere accepts a filesystem path**. - `backend/app/models.py` — three new tables (`knowledge_sources`, `knowledge_chunks`, `knowledge_embeddings`) and the DDL hook that carries the FTS5 virtual table with the table it indexes. - `backend/app/migrations.py` — version 92. - `backend/app/context/builder.py` — the knowledge sections, their budget, and the provenance record in the context snapshot. - `backend/app/bundle.py` — the export carries source content and the reader's judgements about it; passages, index rows and vectors are rebuilt on import. - `backend/app/derived.py`, `backend/app/memorybank.py` — a `knowledge` kind of derived work, and the post-turn pass that catches up vectors an import could not build. - `frontend/src/pages/Play/panels/KnowledgePanel.jsx` (new), `frontend/src/styles/knowledge.css` (new), and additions to the Insights panel — a utilitarian browser surface for the whole lifecycle. Imported text is displayed as inert text and is never rendered as HTML. - **One new runtime dependency**, `python-multipart` — Starlette's multipart parser, pure Python, Apache-2.0, no dependencies of its own. It is what makes the upload surface possible and is the reason no path is ever accepted. No network path was added. Embeddings go through the same `OpenAICompatibleProvider` the memory bank uses, so the endpoint allowlist, the request-time re-check and the OS/private-CA trust union all apply unchanged (ADR 011). Lexical indexing is local SQLite and touches no socket at all. ## What this fork changed in Milestone M2 M2 is subtractive. It reduced the inherited application to the intended single-user, local-first trust boundary. **Nothing was added that upstream did not have, except the endpoint policy and the tests that hold these removals in place.** Removed in full: campaign scripting and the QuickJS sandbox; multi-user accounts, guest sessions, login, registration and the shared demo key; the visitor analytics tables, dashboard and beacon; the access log; per-IP and per-user rate limiting and quotas; Render deployment config; Postgres/Neon support; cloud inference providers and the API-key field; session-cookie signing and API-key encryption at rest. Added: `backend/app/endpoints.py`, which decides what an inference endpoint may be, and a configurable model timeout. Three database tables (`scripts`, `adventure_scripts`, `analytics_daily`, `analytics_visitor_days`, `access_log`) and four columns (`adventures.script_state`, `settings.api_key`, `users.demo_turns_used`, `users.demo_turns_date`) are left in place, unmapped or inert, so that an existing M1 campaign database opens unchanged. They are not product functionality and nothing reads or writes them. ## What this fork changed in Milestone M1 Nothing was removed from upstream. The changes are the offline/locality hardening M1 called for; see `planning/archive/milestone-reports/M1-BASELINE-REPORT.md` for the evidence. - `backend/app/context/encoding.py` (new) and `backend/app/context/builder.py` — build `cl100k_base` from the vendored table instead of downloading it on first use. - `frontend/index.html`, `frontend/src/index.css`, `frontend/src/styles/fonts.css` (new), `frontend/public/fonts/` (new), `frontend/tools/vendor_fonts.py` (new) — self-hosted fonts in place of the Google Fonts link. - `backend/app/main.py` — CSP narrowed to same-origin, with the two Google hosts dropped and `object-src` / `base-uri` / `form-action` added; `woff2` registered so the self-hosted fonts are served with their real media type. - `backend/app/tlstrust.py` (new), `backend/app/providers/openai_compatible.py`, `backend/app/routers/settings.py` — outbound HTTPS verifies against the machine's own CA store as well as certifi's, so a trusted-LAN Ollama with a locally-issued certificate works. Verification is not relaxed. - `start.sh`, `start.ps1`, `docker-compose.yml` — the storyteller listener is explicitly loopback-bound. - `backend/requirements.lock` (new) — the exact tested dependency closure. - `backend/tests/test_offline_assets.py` and `backend/tests/test_tls_trust.py` (new) — regression tests for the above. - `DEVELOPMENT.md` (new) — environment setup and Ollama configuration.