Validates the three finalists by clone, build, test run and live local Ollama inference, then answers the fork question with measurements rather than static review. Recommendation: fork AI-DnD, confidence high. The Phase 0A call holds, but it was wrong that AI-DnD's undo is non-destructive — retry preserves the replaced take, undo hard-deletes it. A follow-up spike fixed that in 3 files (+130/-31): undo now moves a head cursor, redo round-trips, writing below a moved-back head forks and keeps the abandoned line, branch-scoped memory isolation survives, suite 627/632 with all 5 failures asserting the deleted-row behaviour that was replaced. Findings that change the plan: - AI-DnD cannot take a turn air-gapped as shipped; tiktoken fetches its encoding from a CDN. Proven on an internal Docker network, proven fixed by vendoring the file. - ai-adventure needs zero code for Ollama — two config lines — and its turn/head/checkpoint schema is the target model to build to. - Open Dungeon has zero automated tests and a positional summary watermark, making its branch retrofit larger than Phase 0A costed. - The world-state referee takes relative deltas; a 3B model sent absolute values under full context, so a wounded player ended at full health. Validation cannot catch this, so prefer ai-adventure's typed-event vocabulary when generalising narrative state. - Export/import recomputes head depth, so a round-trip silently undoes an undo. Must be fixed alongside the undo work. Docs only; no production code. Working tree from the runs stays untracked under phase0b/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015gUPLuxLs8wypxZPEmccJu
4.9 KiB
Phase 0B — Offline and Network Behavior
Method: a Docker network created with --internal (no NAT, no DNS to the
outside). The Ollama container was attached to it so the app could reach a model
while having no path to the Internet. Isolation was verified from inside the
app container before testing:
blocked ('1.1.1.1', 443) OSError
blocked ('openrouter.ai', 443) gaierror
blocked ('fonts.googleapis.com', 443) gaierror
AI-DnD — fails offline as shipped; fine once one file is vendored
First turn on the isolated network died. The SSE stream emitted the player
event and stopped. Container log:
requests.exceptions.ConnectionError: HTTPSConnectionPool(
host='openaipublic.blob.core.windows.net', port=443):
Max retries exceeded with url: /encodings/cl100k_base.tiktoken
(NameResolutionError ... Temporary failure in name resolution)
tiktoken downloads its BPE encoding on first use, and AI-DnD calls it for
context budgeting on every turn. On the host this was invisible, because the
file had already been cached to /tmp/data-gym-cache/9b5ad71b… during an
earlier online run.
After copying that 1.7 MB file into the container:
OFFLINE TURN GENERATED: "I am Vale, an explorer. I journey alone through the
darkened depths of the lighthouse..."
So: a hard blocker on a clean air-gapped install, and a packaging fix — vendor
the encoding (or pre-seed TIKTOKEN_CACHE_DIR, or replace the tokenizer). Worth
stressing that static analysis could not have found this; it took an actually
isolated run.
Other AI-DnD network surface:
- Google Fonts at runtime. The built SPA's
index.htmlstill containsfonts.googleapis.com/css2?family=Cinzel...&family=Crimson+Pro...&family=Inter..., andmain.py's CSP explicitly allowsfonts.googleapis.com/fonts.gstatic.com. Violates specification §12. Fix: self-host three families. - Only other remote host in backend Python is
https://openrouter.ai/api/v1, used as a default endpoint constant and a header-attribution host check. Both removable with the cloud-provider path. analytics.pyis a self-hosted counter writing to two local tables. No third-party script, no outbound request. It records no IP, no user agent, and hashes the user id with HMAC. Removable, and not a telemetry leak in the meantime.- Connection test and turns honour the configured endpoint only; no automatic URL retrieval or content fetching was observed.
Open Dungeon — runtime clean, build and telemetry are not
- Next.js telemetry is on by default and printed its notice on first start.
Needs
NEXT_TELEMETRY_DISABLED=1ornext telemetry disablein the production configuration. - Fonts are fine at runtime.
layout.tsxusesnext/font/google, which downloads at build time and self-hosts. The served page contains nofonts.googleapis.comreference. The trade-off is that the build needs network access. - Remote hosts referenced in
src/:openrouter.ai(preset provider URL and a model list), plusko-fi.comandgithub.com/sponsorsdonation links in the UI. Everything else is127.0.0.1/localhost(13 occurrences). - Local story play needs no cloud service: Ollama for text, a local FLUX worker or the user's own ComfyUI for images.
- 335 npm packages with 6 high-severity advisories is the largest supply-chain surface of the three.
ai-adventure — verifiably local-only
The strongest posture by a wide margin, and the easiest to audit:
- Exactly one outbound call site in the whole codebase:
urlopeninllm/lm_studio.py. Nothing else inlocal_adventure/opens a socket. - No hardcoded remote host anywhere. The only
httpstring in the package is the scheme check incontent/models.py. - One direct dependency (
pydantic), six packages in the closure. - It is the only candidate that already implements specification §12's non-local-endpoint warning:
def _endpoint_warnings(config):
hostname = urlparse(config.model.base_url).hostname
if hostname not in {"127.0.0.1", "localhost", "::1"}:
return ["model.base_url is not loopback; game prompts and content will
be sent to that endpoint. Enable API authentication."]
audit.store_promptsdefaults tofalse, with prompt hashes stored instead.- Tests, world validation, session creation, replay, branching and export are all offline by construction.
Summary
| Local play works offline | Cloud service required | Telemetry / remote assets | Removal difficulty | |
|---|---|---|---|---|
| AI-DnD | Only after vendoring the tiktoken encoding | No | Google Fonts at runtime; local-only analytics tables | Low — one vendored file, three self-hosted fonts, delete analytics |
| Open Dungeon | Yes at runtime; build needs network | No | Next.js telemetry on by default | Low — one env var; fonts already self-hosted |
| ai-adventure | Yes, unconditionally | No | None | Nothing to remove |