# Findings — Trilium (TriliumNext): note cloning, attribute inheritance, HTML-native Date: 2026-06-14 Source kind: **modern shipped product** — an open-source hierarchical PKB; a *candidate shard* whose distinctive traits are **note cloning (a DAG hierarchy)**, an **attribute/relation system with inheritance + templates**, and **HTML-native** content Lens: shard-wiki — the namespace model (identity vs placement), inherited/computed metadata, HTML translation, per-note encryption, and the scripting/ETAPI surfaces > Why Trilium earns a dive. It is another SQLite-local note app, but it brings a > structural feature **none of the prior twelve systems had: note cloning** — a single > note can sit in **multiple places in the tree at once**, so the hierarchy is a **DAG, > not a tree**, and **note identity is cleanly separated from placement** (a note has > many "branches"). That directly challenges shard-wiki's namespace model (UC-22 assumes > a path) and is the clone/reference primitive made concrete at the *namespace* level. > Trilium also has an **attribute system with inheritance and templates** (effective > metadata is computed, not just stored), and it is one of the few **HTML-native** (not > Markdown) tools — a useful stress on "Markdown-first must degrade." Lineage note (like TWiki→Foswiki): **TriliumNext** is the community fork of the original **Trilium** (zadam) after it went maintenance-only; the active project is TriliumNext. Contrast set: Joplin (SQLite-local, files-on-sync, page-level), Logseq (block-graph on files), Notion (hosted DB, schema+relations). Trilium = **SQLite-local, DAG hierarchy, inherited attributes, HTML content, self-host sync + ETAPI**. --- ## 1. Core architecture — one SQLite file, server-syncable - **Storage:** a single **SQLite** file (`document.db`, via better-sqlite3) holding notes, attachments, history, and settings. Local DB store (like Joplin; not files). - **Clients/sync:** desktop (Electron) or a self-hosted **server**; multi-instance **sync protocol** with **conflict resolution + entity change tracking**, plus **WebSocket** realtime updates. Attach via the server, the ETAPI (§5), or the DB. - **IDs:** 12-char IDs — **`noteId`, `branchId`, `attributeId`, `attachmentId`**. The **note vs branch** split is the key idea (§2). - **Export:** to Markdown/HTML (lossy — content is HTML, §4). --- ## 2. Note cloning — a DAG hierarchy, identity separated from placement Notes form an arbitrarily deep tree, **but a single note can be placed in multiple locations** — "cloning," implemented via the **branches** model: - A **note** (`noteId`) is the content + identity. A **branch** (`branchId`) is *one placement* of that note under a parent (with an optional **branch prefix** giving per-location context). A note with several branches is **cloned** — it appears in several places at once; editing it anywhere edits the one note. - So the hierarchy is a **DAG**, not a tree, and **identity (note) is separated from location (branch)**. There is no single canonical path for a cloned note. This is the most shard-wiki-relevant feature in the dive: - It breaks the assumption behind **UC-22** (resolve a page by *a* path). shard-wiki's namespace model must allow **a page in multiple namespace locations** with no single canonical path (UC-66). - The **note/branch split** is a clean model shard-wiki should borrow: **page identity ≠ page placement**. A page is one entity; its locations are separate placement records — exactly how shard-wiki should treat a page that appears under multiple paths/shards. - It is the **clone/reference-not-copy primitive** (Xanadu clone, ZigZag clone, UC-44/45, T16) realized at the *namespace* level: same note, many positions, one source of truth. --- ## 3. Attributes — labels + relations, with inheritance and templates Trilium's metadata system is **attributes**: - **Labels** (`#tag`, optionally `#key=value`) — typed metadata on a note. - **Relations** (`~relation`) — **typed links to other notes** (a knowledge graph). - **Inheritance** — attributes can be **inherited down the subtree** (inheritable attributes), and **templates** (a note whose attributes/structure are applied to instances via a `~template` relation) inject attribute sets. So a note's **effective metadata = its own attributes + inherited + templated** — computed, not just stored. - Promoted attributes give a form-like UI; attributes drive **search/queries** and **scripting**. shard-wiki read: this is structured data (UC-34) and typed relations (UC-58), but with a **new wrinkle — inheritance/templates make metadata *computed***. Projecting such a shard must distinguish **effective vs own** attributes (and record their provenance: own / inherited-from / template), not flatten them (UC-67). Templates also reinforce blueprint pages (UC-15). --- ## 4. Content model — HTML-native, many note types - **Text notes are HTML** (WYSIWYG via **CKEditor5**), **not Markdown**. Trilium is one of the few HTML-first tools studied. Markdown participation therefore needs **HTML↔ Markdown translation** — more tractable than Notion's block model but still **lossy** for some constructs (CKEditor features, includes) → a fidelity-aware case bridging UC-42 (lossless ideal) and UC-59 (lossy-with-report). - **Note types:** text (HTML), code (CodeMirror), **canvas (Excalidraw)**, relation maps, mind maps (Mind Elixir), spreadsheets (Univer), geo maps (Leaflet), **render notes**, file/image, book — i.e. lots of **non-Markdown content** (UC-55) and **script/render-generated dynamic notes** (UC-54). --- ## 5. Extension surfaces — scripting (in-app) + ETAPI (external) Two surfaces, like Joplin: - **Scripting (in-engine host).** **Code notes** in JS run as **frontend** (UI widgets, buttons) or **backend** (server-side automation) scripts against a **Script API** (create/query notes, attributes, etc.). Executable, in-app — the adapter-host path (UC-38). "Render notes" + scripts produce **dynamic, generated content** (UC-54). - **ETAPI (external REST API).** Trilium's public REST API (since v0.50), **token auth** (`Bearer ETAPITOKEN`), with client libs (`trilium-py`): CRUD over notes/branches/ attributes/attachments, search, import/export — an external attach surface (UC-57) that, unlike Joplin's localhost-only Data API, is the **designed external integration API** for a (often self-hosted) server. --- ## 6. Security — per-note (partial) encryption Trilium offers **strong per-note encryption** ("protected notes") unlocked in a **protected session** (password; TOTP/OpenID for login). Crucially this is **per-note**: a shard can hold **some encrypted and some plaintext notes at once**. This refines the **content-opacity** dimension (UC-61, proposed twelfth spectrum from Joplin/Anytype): opacity is **per-item, not only whole-shard** — the adapter must handle a shard where *part* of the content is opaque without a key (protected notes projectable only as structure-shell; unprotected notes fully). --- ## 7. Trilium as a shard — capability profile | Capability | Trilium | Notes for the adapter contract | |------------|---------|--------------------------------| | Read | yes (ETAPI / sync / DB) | ETAPI REST (server) is the clean surface | | Write | yes (ETAPI / script) | per-note; via ETAPI or backend code notes | | Write granularity | per-note (page) | — | | Identity / addressing | **`noteId` + `branchId`** (12-char) | **identity ≠ placement**; cloned notes have many branches (UC-51, UC-66) | | Hierarchy | **DAG (cloning)** | a note in multiple locations; no single canonical path (UC-66) | | Structure | **labels + relations, inherited + templated** | effective vs own metadata (UC-34, UC-58, UC-67) | | Content | **HTML (CKEditor)** + many types | HTML↔MD lossy translation (UC-42/59); non-MD types (UC-55) | | History | internal revisions in the DB | not portable git → supplement (UC-36) | | Native query | attribute search / script queries | delegate or build index (UC-52/63) | | Subscribe | WebSocket + sync protocol | push; conflict resolution built in (UC-31) | | Content opacity | **per-note encryption (partial)** | per-item opacity (UC-61 refined) | | Extension | **scripting (in-app) + ETAPI (external)** | dual surface (UC-38, UC-57) | | Templates | yes (`~template`) | blueprint pages (UC-15) | Verdict: a capable **SQLite-local, server-syncable** shard best attached via **ETAPI**. Standout demands: the **DAG hierarchy / note-branch identity model** (UC-66) and **inherited/templated attributes** (UC-67); plus HTML-native translation, per-note opacity, and the scripting host. --- ## 8. Mapping to shard-wiki INTENT (compare, do not equate) ### 8.1 Reinforcements - **Identity ≠ placement** (note/branch) is a model shard-wiki should adopt for pages that appear under multiple paths or in multiple shards — provenance and union without erasure depend on separating "what a page is" from "where it sits." - **Typed relations + templates** validate the structured/relation demand (UC-58) and blueprints (UC-15) on a local-first, self-hostable backend. - **ETAPI** is a clean example of a **designed external REST surface** for a self-hosted server (cf. Notion external API, but self-hosted/trust-boundary friendly). ### 8.2 Deliberate divergences (design bugs if conflated) 1. **Don't force a single canonical path.** A cloned note has many placements; modeling it with one path loses information (UC-66). Use the note/branch separation. 2. **Don't flatten computed metadata.** Effective attributes include inherited/templated values; record provenance (own vs inherited vs template), don't collapse (UC-67). 3. **HTML-native, not Markdown.** Translate HTML↔Markdown with a fidelity report; degrade to read-only where lossy (UC-42/59/03). Markdown-first must degrade gracefully. 4. **Per-note opacity.** Some notes are encrypted; never surface protected-note ciphertext; project them as structure-shell (UC-61). 5. **One Trilium instance = one shard**, not the federation layer; its sync protocol is its own — attach via ETAPI/replica, don't re-drive it (not-a-sync-daemon). ### 8.3 What Trilium teaches that shard-wiki should keep - **Separate page identity from placement** (note vs branch) — the cleanest model for multi-location / multi-shard pages and for the clone/reference primitive (T16). - **Model metadata as computed** (own + inherited + templated) with per-attribute provenance (UC-67) — not a flat key/value bag. - **Content opacity is per-item**, not only whole-shard (UC-61 refinement). --- ## 9. Use-case seeds → catalog (promoted 2026-06-14) Last existing UC is **UC-65**. New UCs **UC-66, UC-67** added; existing UCs enriched. | Seed | Catalog action | |------|----------------| | **Attach a shard with a DAG hierarchy / note cloning** — a page may occupy multiple namespace locations at once (note identity separated from placement via branches); no single canonical path | **UC-66 (new)** | | **Preserve inherited / templated attributes** — project a structured shard whose metadata is computed (own + inherited + template), distinguishing effective vs own with per-attribute provenance | **UC-67 (new)** | | DAG/multi-parent vs tree; note/branch (identity vs placement) | **enriches UC-22** | | Labels (`#tag`) + typed relations (`~relation`); HTML-native | **enriches UC-34** | | Templates (`~template`) inject attribute sets | **enriches UC-15** | | HTML (CKEditor) content → HTML↔Markdown lossy translation | **enriches UC-42** (links UC-59) | | Per-note encryption (protected notes) = **partial** content opacity | **enriches UC-61** | | Scripting (frontend/backend code notes, Script API) = in-app host | **enriches UC-38** | | ETAPI (token REST) = designed external surface for a self-host server | links UC-57 | | Render/script notes + attribute queries = dynamic content | links UC-54; typed relations link UC-58 | | `noteId`/`branchId` 12-char IDs | links UC-51 | --- ## 10. Architecture notes for SHARD-WP-0002 (no UC) - **Namespace model must support a DAG** and **separate page identity from placement** (note/branch). A page is one entity with N placements (paths/shards). Feeds the page/ namespace model and the clone/reference primitive. (T12, T16.) - **Computed/inherited metadata** (UC-67): the page model's structured-data representation must carry **effective vs own** with per-attribute provenance (own/inherited/template), not a flat bag. (T12.) - **Content opacity is per-item** (UC-61 refinement): the content-opacity capability (proposed twelfth spectrum) should be **granular** (per-note), not only whole-shard. (T11.) - **HTML as a source content model** joins TML/Notion-blocks in the translation capability (HTML↔Markdown, lossy-aware). (T15.) - **Scripting as an in-engine host** + **ETAPI external REST** are two more adapter-host exemplars (with Roam/Obsidian/Joplin). (T14.) --- ## 11. Open questions (for spec / workplans) 1. How does shard-wiki represent a **cloned note** in the union — one page with multiple path placements, or a page transcluded into multiple locations? (UC-66 vs UC-44/45.) 2. When projecting **inherited attributes** (UC-67), does shard-wiki materialize effective values (snapshot) or compute them live from the shard's tree/templates? 3. Is **HTML↔Markdown** round-trip lossless enough for write-back overlays, or are Trilium overlays read-only/native-HTML (cf. UC-42 Q2)? 4. For **per-note encryption** (UC-61), is a partially-opaque shard projected with protected notes as visible-but-opaque placeholders, or hidden entirely? --- ## 12. Sources | Source | Used for | |--------|----------| | TriliumNext/Trilium — DeepWiki (https://deepwiki.com/TriliumNext/Trilium) | SQLite `document.db`/better-sqlite3; tree + cloning via branches; note types; sync | | Cloning notes — TriliumNext wiki (https://github.com/TriliumNext/Trilium/wiki/Cloning-notes) | Note in multiple locations; branches; branch prefixes | | TriliumNext — DeepWiki API & Synchronization (https://deepwiki.com/TriliumNext/Trilium/4.3-api-and-synchronization) | WebSocket + sync protocol w/ conflict resolution; 12-char IDs (note/branch/attribute/attachment) | | ETAPI (REST API) — Trilium docs (https://docs.triliumnotes.org/User%20Guide/User%20Guide/Advanced%20Usage/ETAPI%20(REST%20API)/) | Public REST API since v0.50; token/Bearer auth; trilium-py | | Script API — TriliumNext wiki (https://github.com/TriliumNext/Trilium/wiki/Script-API) | Frontend/backend code notes; Script API | | BrightCoding — TriliumNext overview (https://www.blog.brightcoding.dev/2025/09/20/triliumnext-notes...) | Attributes (labels/relations), inheritance, templates, per-note encryption, note types | Cross-references: `research/260614-joplin-deep-dive/findings.md` (SQLite-local, dual surface, content opacity), `research/260614-notion-deep-dive/findings.md` (typed relations, external API), `research/260614-zigzag-deep-dive/findings.md` (clone / dimensions), `research/260614-shard-spectrum-synthesis/findings.md` (spectra this refines), `spec/UseCaseCatalog.md` (UC-15, UC-22, UC-34, UC-38, UC-42, UC-51, UC-54, UC-57, UC-58, UC-61), `workplans/SHARD-WP-0002-federation-architecture.md` (T11, T12, T14, T15, T16). --- ## 13. Traceability - New UCs: **UC-66, UC-67** → `spec/UseCaseCatalog.md`. - Enriched UCs: **UC-15, UC-22, UC-34, UC-38, UC-42, UC-61** (links UC-51, UC-54, UC-57, UC-58, UC-59). - Architecture (no UC): DAG namespace + identity/placement (note/branch) split; computed/inherited metadata; per-item content opacity; HTML source model; scripting + ETAPI host surfaces → `SHARD-WP-0002` (T11, T12, T14, T15, T16). - Boundary recorded: Trilium (TriliumNext) is **one SQLite-local candidate shard** with a DAG hierarchy and computed metadata, best attached via **ETAPI**; HTML-native (lossy to Markdown), per-note opacity; not a substrate, not the federation layer (INTENT graceful-degradation, no-silent-mutation, not-a-sync-daemon).