Settings Persistence
Relevant source files
The following files were used as context for generating this wiki page:
The Settings Persistence system in Keynobi manages the lifecycle of application configurations, ensuring that user preferences, project history, and environment paths remain consistent across sessions. The system utilizes a bridge between a SolidJS frontend state and a Rust backend, persisting data to a JSON file located in the user's home directory at ~/.keynobi/settings.json.
The architecture follows an "Atomic Write" philosophy, where settings are written to a temporary file before being renamed to the final destination to prevent corruption during crashes. Furthermore, the system is designed for forward compatibility; every field in the Rust models uses default serialization, allowing the application to handle missing or new configuration keys gracefully without breaking existing setup files.
Sources: src-tauri/src/models/settings.rs:32-34, README.md:395-397, src/stores/settings.store.ts:1-50
Architecture and Data Flow
The settings system operates through three distinct layers: the UI Layer (SolidJS components), the State Layer (SolidJS stores), and the Persistence Layer (Rust backend).
Frontend State Management
On the frontend, settings are managed via a SolidJS store (settingsState). This store is initialized with a comprehensive set of defaults and provides functions for granular updates. To prevent excessive disk I/O, updates are debounced; when a setting is changed via updateSetting or setAppSetting, a 500ms timer is started. If no further changes occur, the store invokes the Tauri IPC command save_settings.
Sources: src/stores/settings.store.ts:51-85
Backend Persistence and Safety
The Rust backend defines the source of truth for the settings structure using the AppSettings struct. It handles the actual file system operations. The project uses the ts-rs crate to automatically generate TypeScript bindings from these Rust models, ensuring type safety across the IPC boundary.
Key safety features include:
- Atomic Writes: The backend performs a "write-to-temp then rename" operation to ensure that a crash mid-save cannot corrupt the
settings.jsonfile. - Corruption Recovery: If the frontend detects a
settings:corruptedevent emitted by the backend, it notifies the user that settings have been reset to defaults.
Sources: README.md:395-397, src-tauri/src/models/settings.rs:32-52, src/stores/settings.store.ts:110-120
Communication Flow
The following diagram illustrates the lifecycle of a settings update from the user interface to the physical disk.
Data Models and Configuration
Settings are categorized into logical sections, ranging from UI appearance to advanced system limits.
AppSettings Schema
The AppSettings structure is the root container for all persisted data.
| Field | Type | Description |
|---|---|---|
appearance | AppearanceSettings | UI-specific settings like font size. |
search | SearchSettings | Limits and context for search operations. |
android | AndroidSettings | Environment paths (e.g., SDK path). |
mcp | McpSettings | Model Context Protocol server configuration. |
onboardingCompleted | boolean | Flag indicating if the setup wizard was finished. |
recentProjects | ProjectEntry[] | Registry of up to 20 recently opened projects. |
Sources: src/bindings/AppSettings.ts:14-35, src-tauri/src/models/settings.rs:32-52
Project Registry Persistence
A significant portion of the settings file is dedicated to the recentProjects registry. Each project is assigned a deterministic hex ID derived from its canonical path. This registry tracks per-project metadata so the application can restore the user's specific state upon reopening a directory.
pub struct ProjectEntry {
pub id: String,
pub path: String,
pub name: String,
pub last_opened: String,
pub pinned: bool,
pub last_build_variant: Option<String>,
pub last_device: Option<String>,
}
Sources: src-tauri/src/models/settings.rs:6-25, src-tauri/src/commands/file_system.rs:16-20
Implementation Details
IPC API Endpoints
The frontend communicates with the Rust backend through a defined set of IPC wrappers.
| Function | IPC Command | Description |
|---|---|---|
getSettings() | get_settings | Fetches the current configuration from disk. |
saveSettings(settings) | save_settings | Persists the provided settings object. |
resetSettingsToDefaults() | reset_settings | Reverts the config file to factory defaults. |
updateProjectMeta(id, ...) | update_project_meta | Updates variant/device selections for a specific project. |
Sources: src/lib/tauri-api.ts:80-100, src-tauri/src/commands/file_system.rs:290-310
Initialization and Recovery
When the application starts, loadSettings() is called. If the settings file is missing (e.g., on first launch), the application silently continues using the hardcoded DEFAULT_SETTINGS. If the file is present but unreadable, the backend triggers a reset and notifies the frontend via the settings:corrupted event.
Sources: src/stores/settings.store.ts:87-105, src/stores/settings.store.ts:110-120
Build and Log Rotation
The settings system also governs the retention of log files. Configuration options allow users to define how many days build logs and application logs should be kept before the backend's rotation logic purges them.
build: {
autoInstallOnBuild: true,
buildLogRetentionDays: 7,
buildLogMaxFolderMb: 100,
}
Sources: src/stores/settings.store.ts:36-40, src-tauri/src/models/settings.rs:135-145
Conclusion
The Settings Persistence system provides a robust backbone for Keynobi's stateful features. By combining SolidJS's reactive stores with Rust's safe file handling and automated type binding generation, the system ensures that user configurations are handled with high performance and data integrity.
Sources: README.md:400-405, AGENTS.md:55-60