# Boot Config Schema Guide This guide explains how to add new boot config keys and covers the V1-to-V2 data migration pipeline. ## When to Use BootConfig BootConfig is for a **very narrow** set of configuration items. Before adding a key here, ask: | Question | If Yes | If No | |----------|--------|-------| | Must it load before the lifecycle system takes over? | BootConfig | Preference | | Does it affect process-level behavior (Chromium flags, data directory)? | BootConfig | Preference | | Can it wait for the `BeforeReady` lifecycle phase? | Preference | BootConfig | | Can it be changed at runtime without restart? | Preference | BootConfig | **Rule of thumb:** If the setting can wait until the lifecycle's `BeforeReady` phase, it belongs in Preference. BootConfig is only for settings that must be available before the lifecycle system even starts. Keep it minimal. ## Key Naming Conventions Boot config keys follow the same naming convention as preferences. ### Format `namespace.key_name` — at least 2 segments separated by dots. **Rules:** - Lowercase letters, numbers, and underscores only - Pattern: `/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/` - Use dots for hierarchy, underscores for multi-word names ### Examples | Valid | Invalid | Reason | |-------|---------|--------| | `app.disable_hardware_acceleration` | `disableHardwareAcceleration` | Missing dot separator | | `app.user_data_path` | `App.userDataPath` | Uppercase, camelCase | | `chromium.gpu_compositing` | `gpu` | Single segment | ## Adding a New Boot Config Key ### Step 1: Add to Schema Interface **File:** `src/shared/data/bootConfig/bootConfigSchemas.ts` The schema interface and defaults live side by side. The current shape looks like this — follow the same pattern for any new key (matching naming convention, matching entry in `DefaultBootConfig`): ```typescript export interface BootConfigSchema { 'app.disable_hardware_acceleration': boolean 'app.user_data_path': Record } export const DefaultBootConfig: BootConfigSchema = { 'app.disable_hardware_acceleration': false, 'app.user_data_path': {} } ``` > **Note:** If this file is currently auto-generated by the V1-to-V2 migration pipeline, add your key outside the `=== AUTO-GENERATED CONTENT ===` markers, or coordinate with the migration toolchain (see [V1 to V2 Data Migration](#v1-to-v2-data-migration) below). ### Step 2: Add Custom Types (if needed) **File:** `src/shared/data/bootConfig/bootConfigTypes.ts` For simple types (boolean, string, number), no changes needed — the type is inferred from the schema. For custom types, define them alongside `BootConfigKey`: ```typescript export type BootConfigKey = keyof BootConfigSchema // Custom types if needed export type GpuMode = 'auto' | 'disabled' | 'software' ``` ### Step 3: Use in Early Boot Code (if needed) Only for settings that must take effect before lifecycle: **File:** `src/main/index.ts` ```typescript import { bootConfigService } from '@main/data/bootConfig' // Apply before app.whenReady() if (bootConfigService.get('app.disable_hardware_acceleration')) { app.disableHardwareAcceleration() } ``` ### Step 4: Access from Renderer / Lifecycle Services No additional wiring needed. The `BootConfigPreferenceKeys` mapped type automatically adds the `BootConfig.` prefix, making the key available through the unified preference API: ```typescript // Renderer — works immediately after adding to schema const [disableHardwareAcceleration, setDisableHardwareAcceleration] = usePreference( 'BootConfig.app.disable_hardware_acceleration' ) // Main process lifecycle service const disableHardwareAcceleration = preferenceService.get('BootConfig.app.disable_hardware_acceleration') ``` For detailed usage of `usePreference`, see [Preference Usage Guide](./preference-usage.md). ## V1 to V2 Data Migration > This section covers the **migration toolchain** used to move legacy data from V1 (Redux/ElectronStore/Dexie) into the V2 boot config system. This is **not** the regular path for adding new keys. ### Overview The `v2-refactor-temp/tools/data-classify/` directory contains the code generation pipeline for migrating legacy data into V2 systems. `classification.json` is the single source of truth that classifies every legacy key into its target system (Preference, BootConfig, Cache, or DataApi). ### How It Works 1. Each item in `classification.json` with `"category": "bootConfig"` maps a legacy key to a boot config key 2. The generator reads these classifications and produces: - `src/shared/data/bootConfig/bootConfigSchemas.ts` — schema interface and defaults - `src/main/data/migration/v2/migrators/mappings/BootConfigMappings.ts` — legacy-to-new key mappings 3. At migration time, `BootConfigMigrator` reads values from legacy sources (Redux, ElectronStore, Dexie settings, localStorage, and the legacy home config file) and writes them to `bootConfigService` ### Migration Sources | Source | Accessor | Example | |--------|----------|---------| | Redux Store | `ReduxStateReader` with category + dot-path | `settings.disableHardwareAcceleration` | | ElectronStore | `ConfigManager.get(key)` | Direct key lookup | | Dexie settings | Key-value table | Direct key lookup | | localStorage | `localStorage.getItem(key)` | Direct key lookup | | Legacy home config file | `LegacyHomeConfigReader` | `~/.cherrystudio/config/config.json` (`appDataPath` field only) | > **Config-file source mappings are manually maintained.** The `data-classify` toolchain's `classification.json` doesn't model config-file sources yet. In two places, a small hand-maintained list complements the classification-driven pipeline: > > - **Schema keys**: `MANUAL_BOOT_CONFIG_ITEMS` at the top of `v2-refactor-temp/tools/data-classify/scripts/generate-boot-config.js` — these items are merged with the classification-derived items and emitted into `bootConfigSchemas.ts` as part of the normal auto-generated output. The resulting schema file is fully auto-generated (no manual sections). > - **Mappings**: inline `configFileMappings` inside `BootConfigMigrator.loadMigrationItems()` — a small `ReadonlyArray<{ originalKey: string; targetKey: BootConfigKey }>` whose `BootConfigKey` annotation is the regen safety net: if the schema loses `app.user_data_path`, this array fails to compile at its declaration site. > > To add a new config-file-sourced key in the future: add an entry to `MANUAL_BOOT_CONFIG_ITEMS` in the generator, add the matching entry to `BootConfigMigrator.loadMigrationItems()`'s `configFileMappings`, and run `npm run generate`. ### Adding a Migration Mapping To migrate a legacy key to boot config: 1. Add or update the item in `classification.json`: ```json { "originalKey": "disableHardwareAcceleration", "source": "redux", "category": "bootConfig", "status": "classified", "targetKey": "app.disable_hardware_acceleration", "targetType": "boolean", "defaultValue": false, "reduxCategory": "settings" } ``` 2. Regenerate mappings: ```bash cd v2-refactor-temp/tools/data-classify && npm run generate ``` 3. Verify the generated output in `BootConfigMappings.ts` ### Current Mappings | Legacy Source | Legacy Key | Target Key | |---------------|-----------|------------| | Redux (`settings`) | `disableHardwareAcceleration` | `app.disable_hardware_acceleration` | | Config file (`~/.cherrystudio/config/config.json`) | `appDataPath` | `app.user_data_path` | #### Known Limitation: AppImage / Windows Portable Executable Path The v1 `~/.cherrystudio/config/config.json` stores `appDataPath` as an array of `{ executablePath, dataPath }` entries keyed by executable path. On AppImage Linux builds and Windows portable builds, `src/main/utils/init.ts:51-60` writes a **special** `executablePath` that differs from `app.getPath('exe')`: - AppImage: `path.dirname(process.env.APPIMAGE) + '/cherry-studio.appimage'` - Windows portable: `process.env.PORTABLE_EXECUTABLE_DIR + '/cherry-studio-portable.exe'` `LegacyHomeConfigReader` does NOT reproduce this normalization — array entries are migrated verbatim with their original `executablePath` key, and the legacy-string fallback uses the raw `app.getPath('exe')`. This is harmless in the current PR because nothing yet reads `app.user_data_path`. **But the follow-up PR that rewires `initAppDataDir()` to consume it MUST normalize the exe path using the same logic in `src/main/utils/init.ts:51-60`, otherwise migrated records under AppImage/portable will never match the lookup key.** ## File Structure | File | Purpose | |------|---------| | `src/shared/data/bootConfig/bootConfigSchemas.ts` | Schema interface and default values | | `src/shared/data/bootConfig/bootConfigTypes.ts` | `BootConfigKey` type, `BootConfigPreferenceKeys` mapped type | | `src/main/data/bootConfig/BootConfigService.ts` | Service implementation | | `src/main/data/bootConfig/types.ts` | `BootConfigLoadError` type | | `v2-refactor-temp/tools/data-classify/data/classification.json` | Migration source of truth | | `v2-refactor-temp/tools/data-classify/scripts/generate-boot-config.js` | Schema generator (migration) | | `src/main/data/migration/v2/migrators/BootConfigMigrator.ts` | Migration executor | | `src/main/data/migration/v2/migrators/mappings/BootConfigMappings.ts` | Auto-generated migration mappings | ## Best Practices 1. **Keep BootConfig minimal** — most settings belong in Preference. Only use BootConfig for settings that must load before the lifecycle system takes over. 2. **Provide sensible defaults** — BootConfigService uses defaults on first launch (missing file). A missing default means the key is unusable on first launch. 3. **Follow naming conventions** — use the same `namespace.key_name` pattern as preferences for consistency. 4. **Process-level settings require restart** — document this in the UI when exposing boot config settings to users. ## Related Documentation - [Boot Config Overview](./boot-config-overview.md) - Architecture and timing - [Preference Schema Guide](./preference-schema-guide.md) - Adding preference keys (non-boot) - [Preference Usage Guide](./preference-usage.md) - `usePreference` hook and service API - [V2 Migration Guide](./v2-migration-guide.md) - Full migration system documentation