Notes → collaborative documents (HedgeDoc-class)
Status: Design + plan of record. 2026-06-17. Reference clone:
/root/references/hedgedoc.Update 2026-06-23: Phases 1, 3, and 4 SHIPPED; Phase 2 PARTIALLY shipped (tags only); Phase 5 (revisions) NOT yet shipped. See the per-phase status notes below.
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 (pre-work, 2026-06-17)
The starting point this plan built on: 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. (Most of these have since landed - see the phase status above.)
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; andyjs,y-monaco,y-protocols(all shipped). - 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-monaco(MonacoBinding) binds the Monaco model to a Yjs doc;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. !!! note “Shipped (Phase 4)” Built as designed:realtime/notes_collab.py(NoteRoom/NoteCollabHub, pycrdtDocwith acontentText),api/routers/notes_ws.py(/ws/notes/{note_id}, bearer-subprotocol auth, read-only collaborators may send awareness but their doc edits are dropped server-side), frontendcomposables/useNoteCollab.ts(y-monaco+y-protocols/awareness). The wire is a hand-rolled 4-tag binary y-sync protocol (SYNC_STEP1/SYNC_STEP2/UPDATE/AWARENESS), NOT they-websocketprovider. Redis cross-pod fan-out tags each message with a per-process pod id to skip its own echoes; persistence debounce is 2s plus a final flush on last leave. The per-checkpoint revision write was deferred to Phase 5 (not implemented). Migrationnotes_ydoc_01. - 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’sgrantee_scope_ref ∈ current_scopes(resolved inNoteRepo, not RLS). Group sharing uses the existing group system. Public read-only links (planned to reuse the [[chat-sharing]] password+token+/s/...infra) are NOT yet built - notes do not appear in the share routers. - 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). ✅ SHIPPED. Monaco (markdown mode, word-wrap) replaces
the textarea via the shared
MonacoEditor.vue; formatting toolbar (toolbarButtonsdrivingeditor.executeEdits); live synced split-preview;useMarkdowngains KaTeX + task lists + footnotes + TOC + anchors; export Markdown + print-to-PDF (exportMarkdown/printNote). No backend change. Lowest risk; ships the biggest day-1 UX win. - Phase 2 - Organization (Tier 2). PARTIAL. ✅
tagsshipped:notes.tags JSONB(GIN indexix_notes_tags, migrationnotes_tags_01),?tag=filter onGET /notes+ filter chips + per-note add/remove + agent-supplied tags via the note tools (normalize_tags: lowercase/dedupe, cap 30). NOT shipped: YAML frontmatter parsing, folders (Folder), slide mode (reveal.js), image paste/upload (note_mediastore,POST /notes/media). - Phase 3 - Sharing (users + groups). ✅ SHIPPED (except public links).
note_permissionsmodel (grantee_scope_ref=user:<sub>|group:<id>,levelread|write; migrationnote_permissions_01) + access resolution inNoteRepo(accessible/get_readable/get_writable, owner > write > read, owner-only delete); share UI =NoteShareDialog.vue+GET/PUT /notes/{id}/shares+GET /users/search(tenant-scoped picker). API + agent tools honour permissions. Memory-access / governance unaffected (notes carry no tags). NOT shipped: public read-only links via chat-sharing (notes are not inpublic_shares/shares). - Phase 4 - Real-time collaboration. ✅ SHIPPED. See the architecture-decision #3 note above for the as-built detail.
- Phase 5 - Revision history. NOT yet shipped. Planned:
note_revisions(note_id, content, author_sub, created_at); snapshot on save / collab checkpoint; diff + restore UI. Nonote_revisionstable or UI exists today.
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]]