Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

AppFlowy Administrator & Developer Guide

Generated May 2026 from AppFlowy-IO/AppFlowy at 4af02cd

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

AppFlowy is an open-source alternative to Notion, delivering a cross-platform productivity suite across macOS, Windows, Linux, iOS, Android, and Web. The application follows a local-first architecture with optional cloud synchronization, built on a Flutter (Dart) frontend communicating with a Rust backend through FFI and Protobuf-encoded events. This guide covers the complete internal architecture, every Rust crate, the Flutter layer, the build system, collaboration engine, and operational concerns at the depth required to maintain, extend, or replicate the system.

Note

This guide documents AppFlowy version 0.11.4 at commit 4af02cd (2026-03-20). All code paths, struct names, event enums, and configuration keys are verified against that specific revision. See the User Guide for installation instructions, everyday usage, and feature walkthroughs.


1. Architecture Overview

1.1 Layered System Design

AppFlowy uses a strict two-layer architecture that separates UI rendering from business logic:

┌─────────────────────────────────────────────────────────┐
│                    Flutter UI Layer                      │
│  (Dart: widgets, BLoCs, navigation, theming, plugins)   │
├─────────────────────────────────────────────────────────┤
│              dart-ffi Bridge (Protobuf)                  │
│  AppFlowyDartConfiguration / AppFlowyYamlConfiguration   │
├─────────────────────────────────────────────────────────┤
│                   Rust Core Layer                        │
│  ┌─────────────┐  ┌──────────────┐  ┌────────────────┐ │
│  │  flowy-core  │  │  flowy-user   │  │  flowy-folder  │ │
│  │ (orchestrator)│  │ (auth/profile)│  │ (views/spaces) │ │
│  └──────┬───────┘  └──────────────┘  └────────────────┘ │
│  ┌──────┴───────┐  ┌──────────────┐  ┌────────────────┐ │
│  │flowy-document │  │flowy-database2│  │   flowy-ai    │ │
│  │ (block docs)  │  │(grid/board/cal)│  │(chat/complete)│ │
│  └──────────────┘  └──────────────┘  └────────────────┘ │
│  ┌──────────────┐  ┌──────────────┐  ┌────────────────┐ │
│  │ flowy-search  │  │flowy-storage │  │  flowy-server  │ │
│  │  (Tantivy)    │  │ (file upload) │  │(cloud abstraction)│
│  └──────────────┘  └──────────────┘  └────────────────┘ │
│  ┌──────────────┐  ┌──────────────┐  ┌────────────────┐ │
│  │collab-integrate│ │ lib-dispatch │  │   lib-infra    │ │
│  │(CRDT builder) │  │(event system)│  │  (utilities)   │ │
│  └──────────────┘  └──────────────┘  └────────────────┘ │
│  ┌──────────────┐  ┌──────────────┐                     │
│  │  lib-log      │  │  build-tool  │                     │
│  │  (logging)    │  │  (codegen)   │                     │
│  └──────────────┘  └──────────────┘                     │
└─────────────────────────────────────────────────────────┘

The Flutter layer handles all rendering, user interaction, and platform-specific concerns. The Rust layer handles all data operations, persistence, collaboration, synchronization, search indexing, and AI integration. Communication between them is exclusively through the lib-dispatch event system using Protobuf-serialized messages over FFI.

1.2 Core Design Principles

Principle Implementation
Local-first All data stored locally in SQLite + CollabDB before any cloud sync
CRDT-based collaboration The collab crate family provides conflict-free replicated data types for all document and database operations
Event-driven architecture lib-dispatch routes typed events between Dart and Rust, with each domain crate registering its own event handlers
Plugin-based extensibility Both the Flutter and Rust layers use plugin systems for registering new view types and capabilities
Offline-capable Full functionality without network; cloud sync is additive
Type-safe FFI Protobuf schemas define the contract between Dart and Rust, with code generation ensuring compile-time safety

1.3 Data Flow Architecture

User Action (Flutter Widget)
    │
    ▼
BLoC / Application Layer (Dart)
    │
    ▼
Event Dispatch (Dart → FFI → Rust)
    │  Protobuf-encoded AFPluginEventRequest
    ▼
lib-dispatch Router (Rust)
    │  Matches event ID → handler function
    ▼
Domain Handler (e.g., flowy-folder event_handler.rs)
    │
    ├──▶ Local Storage (SQLite / CollabDB)
    │
    ├──▶ Collab Object (CRDT operations)
    │       │
    │       └──▶ Cloud Sync (WebSocket → AppFlowy Cloud)
    │
    └──▶ Response (Protobuf-encoded AFPluginEventResponse)
            │
            ▼
        Flutter Callback / Stream

1.4 Application Lifecycle

The AppFlowyCore struct in flowy-core/src is the main orchestrator. It holds Arc references to every manager and initializes them in a specific order. The AppLifeCycleImpl struct implements the AppLifeCycle trait from flowy-user, responding to authentication events:

On launch (already authenticated):

  1. ServerProvider — configure sync and encryption from UserCloudConfig
  2. FolderManager.initialize() — load workspace/view tree from local disk
  3. DatabaseManager.initialize() — prepare database editor cache
  4. DocumentManager.initialize() — register document handlers
  5. FullIndexedDataProvider — start background indexing
  6. InstantIndexedDataProvider — start live indexing
  7. SearchManager — create Tantivy state, initialize search index
  8. ServerProvider.on_launch_if_authenticated() — establish cloud connection
  9. AIManager.on_launch_if_authenticated() — initialize AI features

On sign in: Same sequence with initialize_after_sign_in() variants that may fetch folder data from the cloud if not present on disk. The folder_init_data_source() method checks whether collab data exists locally via CollabKVAction::is_exist() before falling back to cloud fetch.

On sign out: All managers are deinitialized. The Tantivy state is closed via close_document_tantivy_state(). Local data is preserved for the next sign-in.

On workspace switch: The open_workspace lifecycle event reinitializes all managers for the new workspace context. Database and document managers get fresh initialization; the search index is rebuilt for the new workspace.

Important

Manager initialization order matters. FolderManager must initialize before DatabaseManager because database views are children of folder views. SearchManager requires the Tantivy state created during folder initialization. Changing this order will cause runtime panics from None unwraps on uninitialized Weak references.


2. System Requirements

2.1 Build Environment

Component Requirement
Flutter SDK Stable channel (version compatible with Dart 3.x)
Rust toolchain Stable (with nightly for some codegen features)
cargo-make Required for the build system (cargo install cargo-make)
Protobuf compiler protoc must be on PATH for code generation
Python 3 Used by some build scripts
Git For dependency fetching and version stamping

2.2 Platform-Specific Requirements

macOS:

