Baalda has one genuinely hard problem: the same note is a file on your disk and a CRDT document being edited live by other people, and both have to stay true at once. Most of the architecture exists to serve that.
The four pillars
- Desktop app. The Tauri 2 shell, React UI, CodeMirror 6 editor, file tree, and the Rust command surface that owns disk I/O and the file watcher.
- Database architecture. The on-disk vault layout, the local SQLite index for search, backlinks and tags, local CRDT persistence, and the server's Postgres and binary Yjs stores.
- Sync engine. Yjs, the Hocuspocus server, the sync protocol, and the file-to-CRDT bridge algorithm with its loop avoidance.
- Team collaboration. Better Auth accounts and organizations, the folder ACL, how permissions gate which documents a client may sync, and presence.
Two join keys hold it together
Every pillar shares the same two.
doc_id. A stable per-note identity. The .md file, the Yjs document, the SQLite index row and the Postgres row all reference the same doc_id. Everything is keyed by doc_id and never by path, so a rename or a move never forks a note.- The session. Better Auth issues a session, the session mints short-lived per-document sync tokens, and Hocuspocus enforces them. Identity and access flow through that one chokepoint.
The full specification set covers the desktop app, the database architecture, the sync engine, team collaboration and the vault sync engine as separate documents.
This page is written from docs/specs/00-architecture-overview.md in the repository. That document goes further, and it is the one that changes first.
