# AppFlowy Administrator & Developer Guide

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](/guides/AppFlowy-IO/AppFlowy/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`:

```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`:

```rust
// 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.

#### `flowy-search` (Full-Text Search)

**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:

```dart
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

```json
{
  "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:
```json
[
  { "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.

---

_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. Unofficial; not affiliated with or reviewed by the project._
