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
- Executive Summary
- System Architecture
- Process Model & Startup
- The IPC Surface
- The Embedded API Server (CSaaS)
- Provider & Model System
- Data Layer & Schema
- Subsystem Reference
- Requirements & Dependencies
- Configuration Reference
- Security Model
- Build & Distribution
- Updates & Upgrade Path
- Operations & Maintenance
- Testing & Quality Gates
- Development Setup
- Things That Do Not Exist
- Troubleshooting (Operator 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.
System Architecture
Cherry Studio follows the standard Electron three-tier split, with a strict service architecture in the main process.
┌──────────────────────────────────────────────────────────────────────┐
│ 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:
- BootConfig load —
@main/data/bootConfigis imported first (before any other import) so theuserDatapath is configured before Electron reads it. - 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).
- V2 migration gate —
runV2MigrationGate(). If a major data migration is required, a migration window takes over and normal startup is skipped ('handled'). - App identity —
electronApp.setAppUserModelId('com.cherryai.cherrystudio')(Windows notification/taskbar identity). - Lifecycle bootstrap —
application.registerAll(serviceList)thenapplication.bootstrap()runs in parallel withapp.whenReady(). TheBeforeReadylifecycle stage overlaps Electron readiness. - Version recording —
versionService.recordCurrentVersion()for upgrade-path tracking. - IPC registration —
registerIpc()wires the (currently monolithic) IPC handler table.
Note
Service initialization is structured: a
ServiceInitErrorduringbootstrap()is handled internally (dialog → exit/relaunch). The top-levelcatchinindex.tsis only a safety net for unexpected escapes, which callapplication.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-serviceipcHandle/ipcOninside 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_Websitedoes not blindly open URLs — it callsisSafeExternalUrl(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. ATODOflags 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/completionswithstream:truereturnstext/event-stream, writesdata: <chunk>\n\nframes, ends withdata: [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
modelfield is parsed asproviderId::modelId(parseUniqueModelId). The provider is resolved viaproviderService.getByProviderId, its API key is fetched with key rotation (getRotatedApiKey), and the correct endpoint base URL is selected fromprovider.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 toapi.openai.com. - Error mapping: errors are normalized to OpenAI/Anthropic-style error bodies with
type/code, trusting the SDK's structured.statusrather than regex-matching messages (a genuine 500 is not remapped to 502). - Claw MCP: each MCP session gets its own
ClawServer+StreamableHTTPServerTransportpair (the MCP SDK Server supports one transport at a time); sessions have a 30s init timeout and are keyed bymcp-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 to127.0.0.1for 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 toresources/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'sExtensionRegistry/ProviderExtensionallow 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:
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 |
| 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) — includingelectron-updater,tesseract.js, multiple@ai-sdk/*providers,@langchain/*,libsql, andantd. 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_SetAppDataPathfor 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.
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
bypassPermissionswith 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
# 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 buildrunsgenerate:openapifirst — 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
VersionServicerecords 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.1with a strong key. - [ ] Standardize provider configuration (consider the Enterprise Edition for centralized model management).
- [ ] Constrain agent accessible paths and avoid
bypassPermissionsby policy. - [ ] Choose and test a backup destination.
- [ ] Pin an update channel (
latestvsrc/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 = 1and runsPRAGMA integrity_check, failing fast if the test DB isn't healthy.
Development Setup
# 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
.envfor runtime configuration. Settings live in the SQLitepreferencetable, 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 |
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