# Cherry Studio Admin & Developer Guide

This guide is the deep technical reference for **Cherry Studio** — its architecture, configuration, security model, build and distribution pipeline, data layer, and internals. It is written for administrators deploying Cherry Studio in a team, power users who automate it, and developers who want to understand or extend the codebase.

For end-user instructions (chatting, knowledge bases, paintings, translation, settings walkthroughs), see the companion **User Guide**.

> [!NOTE]
> Cherry Studio is a large, actively developed Electron monorepo (~4,900 files, ~10.2M characters of source at the documented commit). This guide focuses on the structures that matter for operating and extending it, not an exhaustive file-by-file dump.

---

## Table of Contents

1. [Executive Summary](#executive-summary)
2. [System Architecture](#system-architecture)
3. [Process Model & Startup](#process-model--startup)
4. [The IPC Surface](#the-ipc-surface)
5. [The Embedded API Server (CSaaS)](#the-embedded-api-server-csaas)
6. [Provider & Model System](#provider--model-system)
7. [Data Layer & Schema](#data-layer--schema)
8. [Subsystem Reference](#subsystem-reference)
9. [Requirements & Dependencies](#requirements--dependencies)
10. [Configuration Reference](#configuration-reference)
11. [Security Model](#security-model)
12. [Build & Distribution](#build--distribution)
13. [Updates & Upgrade Path](#updates--upgrade-path)
14. [Operations & Maintenance](#operations--maintenance)
15. [Testing & Quality Gates](#testing--quality-gates)
16. [Development Setup](#development-setup)
17. [Things That Do **Not** Exist](#things-that-do-not-exist)
18. [Troubleshooting (Operator Reference)](#troubleshooting-operator-reference)
19. [Quick Reference](#quick-reference)

---

## Executive Summary

Cherry Studio is a **cross-platform Electron desktop application** (Windows, macOS, Linux) that acts as a unified front end to many LLM providers and as a local AI workstation. It is built with:

| Layer | Technology |
|-------|-----------|
| Shell | Electron 41 (`com.cherryai.cherrystudio`) |
| Main process | Node.js (TypeScript), service-oriented lifecycle, Express API server |
| Renderer | React 19 + Redux Toolkit + TanStack Router/Query, styled-components + Tailwind, Ant Design |
| Build | electron-vite (Vite via `rolldown-vite`), electron-builder, pnpm workspaces |
| Data | SQLite via Drizzle ORM; libsql vector store for RAG; Dexie (IndexedDB) on the renderer |
| AI runtime | Vercel **AI SDK** (`@ai-sdk/*`), Anthropic **Claude Agent SDK**, MCP SDK, embedjs |

Its distinguishing capabilities for administrators:

- A **local API gateway** ("CSaaS") that re-exposes every configured provider behind OpenAI- and Anthropic-compatible HTTP endpoints.
- **MCP** (Model Context Protocol) client and host support, including built-in servers.
- **Autonomous agents** that can connect to messaging channels (Telegram, Feishu, QQ, WeChat, Discord, Slack).
- A local **knowledge base** (RAG) with a libsql vector store.
- Multiple **backup destinations**: WebDAV, S3, Nutstore, local directory, and LAN transfer.

License: **AGPL-3.0** (Community), with a separately licensed **Enterprise Edition** for centralized management and private deployment.

> [!IMPORTANT]
> Because Cherry Studio is a *client*, "deployment" means distributing and configuring the desktop app on user machines — there is no central Cherry Studio server in the Community Edition. The closest thing to a server is the optional, locally-bound API server described in [The Embedded API Server](#the-embedded-api-server-csaas).

---

## System Architecture

Cherry Studio follows the standard Electron three-tier split, with a strict service architecture in the main process.

```text
┌──────────────────────────────────────────────────────────────────────┐
│                        Electron Application                           │
│                     (appId: com.cherryai.cherrystudio)                │
│                                                                       │
│  ┌─────────────────────────┐        ┌──────────────────────────────┐ │
│  │      Renderer (React)    │        │        Main (Node.js)        │ │
│  │                          │  IPC   │                              │ │
│  │  TanStack Router pages:  │◄──────►│  Lifecycle services:         │ │
│  │   chat / agents / code   │ window │   Application + serviceList  │ │
│  │   knowledge / paintings  │  .api  │   WindowManager, Preference  │ │
│  │   translate / notes ...  │        │   ProviderService, Channels  │ │
│  │  Redux Toolkit store     │        │   Knowledge, FileStorage ... │ │
│  │  Dexie (IndexedDB)       │        │                              │ │
│  └─────────────────────────┘        │  ┌────────────────────────┐  │ │
│             ▲                        │  │  Express API server    │  │ │
│             │ contextBridge          │  │  (CSaaS, :port)        │◄─┼─┼── external
│  ┌──────────┴───────────┐            │  │  /v1/chat/completions  │  │ │   OpenAI/Anthropic
│  │   Preload (bridge)    │            │  │  /v1/messages, /mcps   │  │ │   compatible clients
│  └──────────────────────┘            │  └────────────────────────┘  │ │
│                                      │                              │ │
│                                      │  SQLite (Drizzle) + libsql   │ │
│                                      │  vector store + file storage │ │
│                                      └──────────────────────────────┘ │
│                                                  │                    │
└──────────────────────────────────────────────────┼────────────────────┘
                                                    ▼
                       External providers (OpenAI, Anthropic, Gemini,
                       Ollama, LM Studio, web search, OCR services, MCP)
```

### Repository layout

| Path | Purpose |
|------|---------|
| `src/main/` | Electron main process: lifecycle, services, IPC, API server, AI runtime |
| `src/renderer/` | React UI: routes/pages, components, hooks, Redux store, services |
| `src/preload/` | `contextBridge` API exposed to the renderer |
| `src/shared/` | Code shared by main and renderer: types, config, utilities, IPC channel enum |
| `packages/` | Published workspace packages (see below) |
| `migrations/sqlite-drizzle/` | Drizzle SQL migrations for the SQLite schema |
| `resources/` | Bundled runtime assets: databases, builtin agents, skills, data |
| `config/` | App-upgrade segment definitions |
| `tests/` | Vitest unit/integration + Playwright e2e |

### Workspace packages

The monorepo (pnpm workspaces, managed with Changesets) publishes several internal packages:

| Package | Role |
|---------|------|
| `@cherrystudio/ai-core` (`packages/aiCore`) | Provider-agnostic AI runtime core (providers, models, plugins, runtime) |
| `@cherrystudio/ai-sdk-provider` | Cherry Studio's Vercel AI SDK provider integration |
| `@cherrystudio/provider-registry` | Provider/model registry data and schemas |
| `@cherrystudio/ui` (`packages/ui`) | Shared component library, icons, design tokens |
| `@cherrystudio/extension-table-plus` | TipTap editor table extension |
| `@vectorstores/libsql` | libsql-backed vector store for RAG |
| `@cherrystudio/mcp-trace` | MCP tracing (OpenTelemetry) |

---

## Process Model & Startup

The main entry point is `src/main/index.ts`. Its own header comment is explicit: *"This file is glue — it should only shrink."* New services belong in the lifecycle system, not the entry file.

Startup proceeds through ordered phases:

1. **BootConfig load** — `@main/data/bootConfig` is imported *first* (before any other import) so the `userData` path is configured before Electron reads it.
2. **Preboot** (order matters):
   - `resolveUserDataLocation()` — resolve where app data lives.
   - `requireSingleInstance()` — enforce a single running instance.
   - `configureChromiumFlags()` — set Chromium command-line flags.
   - `initCrashTelemetry()` — install crash reporting.
   - `application.initPathRegistry()` — freeze the path registry (bootstrap asserts this completed).
3. **V2 migration gate** — `runV2MigrationGate()`. If a major data migration is required, a migration window takes over and normal startup is skipped (`'handled'`).
4. **App identity** — `electronApp.setAppUserModelId('com.cherryai.cherrystudio')` (Windows notification/taskbar identity).
5. **Lifecycle bootstrap** — `application.registerAll(serviceList)` then `application.bootstrap()` runs in parallel with `app.whenReady()`. The `BeforeReady` lifecycle stage overlaps Electron readiness.
6. **Version recording** — `versionService.recordCurrentVersion()` for upgrade-path tracking.
7. **IPC registration** — `registerIpc()` wires the (currently monolithic) IPC handler table.

> [!NOTE]
> Service initialization is structured: a `ServiceInitError` during `bootstrap()` is handled internally (dialog → exit/relaunch). The top-level `catch` in `index.ts` is only a safety net for unexpected escapes, which call `application.forceExit(1)`.

### Lifecycle service pattern

Services are registered with the central `application` object and resolved by name, e.g. `application.get('PreferenceService')`, `application.get('WindowManager')`, `application.get('ChannelManager')`. Some services are conditionally activated by platform/CPU (e.g., the OpenVINO Model Server manager runs only on Windows + Intel via `@Conditional(onPlatform('win32'), onCpuVendor('intel'))`).

> [!NOTE]
> A `TODO(v2)` in the code notes that the **monolithic IPC registration** (`registerIpc()`) creates timing coupling between bootstrap and IPC readiness; the intended direction is per-service `ipcHandle`/`ipcOn` inside lifecycle services.

---

## The IPC Surface

The renderer never touches Node APIs directly. It calls main-process handlers through a `contextBridge`-exposed API in `src/preload/`, and the main process registers handlers keyed by a typed `IpcChannel` enum (`@shared/IpcChannel`). This channel table — registered in `src/main/ipc.ts` and across lifecycle services — is effectively Cherry Studio's internal API.

Representative channel groups (each `ipcMain.handle(IpcChannel.X, ...)`):

| Group | Examples | Service |
|-------|----------|---------|
| App | `App_Info`, `App_ClearCache`, `App_GetCacheSize`, `App_Select`, `App_ResetData`, `App_GetDiskInfo`, `App_SetLaunchOnBoot`, `App_HandleZoomFactor`, `App_SetEnableSpellCheck` | AppService / ConfigManager |
| File | `File_Open/Save/Read/Write/Delete/Move/Rename`, `File_Upload`, `File_SaveBase64Image`, `File_ListDirectory`, `File_BatchUploadMarkdown` | FileStorage / FileSystemService |
| PDF / FS | `Pdf_ExtractText`, `Fs_Read`, `Fs_ReadText` | shared PDF util / FileService |
| Backup | `Backup_Backup/Restore`, `Backup_BackupToWebdav`, `Backup_BackupToS3`, `Backup_BackupToLocalDir`, `Backup_CreateLanTransferBackup`, `*_List*`, `*_Check*Connection` | LegacyBackupManager |
| Skills | `Skill_List/Install/Uninstall/Toggle/InstallFromZip/InstallFromDirectory/ReadFile/ListFiles/ListLocal` | SkillService |
| Cloud/auth | `Copilot_*` (GitHub Copilot), `VertexAI_GetAuthHeaders/AccessToken`, `Nutstore_*`, `Obsidian_GetVaults/GetFiles` | CopilotService / VertexAiService / NutstoreService / ObsidianVaultService |
| Channels | `Channel_GetLogs`, `Channel_GetStatuses`, `WeChat_HasCredentials` | ChannelManager |
| System | `System_GetDeviceType/Hostname/CpuName`, `System_CheckGitBash`, `System_GetGitBashPath(Info)`, `System_SetGitBashPath`, `System_ToggleDevTools` | system utils |
| Security | `Aes_Encrypt`, `Aes_Decrypt`, `Cherryai_GetSignature` | aes util / cherryai integration |
| Binaries | `App_IsBinaryExist`, `App_GetBinaryPath`, `App_InstallUvBinary`, `App_InstallBunBinary`, `App_InstallOvmsBinary`, `Ovms_IsSupported` | process util / OvmsManager |
| Misc | `Open_Website` (validated URL), `Open_Path`, `Notification_Send`, `Zip_Compress/Decompress`, `Export_Word` | various |

> [!IMPORTANT]
> `Open_Website` does **not** blindly open URLs — it calls `isSafeExternalUrl(url)` and refuses untrusted schemes (logged as a warning). This is part of the defense against renderer-driven navigation attacks.

---

## The Embedded API Server (CSaaS)

`src/main/apiServer/` runs an **Express 5** HTTP server inside the main process that re-exposes Cherry Studio's configured providers behind standard APIs. Internally this feature is named **CSaaS** ("Cherry Studio as a Service").

### Lifecycle

The `ApiServer` class (`server.ts`) wraps a Node `http.Server`. It reads `feature.csaas.host` and `feature.csaas.port` from the `PreferenceService`, builds the Express app via the `createApp()` factory, applies timeouts, and listens. On successful listen it broadcasts `IpcChannel.ApiServer_Ready` to the main window. It exposes `start()`, `stop()`, `restart()`, and `isRunning()`.

Timeouts: request timeout **5 minutes**, headers timeout request+5s, keep-alive **60s**, and per-socket timeout disabled (`setTimeout(0)`) so long streaming responses aren't cut off.

> [!NOTE]
> The `createApp()` factory pattern is a deliberate timing workaround so Express construction, OpenAPI setup, and route mounting don't run before preboot. A `TODO` flags the app↔routes↔services coupling for a future refactor.

### Routes

| Method | Path | Auth | Backed by |
|--------|------|------|-----------|
| `GET` | `/health` | none | inline (status/timestamp/version) |
| `GET` | `/` | none | inline (endpoint catalog) |
| `GET` | `/api-docs`, `/api-docs.json` | none | swagger-ui-express + generated OpenAPI spec |
| `POST` | `/v1/chat/completions` | API key | `chatCompletionService` (OpenAI-compatible) |
| `POST` | `/v1/messages` | API key | `messagesService` (Anthropic-compatible) |
| `POST` | `/{provider}/v1/messages` | API key | `messagesService`, provider from URL |
| `GET` | `/v1/mcps`, `/v1/mcps/{server_id}` | API key | `mcpApiService` |
| `ALL` | `/v1/claw/{agentId}/claw-mcp` | API key | `ClawServer` (per-session MCP over Streamable HTTP) |
| `*` | `/v1/knowledge-bases`, `/v1/knowledge-bases/search` | API key | knowledge routes |

The OpenAPI docs are mounted **before** the protected router so they stay public. Provider-specific messages and the `/v1` router each apply `authMiddleware`.

### Request handling details

- **Streaming**: `/v1/chat/completions` with `stream:true` returns `text/event-stream`, writes `data: <chunk>\n\n` frames, ends with `data: [DONE]\n\n`, and aborts the upstream provider stream when the HTTP client disconnects (so provider tokens aren't wasted on a closed socket).
- **Model resolution**: the `model` field is parsed as `providerId::modelId` (`parseUniqueModelId`). The provider is resolved via `providerService.getByProviderId`, its API key is fetched with **key rotation** (`getRotatedApiKey`), and the correct endpoint base URL is selected from `provider.endpointConfigs[ENDPOINT_TYPE.OPENAI_CHAT_COMPLETIONS]`. If no OpenAI-compatible base URL is configured, the request is rejected rather than risk sending the provider's key to `api.openai.com`.
- **Error mapping**: errors are normalized to OpenAI/Anthropic-style error bodies with `type`/`code`, trusting the SDK's structured `.status` rather than regex-matching messages (a genuine 500 is not remapped to 502).
- **Claw MCP**: each MCP session gets its own `ClawServer` + `StreamableHTTPServerTransport` pair (the MCP SDK Server supports one transport at a time); sessions have a 30s init timeout and are keyed by `mcp-session-id`.

### Authentication

`middleware/auth.ts` accepts a credential as either `x-api-key: <key>` or `Authorization: Bearer <key>`, compared against `feature.csaas.api_key` with `crypto.timingSafeEqual` (constant-time). Responses: missing credentials → `401`; no server key configured → `403`; wrong key → `403`.

> [!CAUTION]
> CORS is set to `origin: '*'`. The server has no built-in rate limiting and no per-client identity beyond the single shared API key. Bind it to `127.0.0.1` for single-machine use; only expose it on a LAN/host interface deliberately, ideally behind a reverse proxy that adds TLS and rate limiting.

---

## Provider & Model System

Cherry Studio abstracts many AI vendors behind a uniform model interface.

- **Provider registry** (`packages/provider-registry`, bundled to `resources/provider-registry`) ships known providers and models with their endpoints and capabilities.
- **AI core** (`packages/aiCore`) holds the provider-agnostic runtime: a provider/extension registry, model definitions, plugins, and a runtime loop, built on the Vercel **AI SDK** (`@ai-sdk/*` for OpenAI, Anthropic, Google, Bedrock, Azure, Cohere, Groq, Mistral, Perplexity, xAI, Together, HuggingFace, and more).
- **Endpoint configs**: each provider can declare multiple endpoint types (e.g., OpenAI chat-completions vs. Anthropic messages). Mixed providers (e.g., aihubmix, new-api, cherryin) ship multiple endpoint keys, so the code selects the endpoint **by type**, never by "first value."
- **API-key rotation**: `providerService.getRotatedApiKey()` supports multiple keys per provider, rotating across requests.
- **Custom provider extensions**: `aiCore`'s `ExtensionRegistry`/`ProviderExtension` allow registering provider aliases and custom integrations (with validation — e.g., a provider extension must have a name; OpenAI-compatible providers require settings).

> [!NOTE]
> Local inference is just another provider: point an OpenAI-compatible provider at **Ollama** or **LM Studio**. There is no special "local mode" — it's the same endpoint-config mechanism with a localhost base URL.

---

## Data Layer & Schema

Persistent state lives in a **SQLite** database accessed through **Drizzle ORM** (WAL-oriented; migrations under `migrations/sqlite-drizzle/`). The renderer additionally uses **Dexie** (IndexedDB) for some client-side state, and **redux-persist** persists Redux slices. A **libsql** vector store backs knowledge-base embeddings. Migrations are generated/checked with `drizzle-kit` (`db:migrations:generate`, `db:migrations:check`).

### Core tables

| Table | Holds |
|-------|-------|
| `assistant` | Chat assistants (prompt + model + params) |
| `assistant_knowledge_base`, `assistant_mcp_server` | Join tables linking assistants to knowledge bases and MCP servers |
| `topic`, `message` | Conversations and their messages |
| `group`, `pin` | Organization/grouping and pinned items |
| `prompt` | Reusable prompt snippets |
| `tag`, `entity_tag` | Tagging across entities |
| `preference` | Key/value user preferences (the `PreferenceService` store) |
| `app_state` | Internal application state |
| `user_provider`, `user_model` | User-configured providers and models |

### Agent & automation tables

| Table | Holds |
|-------|-------|
| `agent`, `agent_session`, `agent_session_message` | Agents and their running sessions/messages |
| `agent_channel`, `agent_channel_task` | Messaging-channel bindings and their task links |
| `agent_global_skill`, `agent_skill` | Installed skills and per-agent enablement |
| `agent_task`, `agent_task_run_log` | Agent tasks and execution logs |
| `agent_workspace` | Agent working directories |
| `job`, `job_schedule` | Scheduled-task engine records |

### Feature tables

| Table | Holds |
|-------|-------|
| `knowledge_base`, `knowledge_item` | RAG knowledge bases and their indexed items |
| `mcp_server` | Configured MCP servers |
| `mini_app` | Installed mini apps |
| `note` | Notes |
| `painting` | Generated images |
| `translate_history`, `translate_language` | Translation history and language config |
| `file_entry`, `file_ref` | File storage metadata and reference counting |

### Channel & permission constraints (from the schema)

The `agent_channel` table enforces, via CHECK constraints, the valid channel types and permission modes:

```sql
CHECK("agent_channel"."type" IN ('telegram','feishu','qq','wechat','discord','slack'))
CHECK("agent_channel"."permission_mode" IS NULL OR
      "agent_channel"."permission_mode" IN ('default','acceptEdits','bypassPermissions','plan'))
```

> [!IMPORTANT]
> The V2 data refactor introduced a migration engine (`src/main/data/migration/v2/`) with a startup **migration gate**. It validates foreign keys after migrating (`PRAGMA foreign_keys`) and aborts on integrity violations (`Foreign key check failed: N violation(s)`). The `__new_*` shadow tables in migrations are the standard SQLite table-rebuild pattern.

---

## Subsystem Reference

A tour of the major main-process subsystems (`src/main/`):

| Subsystem | Path | Responsibility |
|-----------|------|----------------|
| AI runtime | `ai/runtime`, `ai/streamManager`, `ai/provider` | Drive model calls, manage streaming lifecycle, build provider clients |
| Agents | `ai/agents`, `ai/agentSession`, `ai/skills` | Autonomous agent execution, sessions, skill loading |
| Claude Code | `ai/runtime/claudeCode`, `shared/ai/claudecode` | Agentic coding via the Claude Agent SDK |
| MCP | `ai/mcp` (`servers/browser`, `servers/filesystem`, `servers/claw`), `ai/tools` | MCP client + built-in MCP servers + tool adapters |
| Observability | `ai/observability` | OpenTelemetry tracing of AI/MCP calls (`@cherrystudio/mcp-trace`) |
| Channels | `ai/channels` (`adapters/telegram,feishu,qq,wechat,discord,slack`) | IM bot integrations |
| Knowledge | `services/knowledge`, `knowledge/embedjs`, `knowledge/reranker` | RAG indexing, retrieval, reranking strategies |
| File processing | `services/fileProcessing` (`processors/tesseract,paddleocr,mineru,doc2x,mistral,ovocr,system`) | OCR & document-to-markdown pipeline |
| Files | `services/file`, `services/FileStorage` | Sandboxed file storage and access |
| Web search | `services/webSearch` (`providers/api/*`) | Tavily/Exa/SearXNG/Bocha/Jina/Querit/MCP search |
| Backup | `services/LegacyBackupManager`, `services/nutstore` | WebDAV/S3/local/LAN/Nutstore backup |
| LAN transfer | `services/lanTransfer` | Direct device-to-device backup over LAN (bonjour discovery) |
| Protocol | `services/protocol` | `cherrystudio://` deep-link handling |
| Scheduler | `core/scheduler`, `core/job` | Scheduled tasks (croner) |
| Window mgmt | `core/window`, `MainWindowService` | BrowserWindow lifecycle, multi-window |
| Updates | `services/AppUpdaterService`, `VersionService` | electron-updater integration + segmented upgrades |
| Integrations | `services/ObsidianVaultService`, `CopilotService`, `VertexAiService`, `integration/cherryai` | Obsidian, GitHub Copilot, Vertex AI, CherryIN |
| OVMS | `services/OvmsManager` | OpenVINO Model Server (Windows + Intel only) |

The renderer (`src/renderer/`) mirrors these with pages (TanStack Router, file-based under `routes/`), a Redux Toolkit store (`store/`, 28 slices), hooks (`hooks/`, 73 files), and services (`services/`, 27 files).

---

## Requirements & Dependencies

### Runtime / build requirements

| Requirement | Value |
|-------------|-------|
| Node.js (build/dev) | `>=24.11.1` |
| Package manager | `pnpm@10.27.0` (workspaces) |
| Electron | `41.2.1` |
| electron-builder | `26.8.1` |
| electron-vite | `5.0.0` (Vite via `rolldown-vite@7.3.0`) |
| TypeScript | `~5.8.3` (type-checked with `tsgo` / native preview) |
| React | `19.x` |

### Key dependency families

| Area | Libraries |
|------|-----------|
| AI SDKs | `ai`, `@ai-sdk/openai|anthropic|google|google-vertex|amazon-bedrock|azure|cohere|groq|mistral|perplexity|xai|togetherai|huggingface`, `@openrouter/ai-sdk-provider`, `ollama-ai-provider-v2`, `voyage-ai-provider` |
| Agents/MCP | `@anthropic-ai/claude-agent-sdk`, `@modelcontextprotocol/sdk`, `@anthropic-ai/sdk`, `@cherrystudio/openai` |
| RAG | `@cherrystudio/embedjs*`, `@vectorstores/*`, `@libsql/client`, `@langchain/*` |
| OCR/docs | `tesseract.js`, `@napi-rs/system-ocr`, `sharp`, `officeparser`, `pdf-parse`, `pdf-lib`, `mammoth`/`word-extractor`, `epub`, `@e965/xlsx` |
| Editor/markdown | `@tiptap/*`, `react-markdown`, `remark-*`/`rehype-*`, `shiki`, `katex`, `mermaid` |
| Server | `express`, `cors`, `swagger-ui-express`, `swagger-jsdoc`, `express-validator`, `ws` |
| Data | `drizzle-orm`, `drizzle-kit`, `dexie`, `redux`/`@reduxjs/toolkit`, `redux-persist` |
| Channels | `grammy` (Telegram), `@larksuiteoapi/node-sdk` (Feishu) |
| Cloud | `@aws-sdk/client-s3`, `webdav`, `google-auth-library`, `@google/genai` |

> [!NOTE]
> Several dependencies are **patched** (`pnpm.patchedDependencies`) — including `electron-updater`, `tesseract.js`, multiple `@ai-sdk/*` providers, `@langchain/*`, `libsql`, and `antd`. When upgrading these, re-validate the patches. Native modules (`selection-hook`, `sharp`, `@napi-rs/*`, `libsql`, `@anthropic-ai/claude-agent-sdk`) are platform-specific optional dependencies and are unpacked from the asar at runtime.

---

## Configuration Reference

Cherry Studio is configured almost entirely **through the UI**, which persists to the `preference` table via the `PreferenceService`. There is **no** user-editable config file or environment-variable-based runtime configuration for end users.

### Preference keys (operationally significant)

| Key | Controls |
|-----|----------|
| `feature.csaas.host` | API server bind host |
| `feature.csaas.port` | API server port |
| `feature.csaas.api_key` | API server authentication key |
| `app.spell_check.languages` | Spell-check languages |
| `app.zoom_factor` | UI zoom |
| Git Bash path (`ConfigKeys.GitBashPath`, `GitBashPathSource`) | Windows shell path (`manual`/`auto`) |

### Generation defaults (constants)

| Constant | Value |
|----------|-------|
| `DEFAULT_TEMPERATURE` | `1.0` |
| `DEFAULT_CONTEXTCOUNT` | `5` |
| `DEFAULT_MAX_TOKENS` | `8192` |
| `DEFAULT_TIMEOUT` | `1800000` ms (30 min) |
| `DEFAULT_STREAM_OPTIONS_INCLUDE_USAGE` | `true` |

### Environment variables (build/dev and OS-derived only)

These are **not** product configuration knobs; they are read by build tooling or for OS path resolution:

| Variable | Used for |
|----------|----------|
| `NODE_ENV` | Vite build mode |
| `MAIN_VITE_CHERRYAI_CLIENT_SECRET` | CherryAI integration client secret (build-time) |
| `VITE_RENDERER_INTEGRATED_MODEL` | Renderer integrated-model build flag |
| `CLAUDE_CODE_GIT_BASH_PATH` | Git Bash discovery for Claude Code |
| `APPDATA`, `LOCALAPPDATA`, `XDG_CONFIG_HOME`, `HOME`/`USERPROFILE`, `PATH`, `SHELL` | OS path/shell resolution |
| `PORTABLE_EXECUTABLE_DIR` | Detect Windows portable build |
| `APPIMAGE` | Detect Linux AppImage |

### Application paths

The frozen path registry (`application.getPath(...)`) defines logical locations such as `app.root`, `app.userdata`, `app.install`, `cherry.config`, `feature.files.data`, `feature.notes.data`, `feature.agents.channels`, `app.session.cache`, and the logs directory. The `App_Info` IPC returns these to the renderer.

> [!WARNING]
> Changing the **data path** at runtime is incompatible with the frozen path registry; the code marks `App_SetAppDataPath` for a v2 redesign and the renderer currently triggers a relaunch after a path change. Treat data-path changes as "set, then restart."

---

## Security Model

Cherry Studio's security posture has several named layers. Administrators should understand each.

### 1. Process isolation (Electron)

Renderer windows run with Chromium's process isolation. The renderer reaches the main process **only** through the `contextBridge`-exposed preload API (`window.api`/`window.*`), never via direct Node integration in app windows. Web content (mini apps, webviews) runs in a separate session partition (`persist:webview`).

### 2. URL & navigation safety

External URL opening goes through `isSafeExternalUrl()` (rejecting untrusted schemes). Remote URL fetches enforce `remoteUrlSafety` checks: local/private addresses are blocked (`Unsafe remote url: local or private addresses are not allowed`), and credentials embedded in URLs are rejected (`credentials are not allowed`). Web-search results can be filtered by a blacklist match-pattern engine.

### 3. Filesystem sandboxing

File operations validate that source and destination paths are **absolute** and **inside allowed directories**, enforce a maximum recursion depth, and reject path-traversal (`Path is outside allowed directory`). Agent file access is further scoped by each agent's **accessible paths**. Path-resolver code rejects null bytes in entry ids/paths.

### 4. Secrets at rest

API keys and tokens are stored locally. An AES utility (`Aes_Encrypt`/`Aes_Decrypt` IPC) is available for encrypting sensitive values. OAuth flows (CherryIN, Copilot, Nutstore, Vertex AI) are handled by dedicated services; CherryIN OAuth validates the API host against an allowlist and requires a managed window.

### 5. API server authentication

The CSaaS server uses constant-time API-key comparison (`crypto.timingSafeEqual`) and refuses requests with missing or mismatched credentials. See [The Embedded API Server](#the-embedded-api-server-csaas).

### 6. Agent permission modes

Agent tool execution is gated by `permission_mode` (`default` / `acceptEdits` / `bypassPermissions` / `plan`), enforced both in the UI (tool-approval bridge) and recorded in the schema.

> [!CAUTION]
> The strongest operational risks are (1) running the **API server** on a non-loopback interface without a proxy, and (2) granting agents `bypassPermissions` with broad **accessible paths**. Both are admin choices, not defaults — review them in managed deployments.

### Reporting vulnerabilities

The repository includes a `SECURITY.md`; report security issues through the channel it specifies rather than public issues.

---

## Build & Distribution

### Build pipeline

```bash
# Install (pnpm workspaces)
pnpm install

# Type-check (node + web + aiCore projects in parallel via tsgo)
pnpm typecheck

# Full production build (generates OpenAPI spec, typechecks, builds with electron-vite)
pnpm build

# Package for a platform (electron-builder)
pnpm build:win        # NSIS + portable, x64 + arm64
pnpm build:mac        # dmg + zip, arm64 + x64
pnpm build:linux      # AppImage + deb + rpm, x64 + arm64
```

> [!NOTE]
> `pnpm build` runs `generate:openapi` first — the API server's OpenAPI spec is generated from JSDoc and committed (`src/main/apiServer/generated/openapi-spec.json`); CI enforces it is up to date (`openapi:check`).

### electron-builder configuration (`electron-builder.yml`)

| Setting | Value |
|---------|-------|
| `appId` | `com.cherryai.cherrystudio` |
| `productName` | `Cherry Studio` |
| Protocol scheme | `cherrystudio` |
| Windows targets | `nsis` (configurable install dir, desktop shortcut, not one-click) + `portable` |
| macOS targets | `dmg` + `zip`; entitlements for camera/microphone/Documents/Downloads; notarization via `scripts/notarize.js` |
| Linux targets | `AppImage` + `deb` + `rpm`; category `Utility`; registers `x-scheme-handler/cherrystudio` |
| Publish | `generic` provider at `https://releases.cherry-ai.com` |
| Electron mirror | `https://npmmirror.com/mirrors/electron/` |
| asarUnpack | `resources/**`, sharp binaries, the Claude Agent SDK native CLI (must live on disk to spawn) |
| extraResources | Drizzle migrations and the provider-registry data |

Build hooks: `beforePack`, `afterPack`, `afterSign` (notarize), and `artifactBuildCompleted` scripts customize packaging and signing (Windows signing via `scripts/win-sign.js`).

### Artifact naming

Artifacts follow `${productName}-${version}-${arch}-...`, e.g. `Cherry Studio-2.0.0-x64-setup.exe`, `Cherry Studio-2.0.0-arm64.dmg`, `Cherry Studio-2.0.0-x64.AppImage`.

---

## Updates & Upgrade Path

Cherry Studio uses **electron-updater** against the generic feed `https://releases.cherry-ai.com`, with GitHub and GitCode mirrors. Update routing is defined by **segments** in `config/app-upgrade-segments.json`:

| Segment | Type | Match | Meaning |
|---------|------|-------|---------|
| `gateway-v1.8.1` | legacy | exactly `1.8.1` | All users below v1.8.1 must upgrade to v1.8.1 first |
| `current-v1` | latest | `>=1.8.1 <2.0.0` | Latest v1.x (supports `latest`/`rc`/`beta` channels) |
| `gateway-v2` | breaking | exactly `2.0.0` | Required intermediate stop for entering v2.x (`minCompatible 1.9.0`) |
| `current-v2` | latest | `>=2.0.0 <3.0.0` | Latest v2.x (`latest`/`rc`/`beta` channels) |

> [!IMPORTANT]
> The gateway/breaking segments enforce a **staged upgrade** so the V2 data migration runs from a known-good baseline. Very old clients are routed through v1.8.1 and v2.0.0 before reaching the newest release. The `VersionService` records the current version on each launch to support this path.

The `electron-updater` dependency is patched; auto-update behavior differs by build flavor (AppImage and some Linux targets may rely on external/package-manager updates).

---

## Operations & Maintenance

### Where data lives

All state is under the resolved `userData` directory (platform-dependent: `%APPDATA%` on Windows, `~/Library/Application Support` on macOS, `~/.config` on Linux), including the SQLite database, knowledge-base vectors, files, notes, logs, and the session cache.

### Logs

Logging uses **winston** with daily rotation (`winston-daily-rotate-file`). The logs directory is returned by `App_Info` and surfaced in the UI; `loggerService.withContext(...)` tags log lines by subsystem (e.g., `ApiServer`, `IPC`, `MigrationEngine`).

### Cache management

`App_ClearCache` clears Chromium session caches (default + `persist:webview`) and temp files. `App_GetCacheSize` reports the session cache size.

### Backup operations

Backups can be scripted through the UI's destinations (WebDAV, S3, Nutstore, local, LAN). For team deployments, prefer **WebDAV** or **S3** with a scheduled cadence. LAN transfer accepts **ZIP only** with a size cap.

### Managed-deployment checklist

- [ ] Decide whether the **API server** should be enabled, and if so bind it to `127.0.0.1` with a strong key.
- [ ] Standardize provider configuration (consider the **Enterprise Edition** for centralized model management).
- [ ] Constrain agent **accessible paths** and avoid `bypassPermissions` by policy.
- [ ] Choose and test a **backup destination**.
- [ ] Pin an update channel (`latest` vs `rc`/`beta`) appropriate to your risk tolerance.
- [ ] On Windows, pre-provision **Git Bash** if users will use agentic coding.

---

## Testing & Quality Gates

| Tool | Purpose |
|------|---------|
| **Vitest** | Unit/integration tests, split by project: `main`, `renderer`, `aiCore`, `ui`, `shared`, `provider-registry`, `vectorstores`, `scripts` |
| **Playwright** | End-to-end tests (`tests/e2e/`) with page objects |
| **oxlint** + **ESLint** | Linting (oxlint first, then ESLint with project config) |
| **Biome** | Formatting + lint (`pnpm format`) |
| **tsgo** | Fast type-checking (node/web projects) |
| i18n checks | `i18n:check`, `i18n:hardcoded:strict`, `i18n:unused` (no hardcoded strings) |
| OpenAPI check | `openapi:check` (generated spec must be current) |
| Skills check | `skills:check` |
| Changesets | Versioning/release of workspace packages |

CI runs in two phases: `ci:basic-check` (lint, format, typecheck, i18n, hardcoded-strings, openapi, skills) and `ci:test-check` (all Vitest projects). Bundle size is analyzed via `rollup-plugin-visualizer` (`analyze:renderer`/`analyze:main`).

> [!NOTE]
> Database migration tests use a harness that asserts `PRAGMA foreign_keys = 1` and runs `PRAGMA integrity_check`, failing fast if the test DB isn't healthy.

---

## Development Setup

```bash
# Prerequisites: Node >= 24.11.1, pnpm 10.27.0, (Windows) Git Bash
pnpm install

# Generate the API OpenAPI spec + run the dev app (electron-vite)
pnpm dev

# Dev with renderer hot-reload watcher
pnpm dev:watch

# Run tests
pnpm test                 # all (silent)
pnpm test:main            # main-process project only
pnpm test:e2e             # Playwright

# Lint & format
pnpm lint
pnpm format

# Generate a DB migration after schema changes
pnpm db:migrations:generate
```

The project uses Git hooks (`prek`) and a blame-ignore-revs file (configured by `pnpm prepare`). Contributions follow the branching strategy and Contributor Co-creation Program described in the repo's `README.md` and `CONTRIBUTING.md`.

> [!NOTE]
> The repo includes a `v2-refactor-temp/` directory of transitional tooling and docs from the V1→V2 data refactor. Treat it as scaffolding, not stable API.

---

## Things That Do **Not** Exist

Stating absent surfaces prevents fabricated configuration and false expectations:

- **No central server** in the Community Edition. The only HTTP server is the optional, locally-bound CSaaS API server. (Centralized server/admin backend is an **Enterprise Edition** feature.)
- **No user-facing config file or `.env`** for runtime configuration. Settings live in the SQLite `preference` table, edited through the UI. Environment variables are build/dev or OS-path only.
- **No cloud account required** to use the app. There is no Cherry Studio login for core functionality; provider keys are yours.
- **No telemetry-by-key configuration** exposed as a product knob (crash telemetry is initialized internally; there is no user "API config" for it).
- **No multi-tenant/RBAC** in Community Edition (that's Enterprise).
- **No webhook receiver** — Cherry Studio is an outbound client and a local API server, not a webhook endpoint host.
- **OVMS** (OpenVINO Model Server) integration exists **only on Windows + Intel CPUs**; the `Ovms_*` IPC is gated accordingly.

---

## Troubleshooting (Operator Reference)

Errors below are taken from the source; messages may include interpolated values.

### `MigrationEngine not initialized — call initialize() first` / `Foreign key check failed: N violation(s)`

**Context:** V2 data migration. The engine wasn't initialized, or post-migration FK validation failed.
**Action:** Let the staged upgrade run from a clean baseline (v1.8.1 → v2.0.0). If a migration aborts on FK violations, restore from a pre-upgrade backup and retry; report the violation sample to the maintainers.

### `Provider '<id>' not found or not enabled` (API server)

**Context:** A request used a `providerId::modelId` whose provider isn't enabled, or the provider lacks an OpenAI-compatible endpoint.
**Action:** Enable the provider and configure its endpoint; verify the `providerId::modelId` format.

### `Provider '<id>' has no OpenAI-compatible chat-completions endpoint configured`

**Context:** Calling `/v1/chat/completions` for a provider that only exposes an Anthropic endpoint.
**Action:** Use `/v1/messages` for Anthropic-style providers, or configure an OpenAI-compatible endpoint for that provider.

### `Unsafe remote url: local or private addresses are not allowed`

**Context:** A fetch (web search/knowledge import) targeted a private/loopback address.
**Action:** Expected protection. Use a public URL, or load local content through the file pipeline instead.

### `File too large. Maximum size is ...` / `Only ZIP files are supported` (LAN transfer)

**Context:** LAN transfer constraints.
**Action:** Use WebDAV/S3 for large backups; LAN transfer is for ZIP archives under the size cap.

### `Git Bash not found` (Windows, agentic features)

**Context:** Code/Agent shell features need Git Bash.
**Action:** Install Git for Windows; the path auto-discovers or can be set via `System_SetGitBashPath`.

### `VertexAI requires iam-gcp auth configuration.`

**Context:** Vertex AI selected without service-account/IAM config.
**Action:** Provide a valid GCP service-account configuration in the provider settings.

### API server returns `401`/`403`

**Context:** Missing/empty credentials (`401`) or no/incorrect server key (`403`).
**Action:** Set `feature.csaas.api_key` (API Server settings) and send it as `x-api-key` or `Authorization: Bearer`.

### `Path is outside allowed directory` / `Permission denied`

**Context:** A file operation escaped allowed directories or lacked OS permissions.
**Action:** Keep operations within agent **accessible paths**; verify OS permissions on the target.

---

## Quick Reference

### Identity & platform

| Item | Value |
|------|-------|
| App ID | `com.cherryai.cherrystudio` |
| Product name | `Cherry Studio` |
| URL scheme | `cherrystudio://` |
| License | AGPL-3.0 (Community); commercial/Enterprise available |
| Platforms | Windows, macOS, Linux (`x64`, `arm64`) |
| Update feed | `https://releases.cherry-ai.com` (+ GitHub/GitCode mirrors) |

### API server endpoints (auth: `x-api-key` or `Authorization: Bearer`)

| Method | Path |
|--------|------|
| `GET` | `/health`, `/`, `/api-docs`, `/api-docs.json` (public) |
| `POST` | `/v1/chat/completions` (OpenAI) |
| `POST` | `/v1/messages`, `/{provider}/v1/messages` (Anthropic) |
| `GET` | `/v1/mcps`, `/v1/mcps/{server_id}` |
| `*` | `/v1/knowledge-bases`, `/v1/knowledge-bases/search`, `/v1/claw/{agentId}/claw-mcp` |

### Key commands

| Command | Purpose |
|---------|---------|
| `pnpm dev` | Run the app in development |
| `pnpm build` | Production build (OpenAPI + typecheck + electron-vite) |
| `pnpm build:win|mac|linux` | Package installers |
| `pnpm test` / `pnpm test:e2e` | Vitest / Playwright |
| `pnpm lint` / `pnpm format` | Lint / format |
| `pnpm db:migrations:generate` | Generate Drizzle migration |

### Channel types & agent permission modes

- Channels: `telegram`, `feishu`, `qq`, `wechat`, `discord`, `slack`
- Permission modes: `default`, `acceptEdits`, `bypassPermissions`, `plan`

---

_Generated June 2026 from CherryHQ/cherry-studio at 9751cfb. 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._
