Boot Config Schema Guide

July 20, 2026 · View on GitHub

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:

QuestionIf YesIf No
Must it load before the lifecycle system takes over?BootConfigPreference
Does it affect process-level behavior (Chromium flags, data directory)?BootConfigPreference
Can it wait for the BeforeReady lifecycle phase?PreferenceBootConfig
Can it be changed at runtime without restart?PreferenceBootConfig

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

ValidInvalidReason
app.disable_hardware_accelerationdisableHardwareAccelerationMissing dot separator
app.user_data_pathApp.userDataPathUppercase, camelCase
chromium.gpu_compositinggpuSingle segment

Adding a New Boot Config Key

Step 1: Add to Schema

File: src/shared/data/bootConfig/bootConfigSchemas.ts (auto-generated — do not edit by hand)

The zod schema is the single source of truth: the BootConfigSchema type is inferred from it, and BootConfigService validates file/set values against it at runtime. The current shape:

export const bootConfigSchema = z.object({
  'app.disable_hardware_acceleration': z.boolean(),
  'app.user_data_path': z.record(z.string(), z.string())
})

export type BootConfigSchema = z.infer<typeof bootConfigSchema>

export const DefaultBootConfig: BootConfigSchema = {
  'app.disable_hardware_acceleration': false,
  'app.user_data_path': {}
}

This file is fully auto-generated. To add a key, edit classification.json (simple boolean/string/number types map to zod automatically) or MANUAL_BOOT_CONFIG_ITEMS in the generator (complex types need an explicit zodType expression), then regenerate — see 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:

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/main.ts

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:

// 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')

Internal temp.* keys are the exception. Keys under the temp.* prefix are main-process-internal transient state and are deliberately excluded from the unified preference API — not in UnifiedPreferenceType, not reachable via usePreference, and rejected at the PreferenceService IPC boundary. Access them only via bootConfigService directly (at any phase), and use bootConfigService.onChange() for notification. See Internal temp.* namespace.

For detailed usage of usePreference, see Preference Usage Guide.

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 — zod schema, inferred BootConfigSchema type, 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

SourceAccessorExample
Redux StoreReduxStateReader with category + dot-pathsettings.disableHardwareAcceleration
ElectronStoreConfigManager.get(key)Direct key lookup
Dexie settingsKey-value tableDirect key lookup
localStoragelocalStorage.getItem(key)Direct key lookup
Legacy home config fileLegacyHomeConfigReader~/.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). Each manual item needs an explicit zodType expression string (classification-derived simple types map to zod automatically); the generator aborts on items it cannot map.
  • 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:
{
  "originalKey": "disableHardwareAcceleration",
  "source": "redux",
  "category": "bootConfig",
  "status": "classified",
  "targetKey": "app.disable_hardware_acceleration",
  "targetType": "boolean",
  "defaultValue": false,
  "reduxCategory": "settings"
}
  1. Regenerate mappings:
cd v2-refactor-temp/tools/data-classify && npm run generate
  1. Verify the generated output in BootConfigMappings.ts

Current Mappings

Legacy SourceLegacy KeyTarget Key
Redux (settings)disableHardwareAccelerationapp.disable_hardware_acceleration
Config file (~/.cherrystudio/config/config.json)appDataPathapp.user_data_path

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'

Consumers of app.user_data_path must use getNormalizedExecutablePath() from src/main/core/preboot/userDataLocation.ts so migrated records under AppImage/portable match the lookup key.

File Structure

FilePurpose
src/shared/data/bootConfig/bootConfigSchemas.tsZod value schema (single source), inferred BootConfigSchema type, and default values
src/shared/data/bootConfig/bootConfigTypes.tsBootConfigKey, Public/InternalBootConfigKey, BootConfigPreferenceKeys mapped type
src/main/data/bootConfig/BootConfigService.tsService implementation
src/main/data/bootConfig/types.tsBootConfigLoadError type
v2-refactor-temp/tools/data-classify/data/classification.jsonMigration source of truth
v2-refactor-temp/tools/data-classify/scripts/generate-boot-config.jsSchema generator (migration)
src/main/data/migration/v2/migrators/BootConfigMigrator.tsMigration executor
src/main/data/migration/v2/migrators/mappings/BootConfigMappings.tsAuto-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.