Notes → collaborative documents (HedgeDoc-class)¶
Status: Design + plan of record. 2026-06-17. Reference clone:
/root/references/hedgedoc.
Goal¶
Turn the single-user title + Markdown Notizen into a collaborative document editor: shareable
between users and groups, real-time co-editing, a real markdown editor, organization
(tags/folders/frontmatter/slides), import/export, and revision history. Driven by a HedgeDoc gap
analysis; we adopt the document features, NOT HedgeDoc's multi-tenant-platform parts (its own
auth/LDAP, public explore page, 5 media backends, anonymous editing).
Baseline today¶
Note(owner_sub, org_id, title, content, pinned) — owner-scoped RLS CRUD (/notes list/create/
patch/delete + substring search), agent tools (create/list/update/delete), a master-detail
NotesPage.vue (raw textarea ↔ rendered view), renderer = markdown-it + highlight.js + mermaid +
DOMPurify. No editor, sharing, collab, tags, folders, export, or revisions.
Architecture decisions¶
- Editor = Monaco (REUSE the existing
MonacoEditor.vue), NOT a new editor. We already shipmonaco-editor+@guolao/vue-monaco-editorfor coding-mode + dashboard config editors, with a shared theme (monacoHex). Reusing it avoids a SECOND editor in the bundle and keeps one editor stack. It is the foundation for the toolbar, live split-preview, AND the realtime binding. (Rejected CodeMirror 6 — HedgeDoc's choice + marginally nicer for prose — because a second editor dependency isn't worth it;y-monacogives the same Yjs collab.) New deps are only the renderer/collab libs:katex+@vscode/markdown-it-katex,markdown-it-task-lists,markdown-it-footnote,markdown-it-table-of-contents,markdown-it-anchor(done); and lateryjs,y-monaco,y-protocols(Phase 4). - Renderer upgrades go in the SHARED
useMarkdown(KaTeX, task lists, footnotes, TOC, anchors) so the chat renderer benefits too. Keep the strict DOMPurify gate. - Real-time collab = Yjs (CRDT). Frontend:
y-codemirror.nextbinds the CM6 doc to a Yjs doc viay-monaco;y-protocols/awarenesscarries cursors/presence. Transport: a FastAPI WebSocket/ws/notes/{note_id}(reuse theterminal_wstoken-via-subprotocol auth). Server:pycrdt(maintained Python Yjs binding) holds the room's Y.Doc, applies updates, and MATERIALIZES the markdown text →notes.content(so search / RAG / the agent always see live content — the reason to run a real CRDT server, not a blind relay). Cross-pod fan-out via Redis pub/sub per note room (Contract #4 pattern; unlike the single-pod terminal relay). Persistence:notes.ydoc BYTEA(the CRDT state) + materializedcontent, debounced-saved + on last disconnect; each checkpoint also writes a revision. - Sharing = a
note_permissionstable keyed on the unifiedscope_ref(user:<sub>|group:<id>), levelread|write; owner always full. This rides the scope-entity-unification: a note is visible ifownerOR a permission row'sscope_ref ∈ current_scopes. Public read-only links reuse the existing [[chat-sharing]] infra (password + token +/s/...). Group sharing uses the existing group system. - Out of scope (HedgeDoc-platform, not us): anonymous editing, LDAP/OIDC (we have Keycloak), the public explore page, imgur/azure/webdav media backends, more diagram types (per user).
Phases (each independently shippable + deployable)¶
- Phase 1 — Editor + renderer (Tier 1). Monaco (markdown mode, word-wrap) replaces the
textarea via the shared
MonacoEditor.vue; formatting toolbar; live synced split-preview;useMarkdowngains KaTeX + task lists + footnotes + TOC + anchors; export Markdown + print-to-PDF. No backend change. Lowest risk; ships the biggest day-1 UX win. - Phase 2 — Organization (Tier 2).
tags(column + filter + agent auto-tag), YAML frontmatter (title/tags/type parsed from the body), folders (reuseFolder), slide mode (reveal.js whentype: slide). Image paste/upload → anote_mediastore +POST /notes/media(filesystem/S3 via the existing compute/storage seam). - Phase 3 — Sharing (users + groups).
note_permissionsmodel + RLS extension (owner OR permitted scope); share UI (pick users/groups + level); public links via chat-sharing; API + agent tools honour permissions. Memory-access / governance unaffected (notes carry no tags). - Phase 4 — Real-time collaboration. Yjs +
y-monacobinding;/ws/notes/{id}room;pycrdtserver materializing content; Redis cross-pod fan-out; awareness cursors/presence;notes.ydocpersistence. Depends on Phase 1 (Monaco editor) + Phase 3 (who may edit). - Phase 5 — Revision history.
note_revisions(note_id, content, author_sub, created_at); snapshot on save / collab checkpoint; diff + restore UI.
Order rationale: editor (foundation) → organization (additive) → sharing (permission model) → collab (needs editor + permissions) → revisions (needs the save pipeline). [[notes-system]] [[chat-sharing]] [[scope-entity-unification]] [[group-system]] [[mermaid-charts]]