# 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 `BeforeReady` phase

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.json` via `fs.readFileSync` on 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) or `read_error` (file inaccessible)
- **Missing file** (first launch): falls back to `DefaultBootConfig` — this is normal
- **Corrupt file**: records the error via `loadError`. The app should check `hasLoadError()` 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](./preference-usage.md).

## 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 to `bootConfigService.get('app.disable_hardware_acceleration')`
- The `BootConfigPreferenceKeys` mapped type automatically adds the `BootConfig.` prefix to all boot config keys
- The `UnifiedPreferenceType` merges 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 outside `userData`)
- **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 under `userData` would 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 to `appDataPath` and ensures it is always available at process start, before `initAppDataDir()` runs.

```json
{
  "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](./boot-config-schema-guide.md) - Adding new boot config keys
- [Preference Overview](./preference-overview.md) - PreferenceService architecture
- [Preference Usage Guide](./preference-usage.md) - `usePreference` hook and service API
