Architecture Overview
zudo-text is cloud-primary: end-to-end encrypted cloud storage is the only place managed user content lives. Workspace documents form the decrypted in-memory model, while attachments use a parallel encrypted asset surface. Every client — desktop, iOS, browser — uses those same cloud sources. The local workspace directory, the workspace registry, and the terminal/PTY subsystem were all retired together in epic #4204. The confirmed contract for that model is Cloud-Primary Storage — read it before designing a new feature or changing an existing subsystem.
High-Level Diagram
┌─────────────────────────────────────────────────────┐
│ Tauri WebView │
│ │
│ React App (renderer/) │
│ ┌───────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ CodeMirror│ │ Frameset │ │ Command Palette │ │
│ │ Editor │ │ + Views │ │ Settings Dialog │ │
│ └─────┬─────┘ └────┬─────┘ └────────┬─────────┘ │
│ │ │ │ │
│ └──────────────┴─────────────────┘ │
│ │ │
│ @takazudo/backend-bridge │
│ ┌───────────────────────┬───────────────────────┐ │
│ │ workspaceFiles/assets │ bridge.files / dialog │ │
│ │ (encrypted cloud data)│ (EFE local-only) │ │
│ └───────────┬───────────┴───────────┬───────────┘ │
└───────────────┼───────────────────────┼─────────────┘
│ HTTPS + WebSocket │ Tauri invoke / events
┌─────────────┼──────────┐ ┌─────────┼───────────────┐
│ Sync server (Workers) │ │ Rust Core │
│ D1 + R2 + SyncRoom DO │ │ │
│ ciphertext (see below)│ │ Local file I/O │
└────────────────────────┘ │ watchers, native menus │
│ window mgmt, generator │
└─────────────────────────┘ The split down the middle is the thing to internalize: bridge.workspaceFiles is where workspace documents live, and it speaks workspace-relative POSIX paths to an encrypted store the server cannot read. bridge.assets is a parallel encrypted cloud surface for binary attachments. bridge.files is a separate, deliberately local-only surface used by the feature that reaches outside the workspace — the External File Editor. These namespaces address different storage and must not be mixed.
Doc Cloud is a third, explicitly external namespace: bridge.docCloud talks to zudo-doc-cloud authoring APIs and never enters the encrypted workspace model or the local-file bridge. Its entire UI lives in one core.doc-cloud frame; see Doc Cloud Unified Frame.
The External File Editor is local-only: its provider is registered through the shared caps.localFiles / hasUsableLocalFiles gate. Cloud/browser capabilities therefore do not register these local-file surfaces.
Identity and service credentials are provided by Better Auth: clients retain a sliding session and mint short-lived RS256 JWTs accepted by sync, publish, and notifications.
Uploaded assets are end-to-end encrypted: cloud storage receives only a deterministically encrypted filename token and an authenticated encrypted byte envelope. The public bridge still gives renderer code plaintext filenames and base64 bytes, and derives MIME locally after decryption. Assets intentionally do not enter the workspace-file model, outbox, or local workspace mirror; see D10 in Cloud-Primary Storage.
Local coding agents reach the same cloud-primary model through a deliberately narrow stdio MCP surface. Local Agent Authoring documents the one-process/one-workspace boundary, scoped PATs, document key sessions, locally encrypted asset transfers, history/undo model, and the line between direct tools and the cloud agent. It does not add an in-app terminal or a local workspace directory.
Why Tauri?
zudotext was migrated from Electron to Tauri v2 for several reasons:
Binary size: ~5 MB (Tauri) vs ~260 MB (Electron + Node.js)
No Node.js runtime: backend operations run in native Rust — local file I/O, file watchers, native menus and windows
Security: Tauri's capability-based permission model restricts what the frontend can access
Performance: native Rust for file operations, no IPC serialization overhead for heavy workloads
Rust backend
The Rust backend (tauri-app/src/) handles system-level operations. Note how much smaller its remit is than it once was: it no longer owns user content at all.
| Module | Responsibility | Key Crate |
|---|---|---|
commands/ | Local file read/write and skills dirs | std::fs |
commands/ | Local .zudotext.settings.json read/write with mtime caching (pre-unlock fallback only) | serde_json |
commands/ | Per-instance workspace binding — config.json v2 read/persist/clear | serde_json |
commands/ | Local file change detection and event emission | notify |
commands/ | System font enumeration with monospace detection | font-kit |
commands/ | Window opacity, print, frame pop-out / dock-back | objc2-app-kit |
commands/ | Set OS window title bar text | Tauri window API |
commands/ | No-compile LEAF assembly, workspace→LEAF lookup | std::fs |
commands/ | Per-machine device name (local identity) | std::fs |
commands/ | macOS Spotlight file/directory search (mdfind) | macOS-only |
commands/ | User theme files under the per-app config dir | std::fs |
commands/ | Native notification send-test | tauri-plugin-notification |
commands/ | Allowlisted packaged Doc Cloud HTTP/SSE transport (desktop only) | reqwest |
commands/ | Runtime ROOT/LEAF role resolution | zudotext-core |
native/ | Main and splash window creation | Tauri window API |
native/ | macOS application menu | Tauri menu API |
native/ | Native file/directory picker | tauri-plugin-dialog |
state.rs | Central AppState (watchers, settings cache, file-search handles, SSE) | std::sync::Mutex |
Modules that used to be here and are gone: commands/ and the portable-pty dependency, commands/ and the workspace registry, commands/, commands/, and the Rust BM25 similarity engine (ported to TypeScript so it runs on the workspace model in every client).
tauri-app/core/ is a separate zudotext-core crate mirroring the business logic without Tauri dependencies, so cargo test runs anywhere — including WSL2 and CI hosts with no GTK.
Frontend Architecture
The React frontend (tauri-app/renderer/) is structured as:
Boot gate:
bootstrap/— auth, workspace resolution, genesis/picker/unlock, thenapp- boot. tsx <App>. Shared by desktop, iOS, and browser builds.Pages:
write-page,archives-page,tags-page,frameset-host-pageLayout: a singleton frameset tree of splittable frames, each hosting a registered view provider; header pins install whole layouts
Shared packages: UI components, command palette, shortcut engine, color themes, board packages, the backend bridge
State: React Context for settings and sync, the in-memory workspace model for content
The frontend bootstraps via renderer/ → renderer/, which calls initBackend(createTauriAdapter()) and mounts the boot gate. See App Lifecycle for the full sequence.
Adapters
One renderer runs against three backends, chosen at bootstrap:
| Adapter | Used by | Content source |
|---|---|---|
| Tauri | desktop + iOS builds | workspace core over IPC + HTTPS |
| REST | pnpm dev:rest, the browser build | workspace core over HTTPS (or the axum dev server when no workspace is armed) |
| Mock | pnpm dev:mock, Storybook, CI e2e | in-memory fixtures |
See Backend Bridge for the namespace list and the adapter contract.