Item Value
Deployment target macOS 11.0 (Big Sur) minimum
Xcode Latest stable recommended
Architecture Both arm64 (Apple Silicon) and x86_64 supported

Windows:

Item Value
Architecture x86_64 only (ARM64 not yet supported — see feature request #6403)
Visual Studio Build Tools with C++ workload
CMake Required for native library compilation

Linux:

Item Value
Architecture x86_64 and aarch64
Shared libraries libvdpau.so.1 may be required (issue #8655)
Display server X11 or Wayland

Android:

Item Value
compileSdk 35
minSdk 29 (Android 10)
targetSdk 35
NDK version 24.0.8215888
ABI filters arm64-v8a only
CMake Required for native libs (CMakeLists.txt in android/app/src/main/)

iOS:

Item Value
Deployment target Defined in Xcode project
CI Codemagic with mac_mini_m2 instances

Web:

Item Value
Build target Flutter Web with custom index.html
Rust compilation Via wasm-pack or wasm-bindgen (Tauri integration available)

2.3 Runtime Requirements

Component Purpose
SQLite Local data persistence (via flowy-sqlite with WAL mode)
Tantivy Full-text search indexing (embedded, no external dependency)
Ollama (optional) Local AI model inference
AppFlowy Cloud (optional) Sync, collaboration, cloud storage

Tip

AppFlowy operates fully offline. Cloud sync, AI features, and online collaboration are all opt-in. A clean local installation requires only the application binary and sufficient disk space for SQLite databases and the Tantivy search index.


3. Configuration Reference

3.1 Application Configuration (AppFlowyCoreConfig)

The core configuration struct is defined in flowy-core/src and passed during application startup:

Field Type Purpose
name String Application name (default: "AppFlowy")
device_id String Unique device identifier for sync differentiation
cloud_config Option<AFCloudConfiguration> Cloud connection parameters
app_version String Application version string ("0.11.4")

3.2 Dart Configuration (AppFlowyDartConfiguration)

Defined in dart-ffi/src, this struct serializes the Dart-side environment config into the Rust layer via FFI:

Field Purpose
root Application data root directory
app_version Version string
custom_app_path Optional custom application path
origin_app_path Original application path
device_id Device identifier
cloud_type Server type enum value
authenticator_type Authentication provider type

The AppFlowyYamlConfiguration variant provides YAML-based configuration for environments that prefer file-based config over programmatic initialization.

3.3 Environment Variables

Variable Default Purpose
RUST_LOG "info" Rust logging level (standard tracing filter syntax: error, warn, info, debug, trace; supports per-module filters like flowy_folder=debug,flowy_user=info)
DISABLE_EVENT_LOG unset When set, suppresses event dispatch logging
APP_VERSION "0.11.4" Application version override

3.4 Cloud Configuration

Cloud settings are managed through UserCloudConfig in flowy-user-pub:

Setting Purpose
enable_sync Master toggle for cloud synchronization
enable_encrypt Enable end-to-end encryption for collab data
encrypt_secret Encryption secret for E2E encrypted workspaces

These are persisted per-user and loaded during the authentication lifecycle.

3.5 Testing Environment Variables

Variable Purpose
af_cloud_test_base_url AppFlowy Cloud base URL for integration tests
af_cloud_test_ws_url WebSocket URL for test cloud connections
af_cloud_test_gotrue_url GoTrue authentication URL for tests

3.6 Flutter SharedPreferences Keys

The kv_keys.dart file in lib/core/config/ defines all persistent preference keys used by the Flutter layer. The KeyValueStorage interface in kv.dart wraps SharedPreferences for cross-platform key-value storage. These preferences control UI state, theme selection, sidebar width, last-opened view, notification settings, and feature flags.


4. Deployment & Scaling

4.1 Build System

AppFlowy uses cargo-make (Makefile.toml) as its primary build orchestration tool. The build system spans Rust compilation, Flutter builds, Protobuf code generation, and platform-specific packaging.

Build Profiles

Profile Platform Use Case
development-mac-arm64 macOS Apple Silicon Local development
development-mac-x86_64 macOS Intel Local development
production-mac-arm64 macOS Apple Silicon Release builds
development-windows-x86 Windows x64 Local development
production-windows-x86 Windows x64 Release builds
development-linux-x86_64 Linux x64 Local development
production-linux-x86_64 Linux x64 Release builds
development-linux-aarch64 Linux ARM64 Local development
production-linux-aarch64 Linux ARM64 Release builds

Build Variables

Variable Value Purpose
APPFLOWY_VERSION "0.11.4" Version stamped into binaries
PRODUCT_NAME "AppFlowy" Product identifier
MACOSX_DEPLOYMENT_TARGET "11.0" Minimum macOS version
CARGO_MAKE_CRATE_FS_NAME "dart_ffi" FFI crate name for build targets

Included Build Scripts

The scripts/makefile/ directory contains modular build definitions:

Script Purpose
desktop.toml Desktop platform build tasks
mobile.toml iOS and Android build tasks
protobuf.toml Protobuf compilation and Dart/Rust codegen
tests.toml Test runner configurations
docker.toml Docker-based build environments
env.toml Environment setup and validation
flutter.toml Flutter-specific build tasks
tool.toml Utility build tasks
tauri.toml Tauri (desktop web) build tasks
web.toml Web platform build tasks

4.2 Build Pipeline

A typical development build follows this sequence:

cargo make --profile development-mac-arm64 appflowy-dev

This triggers:

  1. Environment validation — checks for Flutter SDK, Rust toolchain, Protobuf compiler
  2. Protobuf generation — compiles .proto files, generates Dart and Rust bindings
  3. Rust compilation — builds dart_ffi dynamic library (.dylib / .dll / .so)
  4. Flutter build — compiles Dart code, links native library
  5. Asset bundling — fonts, icons, templates, translations

4.3 Protobuf Code Generation

The build-tool directory contains three crates that handle code generation:

Crate Purpose
flowy-ast AST extraction from Rust structs (derives, attributes)
flowy-codegen Protobuf file generation from Rust type definitions
flowy-derive Procedural macros for ProtoBuf, ProtoBuf_Enum derives

The generation flow:

  1. Rust structs annotated with #[derive(ProtoBuf)] are parsed by flowy-ast
  2. flowy-codegen generates .proto files from the AST
  3. protoc compiles .proto to Dart and Rust bindings
  4. Event maps are generated linking Dart event classes to Rust handler functions

Warning

After modifying any Protobuf-annotated struct, you must re-run the code generation step. Failing to do so causes deserialization errors at runtime because the Dart and Rust sides will have mismatched schemas. The error manifests as ProtobufError in the dispatch layer.

4.4 Docker Deployment

Docker build files are available in frontend/scripts/docker-buildfiles/ for containerized builds. These are primarily used for CI/CD pipelines and reproducible release builds.

4.5 CI/CD

Platform Configuration
Codemagic iOS workflow with mac_mini_m2 instances
Commit lint Conventional commits enforced: build, chore, ci, docs, feat, feature, fix, refactor, style, test

4.6 Android Signing

The Android build supports release signing via key.properties:

keyAlias=<alias>
keyPassword=<password>
storeFile=<path-to-keystore>
storePassword=<password>

Place this file at frontend/appflowy_flutter/android/key.properties. The current release build configuration falls back to debug signing when key.properties is absent.


5. Operations

5.1 Data Storage Layout

AppFlowy stores all user data locally. The storage hierarchy per user:

<app_data_root>/
├── <user_id>/
│   ├── collab_db/           # RocksDB-based CollabDB (CRDT state)
│   ├── sqlite/              # SQLite databases
│   │   ├── flowy-user.db    # User profile, sessions, workspace list
│   │   ├── flowy-folder.db  # Workspace structure, view metadata
│   │   └── flowy-chat.db    # AI chat message history
│   ├── cache/               # File upload cache, temp storage
│   └── search_index/        # Tantivy index files per workspace
└── config/
    └── device_id            # Persistent device identifier

5.2 SQLite Configuration

The flowy-sqlite crate manages SQLite connections with these settings:

Setting Value Purpose
Journal mode WAL (Write-Ahead Logging) Concurrent reads during writes
Foreign keys Enabled Referential integrity
Schema versioning Migration-based schema_version tracked per database
Connection pool Single-writer, multiple-reader Thread safety via Rust's ownership

The flowy-sqlite-vec crate extends SQLite with vector operations for AI embedding storage, using the sqlite-vec extension with its own migration system (migrations/001-init/).

5.3 User Migrations

The flowy-user/src/migrations/ directory contains 9 sequential migration files that handle schema evolution across AppFlowy versions. Migrations run automatically on startup when a version mismatch is detected. Each migration is a Rust module implementing a migration trait with up() and down() methods.

Caution

User migrations execute during the authentication lifecycle, before managers are fully initialized. A failed migration leaves the user in an inconsistent state. The migration system does not support partial rollbacks — if a migration fails mid-execution, manual database repair may be required. Always back up the collab_db/ and sqlite/ directories before upgrading to a new AppFlowy version in development.

5.4 Backup and Recovery

AppFlowy's local-first design means the user's data directory is the single source of truth for offline data. To back up:

  1. Close AppFlowy to ensure SQLite WAL is checkpointed
  2. Copy the entire <user_id>/ directory
  3. For cloud-synced workspaces, the cloud retains a server-side copy of all collab objects

Recovery: replace the <user_id>/ directory with the backup copy and restart AppFlowy. The application will reconcile local state with the cloud on next sync (if enabled).

5.5 Job Queue and Background Tasks

The Rust layer uses AFPluginRuntime (a Tokio runtime wrapper) for async task execution. Background tasks include:

Task Owner Behavior
Full indexed data provider AppLifeCycleImpl Background scan of all workspace content for search indexing
Instant indexed data provider AppLifeCycleImpl Real-time indexing of content changes
File upload queue StorageManager Chunked file uploads with retry logic
Cloud sync ServerProvider WebSocket-based real-time sync
AI model management LocalAIController Ollama process management

5.6 Logging

Logging uses the Rust tracing crate with the lib-log wrapper:

  • Log level controlled by RUST_LOG environment variable
  • Event dispatch logging can be disabled via DISABLE_EVENT_LOG
  • Each major operation is instrumented with #[instrument(skip_all)] for span-based tracing
  • Flutter-side logging uses standard Dart logging mechanisms

5.7 Health Monitoring

There is no built-in health endpoint for the desktop application. Cloud-connected instances report health through the client_api connection status. Monitor these signals:

  • WebSocket connection state (connected, disconnected, reconnecting)
  • Sync queue depth (pending collab updates)
  • File upload queue length
  • Search index freshness (last indexed timestamp vs. last modification)

6. Performance

6.1 Memory Architecture

AppFlowy uses Rust's ownership model to control memory lifecycle:

  • Weak references (Weak<T>) between managers prevent circular reference leaks. AppLifeCycleImpl holds Weak pointers to all managers — the Arc owners live in AppFlowyCore.
  • Collab objects are opened on demand and cached in manager-level DashMap or HashMap caches. Closing a document/database drops the collab object and frees its CRDT state.
  • Search index (Tantivy) uses memory-mapped files for the index segments, keeping memory usage proportional to index size rather than total document count.
  • File upload cache stores chunks in a temporary directory that is cleaned after successful upload.

6.2 Concurrency Model

Layer Model
Flutter UI Single-threaded event loop (Dart isolate)
FFI bridge Synchronous call from Dart, dispatched to Tokio runtime on Rust side
Rust event handlers Async (tokio) with AFPluginRuntime managing the thread pool
SQLite Single-writer via flowy-sqlite connection management
CollabDB RocksDB with column families, concurrent reads supported
Search indexing Background Tokio task with channel-based communication

6.3 CRDT Performance Characteristics

The collab crate uses Yjs-compatible CRDTs (via the yrs Rust library). Performance implications:

Operation Complexity Notes
Local insert O(1) amortized Appends to the operation log
Remote merge O(n) where n = pending ops Processes all pending remote operations
Snapshot creation O(document size) Full state serialization for persistence
History traversal O(history length) Undo/redo walks the operation log

For large documents (>10,000 blocks) or databases (>10,000 rows), CRDT merge operations can cause noticeable latency during sync. The collab layer batches operations to mitigate this.

6.4 Search Performance

Tantivy provides sub-millisecond search for workspaces with typical content volumes (hundreds to low thousands of documents). The search manager maintains per-workspace indexes:

  • Index creation: happens once per workspace on first access
  • Incremental updates: document changes trigger index updates via the indexed data providers
  • Memory: uses mmap, so OS page cache manages memory efficiently
  • Disk: index size is typically 10-30% of the raw text content size

6.5 Network Optimization

Cloud sync uses WebSocket for real-time collab updates with these optimizations:

  • Delta encoding: only changed operations are transmitted, not full document state
  • Batched updates: rapid local edits are batched before transmission
  • Compression: collab updates are compressed before network transfer
  • Reconnection: automatic reconnection with exponential backoff on disconnect

7. Security

7.1 Authentication Architecture

AppFlowy supports multiple authentication backends through the AuthService trait pattern:

Backend Implementation Location
AppFlowy Cloud CloudAuthService lib/user/application/auth/
Backend BackendAuthService Direct server authentication
Mock MockAuthService Testing only

The Rust-side authentication lives in flowy-user, handling:

  • sign_in_with_email_password — email/password authentication
  • sign_up — new account creation
  • sign_out — session termination and manager cleanup
  • delete_account — permanent account removal
  • get_user_profile / update_user_profile — profile CRUD

User sessions are persisted locally in SQLite. The UserManager maintains the current session state and triggers lifecycle events on authentication state changes.

7.2 Cloud Authentication (GoTrue)

When connected to AppFlowy Cloud, authentication goes through GoTrue (the authentication service used by Supabase-compatible backends):

  1. User provides credentials (email/password, OAuth, magic link)
  2. GoTrue issues JWT tokens (access + refresh)
  3. client_api stores tokens and refreshes them automatically
  4. All API requests include the JWT in the Authorization header
  5. WebSocket connections authenticate with the same JWT

7.3 Encryption

Layer Mechanism
Transport TLS for all cloud API and WebSocket connections
At rest (optional) E2E encryption for collab data when enable_encrypt is true in UserCloudConfig
Encryption secret Per-workspace secret stored in encrypt_secret field
Local data Not encrypted at rest by default (relies on OS-level disk encryption)

Warning

When E2E encryption is enabled (enable_encrypt: true), the encrypt_secret is the only key that can decrypt the workspace data. If this secret is lost, cloud-stored collab data becomes permanently unrecoverable. AppFlowy does not implement a key recovery mechanism. Users must be warned to securely store their encryption secrets.

7.4 Data Isolation

  • User isolation: Each user has a separate data directory keyed by user_id
  • Workspace isolation: Collab objects are scoped by workspace_id; the CollabDB uses (user_id, workspace_id, object_id) as the composite key
  • Search isolation: Tantivy indexes are per-workspace, preventing cross-workspace data leakage in search results

7.5 Input Validation

The lib-dispatch layer enforces input validation through the validator crate:

  • AFPluginData<T> wraps incoming Protobuf payloads
  • Structs implementing validator::Validate are automatically validated on deserialization
  • AFPluginDataValidator trait provides the validation interface
  • Invalid inputs return DispatchError before reaching business logic

The flowy-folder/src/entities/parser/ directory contains parser modules for workspace names, view names, and other user-provided identifiers, enforcing length limits and character restrictions.

7.6 Error Handling

The flowy-error crate defines the ErrorCode enum, which is the application's canonical error type. Key security-relevant error codes:

Error Code Meaning
UserUnauthorized Authentication required or session expired
RecordNotFound Requested resource does not exist (prevents enumeration)
InvalidData Input validation failure
LocalVersionNotSupport Data format version incompatible
FileLimitExceeded Total file storage quota exceeded
SingleFileLimitExceeded Individual file size limit exceeded

FlowyError wraps ErrorCode with context messages and propagates through the event dispatch system back to the Dart layer.

7.7 Dependency Security

AppFlowy's Rust dependencies are managed through Cargo with Cargo.lock pinning exact versions. The client_api dependency connects to AppFlowy Cloud and is the primary network-facing component. No native code dependencies are loaded dynamically — all Rust code is statically linked into the FFI library.


8. Developer Internals

8.1 Event Dispatch System (lib-dispatch)

The event dispatch system is AppFlowy's central nervous system — the mechanism by which the Flutter UI communicates with Rust business logic. Understanding this system is prerequisite to working on any feature.

Core Types

Type File Purpose
AFPlugin module.rs Plugin registration: maps event IDs to handler functions
AFPluginEventRequest request.rs Inbound event with payload and metadata
AFPluginEventResponse response.rs Outbound response with status and data
AFPluginData<T> data.rs Type-safe wrapper for Protobuf payloads
AFPluginState module.rs Dependency injection: provides weak references to managers
AFPluginRuntime runtime.rs Tokio runtime wrapper for async task execution
Payload request.rs Either None or Bytes — the raw event payload
ResponseBuilder response.rs Builder pattern for constructing responses
DispatchError errors/ Error type for dispatch-layer failures

Plugin Registration

Each domain crate registers its events through an event_map.rs file that returns an AFPlugin:

// Example: flowy-folder/src/event_map.rs
pub fn init(folder_manager: Weak<FolderManager>) -> AFPlugin {
    AFPlugin::new()
        .name("Flowy-Folder")
        .state(folder_manager)
        .event(FolderEvent::CreateView, create_view_handler)
        .event(FolderEvent::ReadView, read_view_handler)
        .event(FolderEvent::UpdateView, update_view_handler)
        .event(FolderEvent::DeleteView, delete_view_handler)
        // ... more events
}

The flowy-core orchestrator collects all plugins during initialization and registers them with the dispatch runtime.

Serialization Protocol

All data crossing the FFI boundary uses Protobuf:

  1. Dart creates a Protobuf message (e.g., CreateViewPayloadPB)
  2. Serializes to bytes
  3. Calls FFI function with event ID + bytes
  4. Rust lib-dispatch routes to handler based on event ID
  5. Handler deserializes payload via AFPluginData<CreateViewPayloadPB>::from_request()
  6. Handler processes and returns AFPluginData<ViewPB> (or FlowyError)
  7. Response serialized to bytes, returned through FFI
  8. Dart deserializes the Protobuf response

Byte Traits

The byte_trait.rs file defines the serialization contract:

  • ToBytes — serialize a type to Bytes (implemented for all Protobuf types via blanket impl)
  • AFPluginFromBytes — deserialize from Bytes (implemented for all Protobuf types)

These traits use TryInto<Bytes> and TryFrom<Bytes> with ProtobufError as the error type, ensuring that only types with valid Protobuf schemas can cross the FFI boundary.

8.2 Rust Crate Reference

flowy-core (Orchestrator)

Location: frontend/rust-lib/flowy-core/src/ (10 files, ~75K chars)

The central crate that wires everything together:

File Purpose
lib.rs AppFlowyCore struct — holds Arc references to all managers
app_life_cycle.rs AppLifeCycleImpl — authentication lifecycle handler
server_layer.rs ServerProvider — manages cloud server type and connection
full_indexed_data_provider.rs Background full-content indexing for search
instant_indexed_data_provider.rs (implied) Real-time content indexing

AppFlowyCore::new() is the application's bootstrap function:

  1. Creates the AFPluginRuntime (Tokio runtime)
  2. Instantiates UserManager with user paths and session config
  3. Creates ServerProvider with cloud configuration
  4. Instantiates all domain managers (Folder, Database, Document, Search, AI, Storage)
  5. Creates AppLifeCycleImpl and registers it with UserManager
  6. Registers all AFPlugin instances with the dispatch runtime
  7. Returns the configured AppFlowyCore ready for events

flowy-user (Authentication & User Management)

Location: frontend/rust-lib/flowy-user/src/ (4 files, ~43K chars)

Component Purpose
UserManager Central user state: session, profile, workspace list
Event handlers sign_in_with_email_password, sign_up, sign_out, delete_account, get_user_profile, update_user_profile
UserCloudConfig Cloud sync toggle, encryption toggle, encryption secret
Data import/export Cross-workspace and cross-device data portability

flowy-user-pub (flowy-user-pub/src/) — Public types shared with other crates:

  • UserProfile — user metadata (name, email, avatar, workspace list)
  • UserWorkspace — workspace identity and type
  • WorkspaceType — Local vs cloud workspace enum
  • Cloud service provider traits

Migrations (flowy-user/src/migrations/, 9 files, ~16K chars): Sequential database schema migrations. Each migration is a numbered module (e.g., m001_initial.rs through m009_*). Migrations are discovered and executed in order by the migration runner during authentication.

Services (flowy-user/src/services/):

  • UserConfig — device configuration and data paths
  • UserPaths — filesystem path resolution for user data directories
  • Entity types for internal service communication

flowy-folder (Workspace & View Hierarchy)

Location: frontend/rust-lib/flowy-folder/src/ (11 files, ~146K chars — the largest module)

This is the most complex crate, managing the entire workspace tree:

Component Purpose
FolderManager Central manager: workspace, views, favorites, recent, trash, sections
View CRUD create_view, get_view, update_view, delete_view, move_view, duplicate_view
View types Document, Grid, Board, Calendar
Spaces Public and private view collections
Share/Publish View sharing and public publishing
Import CSV, AFDoc format import
Workspace members Role-based access: Owner, Member, Guest
Sections Favorites, Recent, Trash — special view collections

flowy-folder-pub (flowy-folder-pub/src/):

  • Public types for folder entities
  • ViewPB, WorkspacePB, TrashPB Protobuf types
  • Folder cloud service traits

The folder data is a hierarchical tree stored as a CRDT collab object. Each view has:

  • view_id (UUID)
  • parent_view_id (UUID, for hierarchy)
  • name (string)
  • layout (Document, Grid, Board, Calendar)
  • created_at / updated_at (timestamps)
  • icon (optional emoji or custom)
  • extra (JSON blob for layout-specific metadata)

flowy-document (Block-Based Documents)

Location: frontend/rust-lib/flowy-document/src/ (11 files, ~62K chars)

Component Purpose
DocumentManager Open/close/create documents, editor cache
Block data model blocks map, children_map, text_map
Awareness states Real-time collaboration cursor/selection sharing
Snapshots Point-in-time document state capture
Sync state Subscription-based sync status monitoring
Notifications DocumentNotification enum for UI updates

flowy-document-pub (flowy-document-pub/src/):

  • Public document types shared across crates
  • Cloud service traits for document sync

Documents use a block-based data model where each block has:

  • A unique ID
  • A type (paragraph, heading, list, code, image, table, etc.)
  • A delta (rich text operations in Quill-like format)
  • Children (nested blocks via children_map)
  • Attributes (formatting, alignment, etc.)

The document collab object is a Yjs Doc with three root-level maps:

  1. blocks — block ID to block data
  2. children_map — block ID to ordered child IDs
  3. text_map — block ID to rich text YText

flowy-database2 (Grid, Board, Calendar)

Location: frontend/rust-lib/flowy-database2/src/ (6 files, ~114K chars)

Component Purpose
DatabaseManager Database lifecycle, editor cache management
Database views Grid, Board, Calendar layout rendering
Fields (columns) Type system with rich type options
Rows Row CRUD, cell access, grouping
Filters Per-field filter conditions
Sorts Multi-field sorting
Groups Group-by for Board/Calendar views
Calculations Aggregation functions on fields
CSV export CSVFormat for data export

Field Type System:

Field Type Purpose
RichText Plain/rich text content
Number Numeric values with format options (currency, percentage, etc.)
DateTime Date and optional time with timezone
SingleSelect One option from a predefined set
MultiSelect Multiple options from a predefined set
Checkbox Boolean toggle
URL Hyperlink with title
Checklist Sub-item checklist with completion tracking
Media File/image attachments
Relation Cross-database row reference
Timestamp Auto-generated created/modified timestamps

Services (flowy-database2/src/services/):

  • field/type_options/ — individual type option implementations
  • group/ — grouping logic for Board and Calendar views
  • filter_test/ — filter evaluation engine

flowy-database-pub (flowy-database-pub/src/):

  • Public database types and cloud service traits

Utils (flowy-database2/src/utils/):

  • CSV generation utilities
  • Database math/aggregation helpers

flowy-ai (AI Integration)

Location: frontend/rust-lib/flowy-ai/src/ (11 files, ~119K chars)

Component Purpose
AIManager Chat lifecycle, model selection, capability management
Chat Individual chat session state and message history
AICompletion Text completion and suggestion tasks
LocalAIController Ollama integration for local model inference
ChatServiceMiddleware Routes requests between cloud AI and local AI
ModelSelectionControl Manages available models from server and local sources

AI Entity Types:

Protobuf Type Purpose
ChatInfoPB Chat session metadata
SendChatPayloadPB Send a message to a chat
StreamChatPayloadPB Stream a chat response
AIModelPB Model identity and capabilities

Middleware (flowy-ai/src/middleware/): The ChatServiceMiddleware implements the routing logic:

  1. Check if local AI is enabled and a local model is available
  2. If local: route to LocalAIController which communicates with Ollama
  3. If cloud: route to the cloud AI service via flowy-server
  4. Handle streaming responses from either source

flowy-ai-pub (flowy-ai-pub/src/):

  • Public AI types and service traits
  • AIModelPB, CompletionTypePB shared across crates

Note

Local AI via Ollama is fully optional. When enabled, AppFlowy manages the Ollama process lifecycle, downloads requested models, and routes AI requests locally. This provides offline AI capability without cloud dependency. The LocalAIController monitors Ollama process health and falls back to cloud AI if the local process fails.

Location: frontend/rust-lib/flowy-search/src/ (3 files, ~2K chars + services ~7K chars)

Component Purpose
SearchManager Tantivy index lifecycle, search execution
Stream-based search Results delivered via Dart port for async UI updates
Index management Per-workspace Tantivy index creation and maintenance

Services (flowy-search/src/services/):

  • Index creation and schema definition
  • Document indexing (title, content, metadata)
  • Query parsing and execution
  • Result ranking and formatting

flowy-search-pub (flowy-search-pub/src/):

  • tantivy_state_init module — Tantivy state creation and teardown (close_document_tantivy_state())
  • Public search types shared across crates

The Tantivy schema indexes:

  • Document title (tokenized, stored)
  • Document content (tokenized, not stored in full — snippets generated from highlights)
  • View ID (stored, for linking results to views)
  • Workspace ID (for index isolation)

flowy-storage (File Management)

Location: frontend/rust-lib/flowy-storage/src/ (9 files, ~52K chars)

Component Purpose
StorageManager File upload queue, progress tracking, cache management
Chunked upload Large files split into chunks for reliable upload
File cache Temporary storage before upload, cached downloads
Events RegisterStream (upload progress port), QueryFile (file metadata)

flowy-storage-pub (flowy-storage-pub/src/):

  • Public storage types and cloud service traits
  • File metadata, upload status enums

The upload pipeline:

  1. File placed in local cache directory
  2. StorageManager splits into chunks (configurable chunk size)
  3. Each chunk uploaded independently with retry logic
  4. Progress reported via registered stream port
  5. On completion, file URL returned and cache entry marked complete
  6. Failed uploads are retried with exponential backoff

flowy-server (Cloud Abstraction)

Location: frontend/rust-lib/flowy-server/src/ (multi-file)

Component Purpose
ServerProvider Manages cloud server type and connection lifecycle
af_cloud/ AppFlowy Cloud implementation via client_api
LoggedUser trait Abstraction for the currently authenticated user

The flowy-server crate abstracts all cloud interactions behind traits defined in flowy-server-pub:

  • UserCloudServiceProvider — authentication and user management
  • Folder, Document, Database cloud service traits
  • Storage cloud service (file upload/download)
  • AI cloud service (chat, completion)

flowy-server-pub (flowy-server-pub/src/):

  • All cloud service trait definitions
  • Server configuration types
  • Shared error types for cloud operations

Cloud communication uses:

  • REST API via client_api for CRUD operations
  • WebSocket for real-time collab sync
  • Streaming HTTP for AI chat responses

collab-integrate (CRDT Collaboration)

Location: frontend/rust-lib/collab-integrate/src/ (5 files, ~28K chars)

Component Purpose
AppFlowyCollabBuilder Factory for creating collab objects
Plugin configuration Local storage and cloud sync plugins per collab
Persistence CollabDB (RocksDB-based) via KV store

The AppFlowyCollabBuilder creates collab objects (Collab from the collab crate family) with the appropriate plugins:

AppFlowyCollabBuilder
    ├── Local storage plugin (CollabDB persistence)
    ├── Cloud sync plugin (WebSocket updates)
    └── Type-specific plugin (document, database, folder)

Each collab object wraps a Yjs Doc:

  • collab_document — block-based document CRDT
  • collab_database — database/grid/board CRDT
  • collab_folder — workspace hierarchy CRDT

The builder configures:

  • CollabPersistenceConfig — local persistence settings
  • CollabCloudPluginProvider — cloud sync channel
  • CollabDataSource — initial data source (local disk or cloud fetch)

Important

Collab objects are created with Weak references to avoid circular ownership. The builder uses Weak<AppFlowyCollabBuilder> itself. If the builder is dropped while collab objects are still alive, new collab creation will fail with FlowyError::ref_drop(). This is by design — collab objects should not outlive the application core.

flowy-notification (Notification System)

Location: frontend/rust-lib/flowy-notification/src/

The notification crate provides typed notifications from Rust to Dart:

  • Each domain defines its own notification enum (e.g., FolderNotification, DocumentNotification, DatabaseNotification)
  • Notifications are sent via a registered Dart port
  • The Flutter layer subscribes to notification streams per-entity

flowy-error (Error System)

Location: frontend/rust-lib/flowy-error/src/

Defines the application-wide error type:

Component Purpose
ErrorCode enum Canonical error variants
FlowyError Error struct with code + message + context
FlowyResult<T> Result<T, FlowyError> alias

Key error codes beyond security (covered in Section 7.6):

Error Code Meaning
AlreadyExists Duplicate resource creation attempt
AIResponseLimitExceeded AI usage quota exhausted
AIImageResponseLimitExceeded AI image generation quota exhausted
AIMaxRequired AI feature requires higher subscription tier
LocalAINotReady Local AI (Ollama) not initialized
LocalAIDisabled Local AI feature disabled in settings

flowy-date (Date Utilities)

Location: frontend/rust-lib/flowy-date/src/

Date parsing and formatting utilities used by the database date field type. Handles timezone conversion, date format strings, and relative date calculations.

lib-infra (Infrastructure Utilities)

Location: frontend/rust-lib/lib-infra/src/

Shared infrastructure utilities:

  • async_trait re-export for async trait definitions
  • Utility types used across crates
  • Platform abstraction helpers

lib-log (Logging Infrastructure)

Location: frontend/rust-lib/lib-log/src/

Logging setup and configuration:

  • Initializes the tracing subscriber with formatted output
  • Configures log level from RUST_LOG environment variable
  • Provides log file rotation (if configured)

build-tool (Code Generation)

Location: frontend/rust-lib/build-tool/

Three crates for compile-time code generation:

Crate Purpose
flowy-ast Parses Rust struct definitions annotated with #[derive(ProtoBuf)] to extract field metadata (field numbers, types, optionality)
flowy-codegen Generates .proto files from parsed AST, generates Dart event dispatch classes, generates Rust event handler boilerplate
flowy-derive Procedural macros: ProtoBuf (struct serialization), ProtoBuf_Enum (enum serialization), event handler macros

The code generation pipeline:

  1. Developer annotates a Rust struct with #[derive(ProtoBuf)]
  2. flowy-derive proc macro invokes flowy-ast to parse the struct
  3. flowy-codegen generates the corresponding .proto file
  4. protoc compiles to Dart and Rust bindings
  5. Event map generation creates the Dart-side event dispatch classes

8.3 Flutter Layer Architecture

Entry Point

lib/main.dart calls runAppFlowy() from the startup package:

Future<void> main() async {
  ScaledWidgetsFlutterBinding.ensureInitialized(
    scaleFactor: (_) => 1.0,
  );
  await runAppFlowy();
}

runAppFlowy() (in lib/startup/startup.dart) initializes:

  1. Platform-specific bindings
  2. Rust FFI bridge
  3. Environment configuration
  4. Root widget tree

Plugin System

Location: lib/startup/plugin/ and lib/plugins/

The Flutter plugin system registers view types that users can create:

Plugin View Type Purpose
Document Document Rich text editor with block-based content
Database (Grid) Grid Spreadsheet-like table view
Database (Board) Board Kanban board view
Database (Calendar) Calendar Calendar event view
Trash Trash Deleted items recovery

Each plugin provides:

  • A PluginBuilder for creating new instances
  • Widget builders for the view content
  • Menu item builders for the sidebar
  • Event handlers for plugin-specific actions

Plugin registration happens in lib/startup/plugin/src/ where each plugin type is registered with the plugin registry.

State Management

AppFlowy uses the BLoC (Business Logic Component) pattern throughout the Flutter layer:

  • Each feature has a corresponding BLoC in application/ directories
  • BLoCs communicate with Rust via event dispatch
  • UI widgets observe BLoC state via BlocBuilder / BlocListener
  • Settings BLoCs: CloudSettingBloc, AppearanceSettingsCubit, NotificationSettingsBloc

Flutter Package Structure

The packages/ directory contains reusable Flutter packages:

Package Purpose
appflowy_backend FFI bridge to Rust (FlowySDK class)
appflowy_popover Custom popover/dropdown widget
appflowy_result Result type utilities (FlowyResult Dart equivalent)
appflowy_ui Shared UI components (buttons, modals, text fields, avatars, menus)
flowy_infra Infrastructure: color schemes, platform utils
flowy_infra_ui Platform-specific UI implementations (Android, Web, platform interface)
flowy_svg SVG rendering utilities

Authentication Layer

Location: lib/user/application/auth/

Three AuthService implementations:

  • CloudAuthService — production cloud authentication
  • BackendAuthService — direct backend auth (development)
  • MockAuthService — in-memory auth for tests

The auth layer wraps Rust-side auth events, providing a Dart-friendly interface for sign-in flows, token management, and session persistence.

Settings Architecture

Location: lib/workspace/application/settings/ and lib/features/settings/

Settings are split across multiple BLoCs:

BLoC Controls
CloudSettingBloc Cloud sync toggle, server configuration
AppearanceSettingsCubit Theme, font, locale, layout density
NotificationSettingsBloc Push notification preferences

Settings data models live in lib/features/settings/data/models/ with corresponding presentation widgets in lib/workspace/presentation/settings/.

8.4 Data Model Deep Dive

Collab Object Hierarchy

Workspace (collab_folder)
├── Space (public or private)
│   ├── View (Document)
│   │   └── collab_document
│   ├── View (Grid)
│   │   └── collab_database
│   ├── View (Board)
│   │   └── collab_database (same type, different layout)
│   └── View (Calendar)
│       └── collab_database (same type, different layout)
├── Favorites (section)
├── Recent (section)
└── Trash (section)

Each collab object is identified by:

  • object_id (UUID) — unique identifier
  • collab_type (enum) — Document, Database, Folder, etc.
  • workspace_id (UUID) — owning workspace

Document Data Model

{
  "blocks": {
    "block_id_1": {
      "id": "block_id_1",
      "type": "paragraph",
      "data": { "delta": [...] },
      "parent": "page_block_id",
      "children": "children_id_1"
    }
  },
  "children_map": {
    "children_id_1": ["block_id_2", "block_id_3"]
  },
  "text_map": {
    "block_id_1": "<YText CRDT object>"
  }
}

Block types include: paragraph, heading (h1-h6), bulleted_list, numbered_list, checkbox, quote, code, divider, image, table, callout, math_equation, toggle_list, outline, and more.

Rich text uses a Quill-compatible delta format:

[
  { "insert": "Hello " },
  { "insert": "world", "attributes": { "bold": true } },
  { "insert": "!" }
]

Database Data Model

A database collab object contains:

  • Fields — column definitions with type options
  • Rows — data rows, each containing cells keyed by field ID
  • Views — named configurations (Grid, Board, Calendar) with filters, sorts, groups
  • Meta — database-level settings

Each field has a TypeOptionData blob specific to its type:

  • RichTextTypeOption — no extra config
  • NumberTypeOption — format (decimal, currency, percentage), scale, symbol
  • DateTypeOption — date format, time format, timezone
  • SelectTypeOption — option list with colors
  • ChecklistTypeOption — sub-option tracking

8.5 Notification System

Notifications flow from Rust to Dart through a port-based mechanism:

Rust handler
    │
    ▼
NotificationSender::send(subject_id, notification_type, payload)
    │
    ▼
Dart port (registered per-entity)
    │
    ▼
NotificationListener<T> (Dart widget/BLoC)
    │
    ▼
UI update

Each domain defines notification types:

Domain Example Notifications
Folder DidUpdateWorkspace, DidUpdateView, DidUpdateTrash, DidUpdateFavorites
Document DidUpdateContent, DidUpdateAwareness
Database DidUpdateFields, DidUpdateRows, DidUpdateCells, DidUpdateFilters
User DidUpdateUserProfile, DidUpdateWorkspace

8.6 Testing Architecture

Integration Tests

Location: frontend/appflowy_flutter/integration_test/

Directory Platform Scope
desktop/settings/ Desktop Settings page flows
desktop/cloud/ Desktop Cloud-connected scenarios
mobile/settings/ Mobile Mobile settings flows

Integration tests use Flutter's IntegrationTestWidgetsFlutterBinding and run the full application stack.

Rust Event Integration Tests

Location: frontend/rust-lib/event-integration-test/src/

These tests exercise the Rust layer directly through the event dispatch system:

  • document/ — document creation, editing, collab operations
  • Database CRUD, field operations, view switching
  • User authentication flows
  • Folder operations

Unit Tests

  • Dart unit tests: frontend/appflowy_flutter/test/
    • unit_test/ — pure unit tests
    • bloc_test/ — BLoC state transition tests
    • widget_test/ — widget rendering tests
  • Rust unit tests: in-crate #[cfg(test)] modules
    • flowy-document/tests/ — document operation tests
    • flowy-database2/tests/ — database operation tests (filter, layout, mock data)

Test Configuration

  • BLoC tests: test/bloc_test/lib/features/settings/ — settings-specific BLoC tests
  • Widget tests: test/widget_test/lib/ — widget rendering validation

Tip

When adding a new event handler, write a Rust event integration test first (event-integration-test/). This catches serialization errors and handler registration issues before the Flutter layer is involved. The integration test framework creates a full AppFlowyCore in-process, so tests exercise the real dispatch path.


9. Integration Points

9.1 AppFlowy Cloud

AppFlowy Cloud is the official cloud backend providing:

Feature Protocol Endpoint
Authentication REST (GoTrue) gotrue_url
User management REST base_url/api/
Collab sync WebSocket ws_url
File storage REST base_url/api/file_storage/
AI services REST (streaming) base_url/api/ai/

The client_api crate (external dependency) handles all cloud communication. ServerProvider in flowy-server manages the connection lifecycle:

  1. On authentication: establish REST session, connect WebSocket
  2. On collab open: subscribe to real-time updates for that object
  3. On local edit: push update through WebSocket
  4. On remote update: apply to local collab object (CRDT merge)
  5. On disconnect: queue updates locally, replay on reconnect

9.2 Ollama (Local AI)

The LocalAIController in flowy-ai manages local AI through Ollama:

Operation Implementation
Process management Start/stop Ollama process
Model management Download, list, select models
Chat inference Send prompts, stream responses
Text completion Inline text suggestions
Health monitoring Process health checks, auto-restart

Configuration is per-workspace: users can select different models for different workspaces. The ModelSelectionControl aggregates available models from both server (cloud) and local (Ollama) sources.

Note

Local AI model selection persists in the user's workspace settings. Switching between local and cloud AI does not affect conversation history — the ChatServiceMiddleware abstracts the backend transparently.

9.3 Subscription and Billing

The SubscriptionPlan enum from client_api::entity::billing_dto defines plan tiers:

Plan AI Capabilities
Free Basic AI with response limits
Pro Extended AI limits
AI Max Unlimited AI responses, image generation

The AIMaxRequired error code gates features requiring higher tiers. AIResponseLimitExceeded and AIImageResponseLimitExceeded enforce per-period quotas.

9.4 Import/Export

Format Direction Handler
CSV Import flowy-folder import handler
CSV Export flowy-database2 CSVFormat export
AFDoc Import Native AppFlowy document format import
Markdown Import/Export Document content conversion

9.5 Internationalization

Location: project.inlang/

AppFlowy uses the inlang framework for internationalization:

  • Translation files define all UI strings
  • Flutter AppLocalizations generated from translation sources
  • Locale selection persisted in SharedPreferences

9.6 Theme System

AppFlowy supports custom themes through the flowy_infra package:

  • colorscheme/ — named color scheme definitions
  • AppearanceSettingsCubit — manages active theme
  • Custom theme import/export (issue #8663 notes themes can be lost after update)
  • Light and dark mode variants

9.7 Platform-Specific Integration

Platform Integration
macOS Native window management, menu bar integration
Windows Windows Runner (flutter_window.h), COM initialization
Linux GTK integration, X11/Wayland support
Android CMake native build, JNI library loading, libc++_shared.so bundling (arm64-v8a, armeabi-v7a, x86_64)
iOS Xcode project, Codemagic CI
Web Flutter Web, custom index.html, potential Tauri wrapping

9.8 Known Issues and Operational Considerations

Based on community-reported issues at the documented commit:

Issue Impact Workaround
#8655 libvdpau.so.1 missing Linux: app fails to start Install libvdpau1 package
#8577 Windows 10 crash Fresh install crash on Windows 10 Update to latest version or Windows 11
#8668 Settings page error offline Settings page throws without internet Ensure offline fallback in cloud settings BLoC
#8635 Cloud restart offline = data loss Data loss when cloud service restarts while user is offline Avoid toggling cloud settings without connectivity
#8582 Stale sidebar views Sidebar shows outdated view list Restart application to refresh
#8651 Nested grid undo crash Undo in nested grids causes grid disappearance Avoid deep undo in nested database views

Caution

Issue #8635 describes a data loss scenario: if the cloud service restarts while the user is offline, and the user has unsynced local changes, those changes may be lost when sync resumes. This is a fundamental challenge of the local-first sync model. Until this is resolved, advise users to ensure they have a working cloud connection before making critical edits to shared workspaces.

9.9 Feature Roadmap Context

Notable open feature requests that affect architectural planning:

Request Architectural Impact
#718 Plugin marketplace Requires plugin sandboxing, API versioning, distribution system
#6403 Windows ARM build New build profile, CI pipeline, potential Rust target issues
#8438 E2E Encrypted Sync Deep changes to collab sync protocol, key management
#6683 Cross-workspace search Tantivy index architecture change (currently per-workspace)

Appendix A: Crate Dependency Graph

flowy-core (orchestrator)
├── flowy-user
│   ├── flowy-user-pub
│   ├── flowy-sqlite
│   └── flowy-error
├── flowy-folder
│   ├── flowy-folder-pub
│   ├── flowy-error
│   └── flowy-notification
├── flowy-document
│   ├── flowy-document-pub
│   ├── flowy-error
│   └── flowy-notification
├── flowy-database2
│   ├── flowy-database-pub
│   ├── flowy-error
│   ├── flowy-notification
│   └── flowy-date
├── flowy-ai
│   ├── flowy-ai-pub
│   ├── flowy-error
│   └── flowy-notification
├── flowy-search
│   ├── flowy-search-pub
│   └── flowy-error
├── flowy-storage
│   ├── flowy-storage-pub
│   └── flowy-error
├── flowy-server
│   ├── flowy-server-pub
│   ├── client_api (external)
│   └── flowy-error
├── collab-integrate
│   ├── collab (external crate family)
│   └── collab_plugins
├── lib-dispatch
│   ├── protobuf
│   ├── bytes
│   └── validator
├── lib-infra
├── lib-log
│   └── tracing
├── dart-ffi
│   └── lib-dispatch
└── build-tool
    ├── flowy-ast
    ├── flowy-codegen
    └── flowy-derive

Appendix B: Event Map Reference

Each crate registers events through its event_map.rs. The event enum name follows the pattern {Domain}Event:

Crate Event Enum Example Events
flowy-user UserEvent SignInWithEmailPassword, SignUp, SignOut, DeleteAccount, GetUserProfile, UpdateUserProfile
flowy-folder FolderEvent CreateView, ReadView, UpdateView, DeleteView, MoveView, DuplicateView, CreateSpace, GetFavorites, GetRecentViews, GetTrash
flowy-document DocumentEvent CreateDocument, OpenDocument, CloseDocument, ApplyAction, GetDocumentSnapshot
flowy-database2 DatabaseEvent CreateDatabase, OpenDatabase, GetFields, CreateField, UpdateField, CreateRow, GetRow, UpdateCell, GetFilters, UpdateSort
flowy-ai AIEvent CreateChat, SendMessage, StreamMessage, GetModels, SelectModel, CompleteText
flowy-search SearchEvent Search, IndexDocument
flowy-storage StorageEvent RegisterStream, QueryFile

Tip

To trace the full path of any user action: find the Dart event dispatch call (usually in a BLoC or service class), note the event name, find the corresponding Rust event enum variant, locate the handler function in event_map.rs, then follow the handler implementation. This is the single most effective debugging technique for AppFlowy.

Appendix C: Protobuf Schema Conventions

All Protobuf types follow these conventions:

Convention Example
Payload suffix for input types CreateViewPayloadPB
PB suffix for all Protobuf types ViewPB, UserProfilePB
Field numbering starts at 1 Standard Protobuf practice
Optional fields use oneof or wrapper types Option<T> in Rust maps to optional Protobuf field
Enum values start with the enum name ViewLayout.Document, ViewLayout.Grid

The #[derive(ProtoBuf)] macro on Rust structs generates:

  1. A .proto schema file
  2. Rust TryFrom<Bytes> and TryInto<Bytes> implementations
  3. Dart class with matching fields and serialization

The #[pb(index = N)] attribute on struct fields sets the Protobuf field number, which must remain stable across versions for backward compatibility.