# TiddlyWiki — deep dive (findings) **Date:** 2026-06-14 · **Source:** SHARD-WP-0003 T3 · **Subject:** TiddlyWiki / TiddlyWiki5 (TW5), Jeremy Ruston's self-contained personal wiki. ## Why this dive The synthesis matrix names **whole-file write granularity** as one extreme of the write-granularity spectrum, anchored by TiddlyWiki. This dive confirms the anchor and the *portability extreme* it implies — a wiki that is **a single HTML file you can email** — and finds the twist: TiddlyWiki also has a **Node.js file-per-tiddler** substrate, so it spans the granularity spectrum the way Logseq spans file/DB (UC-62). The question: how does shard-wiki attach a backend whose *entire content is one file*? ## 1. The single-file model Classic TiddlyWiki ships as **one `.html` file** that contains **both**: 1. the **TiddlyWiki core** (the JavaScript engine, parser, renderer, UI), and 2. **every tiddler** (all content), serialized into the file. Open it in a browser and the file *is* the running application. There is **no server and no build step** — the app reconstitutes itself from the file it was loaded from. This is the portability extreme: a complete, self-hosting wiki in a single, emailable, USB-stick-able artifact that runs offline anywhere. **Saving** is the catch: a browser page cannot normally overwrite the file it came from, so TiddlyWiki uses **"savers"** — TiddlyFox/browser extension, the File System Access API, a Node.js server, TiddlySpot/put-savers, or "download a new copy." Crucially, **a save rewrites the *entire* HTML file** (core + all tiddlers re-serialized). Hence **whole-file write granularity**: there is no concept of writing one page in isolation in the single-file mode — every save touches the whole artifact. ## 2. The tiddler data model The atomic unit is the **tiddler** — a named record with **fields**: - Core fields: **`title`** (the identity), **`text`** (the body), **`tags`**, **`created`**, **`modified`**, **`type`** (content type, e.g. `text/vnd.tiddlywiki`, `text/markdown`), plus **arbitrary custom fields** (any key→value). A tiddler is effectively a **flexible flat record** — closer to a typed-field record (UC-34) than to prose-with-frontmatter. - **Everything is a tiddler**: not just pages, but tags, macros, templates, themes, plugins, and the wiki's own configuration are all tiddlers. A **plugin is a bundle of tiddlers**. - Content markup is **WikiText** (TW5's own), though `type` can mark a tiddler as Markdown, JSON, image, etc. **Transclusion** is native: `{{SomeTiddler}}` embeds another tiddler; `{{SomeTiddler!!field}}` embeds a field. ## 3. The dual substrate — single-file vs file-per-tiddler TiddlyWiki on **Node.js** stores each tiddler as a **separate `.tid` file** on disk: a small text file with a header of `field: value` lines, a blank line, then the body. The Node server assembles these into the same wiki at serve time. This substrate is: - **git-diffable and fine-grained** — one file per tiddler, line-level diffs, per-tiddler history — the *opposite* end of the granularity spectrum from the single HTML file. - the natural attach surface for a *versioned, multi-author* TiddlyWiki. So TiddlyWiki **spans the write-granularity spectrum by substrate** (single-file = whole-file write; Node = file-per-tiddler write), exactly as Logseq spans file/DB (UC-62) and as the backend-swap question (UC-43) anticipates. ## 4. Native query — filter expressions TiddlyWiki's query language is **filter expressions** over tiddler fields, e.g. `[tag[todo]!tag[done]sort[modified]]` — a compact DSL that selects/orders tiddlers by field and tag. Lists, tables, and dynamic views are built from filters. This is a **native-query capability** (UC-52 tier) — less expressive than SPARQL/datalog but real, and computed over the tiddler store. ## 5. Capability profile | Dimension (synthesis spectrum) | TiddlyWiki (single-file) | TiddlyWiki (Node `.tid`) | |--------------------------------|--------------------------|--------------------------| | Attachment mode | **file-store: one HTML file** | **file-store: dir of `.tid` files** | | Addressing granularity | tiddler (`title`) within the file | tiddler = one file | | Content identity | **`title`** (placement-bound) | title ↔ filename | | Structure | flat record store w/ arbitrary **fields** + tags | same | | History | none in-file (whole-file save) | **per-file git history** | | Merge model | whole-file replace (no merge) | git 3-way per tiddler | | Native query | **filter expressions** | filter expressions | | Translation | WikiText (or per-tiddler `type`: markdown/json/…) | same | | **Write granularity** | **whole file** (the anchor) | **file per tiddler** | | Operational envelope | trivial — a browser; no server | a Node server | | Access grant | file access = full access | server/file perms | | Content opacity | transparent (parse the HTML store) | transparent text | | Provenance | created/modified fields | git + fields | ## 6. INTENT mapping ### Reinforcements - **Graceful degradation**: a single-file TiddlyWiki is a *trivial* read-only / projection / backup shard — parse the tiddlers out of the HTML, project pages; no server needed. The limited-backend-still-usable principle at its simplest. - **Markdown-first but backend-neutral**: tiddlers carry a `type`, so Markdown tiddlers coexist with WikiText — the page model's content-type field maps directly. - **Typed fields** (UC-34): arbitrary tiddler fields are a flexible record model the page model already accommodates. - **Backend-swap under stable identity** (UC-43): single-file ↔ Node `.tid` is the same logical wiki on two substrates — the migration UC-43 anticipates, within one engine. ### Divergences (boundaries / notes) - **Whole-file write granularity** is a real constraint: in single-file mode shard-wiki cannot write one page atomically — an overlay applied "to one page" still **rewrites the whole file** (T11). This is the coarsest write tier; model it explicitly so overlays/locks account for it (a write to any page conflicts with any concurrent write). - **Identity = title**, file-local; cross-shard identity (T16) layered above. - **The app is in the file**: when parsing a single-file TiddlyWiki, shard-wiki must extract the **tiddler store** and ignore the embedded engine — i.e. treat the HTML as a *container format*, not as page content (don't mistake the app for content). ### What to keep 1. **Single-file self-contained wiki as a first-class file-store shard** — container-format parse, whole-file write granularity (UC-78); the portability/granularity extreme. 2. **Whole-file write granularity as a named tier** (T11) with overlay/lock implications. 3. **Dual-substrate binding** (single-file vs `.tid` dir) as another instance of substrate-choice under one identity (UC-43/UC-62). ## 7. UC seed | # | Seed | Disposition | |---|------|-------------| | UC-78 | Attach a **single-file self-contained wiki** (TiddlyWiki HTML) as one shard — parse tiddlers out of the container, project pages; **write = rewrite the whole file** (whole-file write granularity, the coarsest tier) | **new** | | — | whole-file write granularity anchor + overlay/lock implications | enrich **UC-35** | | — | single HTML file as a file-store shard (container format) | enrich **UC-40** | | — | tiddler arbitrary fields = flexible record | enrich **UC-34** | | — | filter expressions as a native-query tier | enrich **UC-52** | | — | single-file ↔ Node `.tid` substrate swap | enrich **UC-43** | ## 8. Architecture notes for SHARD-WP-0002 - **T11 (capability / write granularity):** confirm **whole-file** as the coarsest named write tier (anchored by single-file TiddlyWiki), with the implication that an overlay to *any* page conflicts with concurrent writes (no per-page atomicity). File-per-tiddler is the fine tier on the same engine. - **T14 (attach binding):** a single-file wiki binds as a **container-format file-store** (parse tiddler store, ignore embedded engine); a Node TiddlyWiki binds as a **dir of `.tid` files** (git-diffable). One engine, two bindings — parameterize like UC-43. - **Native query:** filter expressions are a low-mid native-query tier between "none" and datalog/SPARQL — delegate where present (UC-52). ## 9. Open questions 1. In single-file mode, how does shard-wiki represent **per-page overlays** when writes are whole-file — buffer overlays and re-serialize, or require the Node `.tid` substrate for write-through and treat single-file as read/projection/backup only? 2. Is a single-file TiddlyWiki's **embedded plugins/config** ever relevant to the union, or strictly ignored as app-internals (parse only content tiddlers)? 3. Does shard-wiki expose tiddler **filter expressions** as a delegated query, or only its own union query over projected tiddlers? ## 10. Sources - TiddlyWiki.com — *Tiddlers*, *TiddlerFields*, *Filters*, *Saving*, *Node.js* docs - *TiddlyWiki5* GitHub (Jermolene/TiddlyWiki5) — `.tid` file format, store structure - prior: `research/260614-logseq-deep-dive/` (file/DB dual substrate, UC-62) ## 11. Traceability New UC **UC-78** carries the marker **⊡** in the wikiengines column of `spec/UseCaseCatalog.md`. Enriched: UC-35, UC-40, UC-34, UC-52, UC-43. Architecture cross-refs: SHARD-WP-0002 T11 (whole-file tier), T14 (dual binding).