Backend API
Overview
zudotext uses Tauri v2 for native OS integration, with no Node.js main process. The cloud workspace is accessed through TypeScript bridge domains. Rust commands cover native concerns such as windows, device-local configuration, file pickers, and the deliberately local External File Editor surface.
The backend exposes functionality to the frontend through two mechanisms:
Commands — synchronous or async request/response calls (like RPC)
Events — backend-to-frontend push notifications (like pub/sub)
How the Frontend Calls the Backend
Commands via invoke()
The frontend calls Rust functions using Tauri's invoke(). The @takazudo/backend-bridge package abstracts this so that production code uses real Tauri calls while tests and Storybook use an in-memory mock adapter.
import { invoke } from '@tauri-apps/api/core';
// Read an explicitly local file (External File Editor)
const content = await invoke<string>('files_read_text', {
path: '/Users/example/notes/local.md',
});Events via listen()
The backend pushes real-time notifications to the frontend using Tauri's event system. The frontend subscribes with listen().
import { listen } from '@tauri-apps/api/event';
// Listen for a file opened in External File Editor changing on disk
const unlisten = await listen<{ path: string }>('files:externalChange', (event) => {
console.log('File changed:', event.payload.path);
});Command Naming Convention
All Tauri commands use snake_case naming, grouped by domain:
| Prefix | Domain | Example commands |
|---|---|---|
files_ / local_dir_ | Explicitly local file surfaces | files_read_text, files_write_text, files_delete_file, local_dir_list_files |
assets_ / download_sink_ | Native asset import/export helpers | assets_download_file, assets_download_many, download_sink_open |
settings_ | App settings | settings_get, settings_save |
app_binding_ | Per-app cloud workspace binding | app_binding_read, app_binding_persist, app_binding_clear |
device_ | Device identity | device_get_name, device_set_name, device_clear_name |
generator_ | Child-app generation | generator_scaffold, generator_assemble_child, generator_open_app, generator_find_leaf_for_workspace |
file_search_ | Spotlight file search | file_search_start, file_search_cancel, file_search_is_supported |
skills_ | Inline-AI skill files | skills_list_dir, skills_watch_dir, skills_unwatch_dir |
fonts_ | System fonts | fonts_list |
frame_ | Window/popout frames | frame_pop_out, frame_dock, frame_popout_emit_event |
set_window_ | Window properties | set_window_opacity, set_window_title |
print_ | Printing | print_webview |
reveal_ | File manager | reveal_directory |
get_ | Local environment getters | get_home_dir, get_user_skills_dir |
app_mode_ | App mode (ROOT/LEAF) | app_mode_get |
fs_ | Filesystem helpers | fs_mkdir |
Application State
All commands share a central AppState managed by Tauri's dependency injection:
pub struct AppState {
pub file_searches: Mutex<HashMap<String, MdfindChild>>,
pub watchers: Mutex<WatcherState>,
pub app_config_dir: PathBuf,
pub settings_cache: Mutex<Option<serde_json::Value>>,
pub settings_mtime: Mutex<u64>,
pub event_tx: broadcast::Sender<SseEvent>,
}app_config_dir— immutable path to the app's config directory (e.g.~/.config/zudotext/<appname>/); not wrapped in aMutexbecause it never changes after initializationfile_searches— activemdfindsubprocess handles keyed by search ID (Spotlight integration)watchers— file watcher instances and content/mtime tracking for self-write detectionsettings_cache/settings_mtime— cached settings with mtime-based invalidationevent_tx— broadcast channel for SSE events (REST adapter)
Native state is synchronized with narrow mutexes; commands avoid holding unrelated locks across I/O.
Page Index
| Page | Domains covered |
|---|---|
| Messages | Compatibility message domain; cloud-backed in production, local only in the development REST fallback |
| Pins | Compatibility pin domain over workspace documents |
| Inbox | Compatibility Note Tray and active-selection domains |
| Assets | Workspace Assets API plus native import/export helpers |
| Settings and Workspace Binding | settings_*, app_binding_*, fonts_list, misc getters |
| Watchers | Development REST fallback, user-skills, and External File Editor watchers |
| Helper Modules | Internal Rust helpers (not Tauri commands) |
| Sync and Auth | Cloud sync bridge (bridge.sync, bridge.auth) |
| Sync Server API | Sync Worker REST routes and PAT server routes |
| Generator | generator_* — LEAF-app assembly pipeline |
| Device Identity | device_* — per-machine device name |
| File Search and Similarity | file_search_* (Spotlight) plus bridge.similarDocs — the TypeScript BM25 index over the workspace model (no Tauri commands) |
| Frame Pop-Out | frame_pop_out, frame_dock, frame_popout_emit_event, bridge.framePopout restore protocol |
| Publish and API Tokens | bridge.publish (publish Worker), bridge.apiTokens (PAT management) |
| Automation API | PAT-authenticated, key-session-gated workspace automation routes |
Internal / Unlisted Commands
The following Tauri commands are registered in generate_handler! but are not documented on dedicated pages. They are internal utilities, UI conveniences, or native OS dialogs without a significant contract to specify:
| Command | Description |
|---|---|
app_mode_get | Returns "root" or "leaf" — consumed internally at startup; documented in architecture docs |
files_read_text | Low-level read of a LOCAL file by absolute path (External File Editor); workspace content goes through bridge.workspaceFiles |
files_write_text | Low-level write to a LOCAL file by absolute path; workspace content goes through bridge.workspaceFiles |
files_delete_file | Delete a file within allowed roots (validate_path_in_roots guard); see Helper Modules |
fs_mkdir | Create a directory within allowed roots; see Helper Modules |
get_user_skills_dir | Return the user-scoped skills config directory path |
skills_list_dir | List .md files in the skills directory; used by the inline-command skill loader |
set_window_title | Set the native window title string |
set_window_opacity | Set window opacity (0.0–1.0); documented as a UI convenience on the Settings and Workspace Binding page |
print_webview | Trigger the system print dialog for the WebView; documented on the Settings and Workspace Binding page |
open_directory | Open a directory via the native OS file picker dialog |
dialog_create_directory | Prompt the user to create a new directory via a native dialog |
Event Summary
| Event | Payload | Source |
|---|---|---|
messages:changed | { filename: string } | Development REST fallback watcher |
notes:changed | { dir: string } | Development REST fallback watcher |
draft:externalChange | { draftNumber: number } | Development REST fallback watcher |
files:externalChange | { path: string } | External File Editor watcher |
skills:changed | { events: Array<{ path: string, kind: string }> } | Skills directory watcher |
generator:assemble-progress | AssembleProgress | Generator pipeline step |
frame:popout-closed | { frameId: string, windowLabel: string } | Pop-out window destroy handler |
file-search:{id}:hit | { path: string, kind: string } | Spotlight search hit |
file-search:{id}:complete | (none) | Spotlight search completed |
file-search:{id}:error | { message: string } | Spotlight search failed |
Security
Path traversal prevention — local file operations use purpose-specific containment and canonicalization guards such as
validate_path_in_roots; workspace paths are validated by the cloud workspace layerInput validation — settings must be JSON objects, inbox active draft numbers must be 1–99
Filename sanitization — asset filenames are stripped of path components to prevent traversal