packages/shared was never a real pnpm workspace package (no package.json); it was referenced only through the @shared TypeScript path alias. Relocate it under src/ via git mv (143 files, detected as pure renames).
Repoint the @shared alias and include globs to src/shared across electron.vite.config.ts, tsconfig.{json,node,web}.json and vitest.config.ts; update scripts/check-custom-exts.ts, scripts/update-languages.ts, the eslint.config.mjs generated-file globs, the data-classify generator output targets, .github/CODEOWNERS path rules, and CLAUDE.md/docs/source-comment references.
The @shared alias name is unchanged, so all 1403 @shared/* import sites resolve without modification. Verified with typecheck:node, typecheck:web and the full test suite (700 files, 9739 tests passing).
13 KiB
Boot Config System Overview
The Boot Config system provides synchronous, file-based configuration for settings that must be available before the application lifecycle takes over — before the database, before PreferenceService, before any lifecycle phase runs.
Purpose
BootConfigService handles data that:
- Must be loaded synchronously at process startup (before any async initialization)
- Affects process-level behavior that cannot be changed at runtime (e.g., Chromium flags)
- Cannot wait for database initialization (SQLite is not yet available)
- Needs to be read before the lifecycle system's
BeforeReadyphase
Typical examples: disabling hardware acceleration, setting user data directory paths, configuring Chromium command-line switches.
Boot Timing
┌──────────────────────────────────────────────────────────────────────┐
│ App Startup Sequence │
│ │
│ 1. BootConfig load ← Sync read of boot-config.json │
│ (bootConfigService) Only data system available here │
│ │ │
│ 2. Bootstrap ← App data directory setup │
│ │ │
│ 3. application.bootstrap() │
│ │ │
│ ├── Background phase (fire-and-forget) │
│ │ │
│ ├── Promise.all([ │
│ │ BeforeReady phase, ← DB init, PreferenceService, │
│ │ app.whenReady() CacheService, DataApiService │
│ │ ]) │
│ │ │
│ └── WhenReady phase ← Window creation, IPC handlers │
│ │
└──────────────────────────────────────────────────────────────────────┘
BootConfig is the only data system available at stage 1 — before the lifecycle system takes over. The BeforeReady phase and app.whenReady() run in parallel (via Promise.all); once both complete, WhenReady services start. From the BeforeReady phase onward, boot config values are also accessible through PreferenceService via the BootConfig.* prefix.
Key Characteristics
Synchronous Loading
- Reads
boot-config.jsonviafs.readFileSyncon module import - No async, no promises — values available immediately at the top of
src/main/index.ts - If the file is missing (first launch), falls back to defaults
- If the file is corrupt, records the error — the app should not continue with corrupted boot config
Flat Key-Value Structure
Keys follow the same naming convention as preferences: namespace.key_name
| Key | Type | Default | Description |
|---|---|---|---|
app.disable_hardware_acceleration |
boolean |
false |
Disable Chromium hardware acceleration |
Atomic File Writes
- Writes to a temp file first, then renames to
boot-config.json - Prevents corruption from crashes during write
Debounced Saving
- Writes are debounced by 500ms to coalesce rapid changes
flush()forces an immediate write (used on app quit)
Error Handling
- Tracks load errors:
parse_error(invalid JSON) orread_error(file inaccessible) - Missing file (first launch): falls back to
DefaultBootConfig— this is normal - Corrupt file: records the error via
loadError. The app should checkhasLoadError()and abort startup rather than continue with potentially incorrect configuration - Errors can be inspected via
hasLoadError()/getLoadError()/clearLoadError()
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Early Boot (before lifecycle) │
│ │
│ src/main/index.ts │
│ │ │
│ ▼ │
│ bootConfigService.get('app.disable_hardware_acceleration') │
│ │ ▲ │
│ ▼ │ │
│ ┌───────────────────┴──────────────────┐ │
│ │ BootConfigService │ │
│ │ - Sync load on import │ │
│ │ - In-memory config map │◄──── boot-config.json │
│ │ - Debounced save │ (~/.cherrystudio/)│
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ After Lifecycle Starts │
│ │
│ Renderer Main Process │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ usePreference │ IPC │ PreferenceService │ │
│ │ ('BootConfig.*') │─────────────►│ detects BootConfig.* │ │
│ └──────────────────┘ │ prefix, routes to │ │
│ │ bootConfigService │ │
│ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
BootConfig also carries data migrated from v1's ~/.cherrystudio/config/config.json file (see BootConfigMigrator's file source). The app.user_data_path key holds the custom user data directory mapping that the v1 file stored under appDataPath. Long-term, BootConfig will fully replace the legacy config/config.json — the follow-up PR will rewire initAppDataDir() to read app.user_data_path from BootConfig instead of parsing the legacy file directly.
Access Convention
| Context | API | Note |
|---|---|---|
| Early boot (before lifecycle) | bootConfigService.get(key) / .set(key, value) |
Only option — DB and lifecycle not yet available |
| Lifecycle services (Main) | preferenceService.get('BootConfig.*') |
Standardized; enables cross-window sync |
| Renderer (React components) | usePreference('BootConfig.*') |
Same as regular preference usage |
Rule: Once the lifecycle is running, always access boot config values through PreferenceService. Direct bootConfigService usage is reserved exclusively for early boot code.
For detailed usage of usePreference and preferenceService, see Preference Usage Guide.
BootConfig vs Preference
| Aspect | BootConfig | Preference |
|---|---|---|
| Loading | Synchronous, before lifecycle takes over | Async, at BeforeReady phase (parallel with app.whenReady()) |
| Storage | boot-config.json (filesystem) |
SQLite database |
| Availability | From process start | After DB initialization |
| Use case | Process-level flags, Chromium switches | User-modifiable app settings |
| Cross-window sync | Via PreferenceService delegation | Native |
| Key count | Minimal (process-level only) | 158+ keys |
PreferenceService Integration
Boot config keys are accessible through PreferenceService using the BootConfig. prefix:
preferenceService.get('BootConfig.app.disable_hardware_acceleration')routes tobootConfigService.get('app.disable_hardware_acceleration')- The
BootConfigPreferenceKeysmapped type automatically adds theBootConfig.prefix to all boot config keys - The
UnifiedPreferenceTypemerges both preference and boot config type spaces, providing full type safety - Changes made through PreferenceService are broadcast to all windows
Utility functions in src/shared/data/preference/preferenceUtils.ts:
| Function | Purpose |
|---|---|
isBootConfigKey(key) |
Check if a key has the BootConfig. prefix |
toBootConfigKey(key) |
Strip BootConfig. prefix to get the underlying key |
getDefaultValue(key) |
Unified default lookup for both preference and boot config keys |
File Storage
- Path:
~/.cherrystudio/boot-config.json(intentionally outsideuserData) - Format: Flat JSON object, pretty-printed (2-space indent)
Why outside
userData? Boot config must be readable before the app data directory is determined. Storing it underuserDatawould create a chicken-and-egg problem: the file that decides where data lives cannot itself live inside that data. Placing it under~/.cherrystudio/keeps it stable across changes toappDataPathand ensures it is always available at process start, beforeinitAppDataDir()runs.
{
"app.disable_hardware_acceleration": false,
"app.user_data_path": {
"/Applications/Cherry Studio.app/Contents/MacOS/Cherry Studio": "/Volumes/External/CherryData"
}
}
app.user_data_path is a Record<executablePath, dataPath> keyed by the executable path — same-machine multiple installations (stable / dev / portable) can each have their own user data directory, matching the semantic of v1's appDataPath array.
Related Source Code
| File | Purpose |
|---|---|
src/main/data/bootConfig/BootConfigService.ts |
Core service — sync load, debounced save, change notifications |
src/main/data/bootConfig/types.ts |
BootConfigLoadError type definition |
src/shared/data/bootConfig/bootConfigSchemas.ts |
BootConfigSchema interface and DefaultBootConfig |
src/shared/data/bootConfig/bootConfigTypes.ts |
BootConfigKey, BootConfigPreferenceKeys mapped type |
src/shared/data/preference/preferenceUtils.ts |
BootConfig.* prefix routing utilities |
src/main/data/PreferenceService.ts |
Routes BootConfig.* keys to bootConfigService |
src/main/index.ts |
Early boot usage (first import, hardware acceleration check) |
Related Documentation
- Boot Config Schema Guide - Adding new boot config keys
- Preference Overview - PreferenceService architecture
- Preference Usage Guide -
usePreferencehook and service API