# Architecture

> The four pillars, the two join keys, the three stores on a client, and the specification set behind each.

Source: https://baalda.com/docs/architecture
Site: Baalda, https://baalda.com

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.** A Tauri 2 shell with a React UI, the CodeMirror 6 editor, a virtualized file tree, and a Rust command surface that owns disk access and the watcher. The UI never touches the disk itself.
- **Database architecture.** The on-disk vault layout, the local SQLite index, local CRDT persistence, and the server's Postgres and binary document stores.
- **Sync engine.** Yjs, the Hocuspocus server, the sync protocol, and the file-to-CRDT bridge with its loop avoidance.
- **Team collaboration.** Accounts and organizations, the folder ACL, how permissions gate which documents a client may open, and presence.

## Two join keys

- **`doc_id`.** The file, the Yjs document, the local index row and the server row all reference the same id. Nothing is keyed by path, which is why a rename or a move never forks a note.
- **The session.** It mints short-lived per-document sync tokens, and the editing server enforces them. Identity and access flow through that one chokepoint.

## Three stores on every client

| Store | Technology | Holds | Rebuildable |
|---|---|---|---|
| Vault files | `.md` and attachments on disk | The notes themselves: what you, your AI and `git` touch | It is the truth |
| Local index | SQLite | Search, backlinks, tags, the folder tree, the id to path map, sync cursors | Yes, from the files |
| CRDT store | Yjs updates in SQLite | Live document state and the offline edit log | Yes, from the files or the server |

## What the local index holds

| Table | Purpose |
|---|---|
| `notes` | The id to path map, with title, mtime, content hash and parsed frontmatter |
| `notes_fts` | FTS5 full-text search over title and body |
| `tags`, `note_tags` | Tags from inline `#tags` and the `tags:` property |
| `links` | One row per wikilink, with the resolved target or null when it dangles |
| `folders` | The folder tree, nested by parent |
| `yjs_updates`, `yjs_snapshot` | Local CRDT history and its periodic compaction |

On a rename, link targets are updated by id and never by path. That single rule is why moving a note cannot break the links pointing at it.

## On the server

- A binary Yjs store holding opaque document updates and periodic snapshots. Your Markdown never travels the wire in plain text.
- A relational store for identity, organizations, members, permissions, the registry and attachment metadata.
- Attachment bytes, in the database or in S3-compatible storage.

## The specifications

| Document | Covers |
|---|---|
| `REQUIREMENTS` | The 12 core requirements and supporting features, each mapped to a spec and a phase |
| `00-architecture-overview` | How the pillars fit, the bridge, the data-flow diagram, the principles |
| `01-desktop-app` | The Tauri shell, the Rust command surface, the editor and the file tree |
| `02-database-architecture` | Vault layout, the index schema, the server's stores |
| `03-sync-engine` | The sync protocol, the bridge algorithm and loop avoidance |
| `04-team-collaboration` | The organization model, the ACL, how permissions gate sync, presence |
| `05-vault-sync-engine` | The always-on vault channel, the stateless relay and pluggable fanout |
