desktop-storage-electron
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseElectron Storage & Credentials
Electron存储与凭据管理
Quick Guide: Usefor typed JSON preferences (small key-value config with schema validation, migrations, and file watching). Useelectron-storefor structured/relational data or anything beyond simple key-value (synchronous, WAL mode, transactions). Usebetter-sqlite3for encrypting secrets like tokens and API keys via the OS keychain -- it replaces the deprecatedsafeStorage. All persistent data belongs underkeytar. Never store secrets in plain JSON files.app.getPath("userData")
<critical_requirements>
快速指南: 使用存储带类型的JSON偏好设置(支持模式验证、数据迁移和文件监听的小型键值配置)。使用electron-store存储结构化/关系型数据或超出简单键值对范畴的数据(同步操作、WAL模式、事务支持)。使用better-sqlite3通过操作系统密钥链加密令牌和API密钥等机密信息——它已取代被弃用的safeStorage。所有持久化数据都应存储在keytar路径下。绝不要在纯JSON文件中存储机密信息。app.getPath("userData")
<critical_requirements>
CRITICAL: Before Using This Skill
重要提示:使用本技能前须知
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,, named constants)import type
(You MUST use / for secrets -- never store tokens, API keys, or passwords in plain text or in electron-store without OS-level encryption)
safeStorage.encryptString()safeStorage.decryptString()(You MUST store all persistent data under -- never write to the app installation directory, which is replaced on updates)
app.getPath("userData")(You MUST enable WAL mode () when using better-sqlite3 -- it prevents readers from blocking writers and avoids SQLITE_BUSY errors in multi-window apps)
PRAGMA journal_mode = WAL(You MUST call before encrypting -- it returns false before the app event and on some Linux configurations)
safeStorage.isEncryptionAvailable()ready(You MUST rebuild better-sqlite3 for Electron's Node.js version using -- mismatched native bindings crash the app)
@electron/rebuild</critical_requirements>
Auto-detection: electron-store, better-sqlite3, safeStorage, app.getPath, userData, encryptString, decryptString, isEncryptionAvailable, lowdb, JSONFilePreset, persistent storage, credential storage, keytar replacement, electron config, electron preferences, electron database
When to use:
- Persisting user preferences and app configuration
- Storing structured or relational data locally
- Encrypting tokens, API keys, or other secrets
- Choosing between storage solutions for an Electron app
- Migrating stored data between app versions
- Working with standard directories
app.getPath()
When NOT to use:
- Choosing a UI framework or styling for the renderer (separate skill)
- IPC communication patterns between main and renderer (separate concern)
- Packaging and distribution concerns (separate concern)
- Server-side or cloud storage
Key patterns covered:
- electron-store: typed config, schema validation, migrations, encryption, watching
- better-sqlite3: WAL mode, prepared statements, transactions, native module rebuild
- safeStorage: OS keychain encryption for secrets, replacing keytar
- lowdb: lightweight JSON database for medium-complexity data
- Storage path conventions using
app.getPath() - Credential storage best practices
<philosophy>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名、命名导出、导入顺序、、命名常量)import type
(你必须使用 / 存储机密信息——绝不要以明文形式存储令牌、API密钥或密码,也不要在未经过操作系统级加密的情况下将其存入electron-store)
safeStorage.encryptString()safeStorage.decryptString()(你必须将所有持久化数据存储在路径下——绝不要写入应用安装目录,该目录会在更新时被替换)
app.getPath("userData")(使用better-sqlite3时必须启用WAL模式()——它可防止读取操作阻塞写入操作,避免多窗口应用中出现SQLITE_BUSY错误)
PRAGMA journal_mode = WAL(加密前必须调用——在应用事件触发前以及部分Linux配置下,它会返回false)
safeStorage.isEncryptionAvailable()ready(必须使用针对Electron的Node.js版本重新构建better-sqlite3——不匹配的原生绑定会导致应用崩溃)
@electron/rebuild</critical_requirements>
自动检测关键词: electron-store, better-sqlite3, safeStorage, app.getPath, userData, encryptString, decryptString, isEncryptionAvailable, lowdb, JSONFilePreset, persistent storage, credential storage, keytar replacement, electron config, electron preferences, electron database
适用场景:
- 持久化用户偏好设置和应用配置
- 本地存储结构化或关系型数据
- 加密令牌、API密钥或其他机密信息
- 为Electron应用选择存储方案
- 在应用版本间迁移存储的数据
- 使用标准目录
app.getPath()
不适用场景:
- 为渲染进程选择UI框架或样式(属于独立技能范畴)
- 主进程与渲染进程间的IPC通信模式(属于独立关注点)
- 打包与分发相关问题(属于独立关注点)
- 服务器端或云存储
涵盖的核心模式:
- electron-store:带类型的配置、模式验证、数据迁移、加密、监听
- better-sqlite3:WAL模式、预编译语句、事务、原生模块重构
- safeStorage:基于操作系统密钥链的机密信息加密、替代keytar
- lowdb:适用于中等复杂度数据的轻量级JSON数据库
- 使用的存储路径约定
app.getPath() - 凭据存储最佳实践
<philosophy>
Philosophy
设计理念
Electron apps have access to the full filesystem but should store data in OS-designated locations. The right storage solution depends on data shape and sensitivity:
Preferences and small config (theme, window bounds, feature flags): writes a single JSON file atomically. It is read and written in full on every change, so it is only appropriate for small data (under ~1MB).
electron-storeStructured or queryable data (chat history, project metadata, analytics): provides a synchronous SQLite database with ACID transactions. It handles concurrent reads via WAL mode and scales to gigabytes.
better-sqlite3Secrets (OAuth tokens, API keys, passwords): uses the OS keychain (macOS Keychain, Windows DPAPI, Linux secret service) to encrypt strings. The encrypted buffer can be stored in electron-store or a file -- only your app can decrypt it on the same machine and user account.
safeStorageMedium-complexity JSON data (todo lists, small document stores): provides a file-backed JavaScript object with native array methods. Simpler than SQLite for JSON-shaped data that does not need relational queries.
lowdbKey principle: Storage runs in the main process. Renderers request data via IPC. Never give renderers direct filesystem or database access.
</philosophy>
<patterns>
Electron应用可访问完整文件系统,但应将数据存储在操作系统指定的位置。合适的存储方案取决于数据形态和敏感度:
偏好设置与小型配置(主题、窗口尺寸、功能开关):以原子方式写入单个JSON文件。每次变更时都会完整读写整个文件,因此仅适用于小型数据(约1MB以下)。
electron-store结构化或可查询数据(聊天记录、项目元数据、分析数据):提供支持ACID事务的同步SQLite数据库。它通过WAL模式处理并发读取,可扩展至千兆字节级数据。
better-sqlite3机密信息(OAuth令牌、API密钥、密码):使用操作系统密钥链(macOS Keychain、Windows DPAPI、Linux秘密服务)加密字符串。加密后的缓冲区可存储在electron-store或文件中——只有同一台机器上的同一用户账户下的你的应用才能解密。
safeStorage中等复杂度JSON数据(待办事项列表、小型文档存储):提供基于文件的JavaScript对象,支持原生数组方法。对于不需要关系查询的JSON形态数据,它比SQLite更简单。
lowdb核心原则: 存储操作运行在主进程中。渲染进程通过IPC请求数据。绝不要让渲染进程直接访问文件系统或数据库。
</philosophy>
<patterns>
Core Patterns
核心模式
Pattern 1: electron-store -- Typed Preferences
模式1:electron-store——带类型的偏好设置
Use for small key-value configuration that persists across sessions. Supports schema validation, defaults, and migrations.
typescript
import Store from "electron-store";
interface AppSettings {
theme: "light" | "dark" | "system";
windowBounds: { width: number; height: number; x?: number; y?: number };
recentFiles: string[];
fontSize: number;
}
const DEFAULT_WIDTH = 1200;
const DEFAULT_HEIGHT = 800;
const MIN_FONT_SIZE = 8;
const MAX_FONT_SIZE = 72;
const DEFAULT_FONT_SIZE = 14;
const store = new Store<AppSettings>({
defaults: {
theme: "system",
windowBounds: { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT },
recentFiles: [],
fontSize: DEFAULT_FONT_SIZE,
},
schema: {
fontSize: {
type: "number",
minimum: MIN_FONT_SIZE,
maximum: MAX_FONT_SIZE,
},
},
});Why good: Type-safe generic parameter ensures and are checked at compile time, named constants for all limits, schema rejects invalid values at write time
.get().set()See examples/core.md for migrations, file watching, dot-notation access, and renderer integration via IPC.
用于跨会话持久化的小型键值配置。支持模式验证、默认值和数据迁移。
typescript
import Store from "electron-store";
interface AppSettings {
theme: "light" | "dark" | "system";
windowBounds: { width: number; height: number; x?: number; y?: number };
recentFiles: string[];
fontSize: number;
}
const DEFAULT_WIDTH = 1200;
const DEFAULT_HEIGHT = 800;
const MIN_FONT_SIZE = 8;
const MAX_FONT_SIZE = 72;
const DEFAULT_FONT_SIZE = 14;
const store = new Store<AppSettings>({
defaults: {
theme: "system",
windowBounds: { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT },
recentFiles: [],
fontSize: DEFAULT_FONT_SIZE,
},
schema: {
fontSize: {
type: "number",
minimum: MIN_FONT_SIZE,
maximum: MAX_FONT_SIZE,
},
},
});优势: 类型安全的泛型参数确保和在编译时被校验,所有限制使用命名常量,模式在写入时拒绝无效值
.get().set()查看examples/core.md了解数据迁移、文件监听、点符号访问以及通过IPC与渲染进程集成的内容。
Pattern 2: better-sqlite3 -- Local Database
模式2:better-sqlite3——本地数据库
Use for structured data that benefits from queries, indexes, or transactions. Always enable WAL mode.
typescript
import Database from "better-sqlite3";
import { app } from "electron";
import path from "node:path";
const DB_FILE = "app-data.db";
const db = new Database(path.join(app.getPath("userData"), DB_FILE));
// Performance pragmas -- set once at connection open
db.pragma("journal_mode = WAL");
db.pragma("synchronous = NORMAL");
db.pragma("foreign_keys = ON");Why good: WAL mode allows concurrent reads during writes (essential for multi-window apps), balances safety and speed, foreign keys enforce referential integrity
synchronous = NORMALSee examples/sqlite.md for prepared statements, transactions, bulk inserts, and schema migrations.
用于受益于查询、索引或事务的结构化数据。始终启用WAL模式。
typescript
import Database from "better-sqlite3";
import { app } from "electron";
import path from "node:path";
const DB_FILE = "app-data.db";
const db = new Database(path.join(app.getPath("userData"), DB_FILE));
// 性能优化参数——连接打开时设置一次
db.pragma("journal_mode = WAL");
db.pragma("synchronous = NORMAL");
db.pragma("foreign_keys = ON");优势: WAL模式允许写入期间进行并发读取(对多窗口应用至关重要),平衡安全性与速度,外键强制引用完整性
synchronous = NORMAL查看examples/sqlite.md了解预编译语句、事务、批量插入和模式迁移的内容。
Pattern 3: safeStorage -- OS Keychain Encryption
模式3:safeStorage——操作系统密钥链加密
Use for secrets (tokens, API keys, passwords). The encrypted buffer is opaque -- only your app on the same machine and user account can decrypt it.
typescript
import { safeStorage, app } from "electron";
import Store from "electron-store";
const credentialStore = new Store<Record<string, string>>({
name: "credentials",
});
function saveSecret(key: string, plainText: string): void {
if (!safeStorage.isEncryptionAvailable()) {
throw new Error("OS encryption is not available");
}
const encrypted = safeStorage.encryptString(plainText);
credentialStore.set(key, encrypted.toString("base64"));
}
function loadSecret(key: string): string | null {
const stored = credentialStore.get(key);
if (!stored) return null;
const buffer = Buffer.from(stored, "base64");
return safeStorage.decryptString(buffer);
}Why good: Secrets are encrypted via the OS keychain before being persisted, base64 encoding allows storing the buffer in JSON, explicit availability check prevents crashes on unsupported systems
See examples/core.md for the full credential manager pattern and async API usage.
用于存储机密信息(令牌、API密钥、密码)。加密后的缓冲区是不透明的——只有同一台机器上的同一用户账户下的你的应用才能解密。
typescript
import { safeStorage, app } from "electron";
import Store from "electron-store";
const credentialStore = new Store<Record<string, string>>({
name: "credentials",
});
function saveSecret(key: string, plainText: string): void {
if (!safeStorage.isEncryptionAvailable()) {
throw new Error("操作系统加密不可用");
}
const encrypted = safeStorage.encryptString(plainText);
credentialStore.set(key, encrypted.toString("base64"));
}
function loadSecret(key: string): string | null {
const stored = credentialStore.get(key);
if (!stored) return null;
const buffer = Buffer.from(stored, "base64");
return safeStorage.decryptString(buffer);
}优势: 机密信息在持久化前通过操作系统密钥链加密,Base64编码允许将缓冲区存储在JSON中,显式的可用性检查可防止在不支持的系统上崩溃
查看examples/core.md了解完整的凭据管理器模式和异步API用法。
Pattern 4: Storage Path Conventions
模式4:存储路径约定
All persistent data belongs under . Use other paths for specific purposes.
app.getPath("userData")typescript
import { app } from "electron";
// User-specific persistent data (config, databases, credentials)
const userDataDir = app.getPath("userData");
// macOS: ~/Library/Application Support/<AppName>
// Windows: %APPDATA%/<AppName>
// Linux: ~/.config/<AppName>
// Temporary files (cache, downloads in progress)
const tempDir = app.getPath("temp");
// Log files
const logsDir = app.getPath("logs");
// User's documents, downloads, desktop (for file save dialogs)
const documentsDir = app.getPath("documents");
const downloadsDir = app.getPath("downloads");Key point: The directory survives app updates. The app installation directory does not -- writing data there causes data loss on update.
userData所有持久化数据都应存储在路径下。其他路径用于特定用途。
app.getPath("userData")typescript
import { app } from "electron";
// 用户专属持久化数据(配置、数据库、凭据)
const userDataDir = app.getPath("userData");
// macOS: ~/Library/Application Support/<AppName>
// Windows: %APPDATA%/<AppName>
// Linux: ~/.config/<AppName>
// 临时文件(缓存、正在下载的文件)
const tempDir = app.getPath("temp");
// 日志文件
const logsDir = app.getPath("logs");
// 用户的文档、下载、桌面目录(用于文件保存对话框)
const documentsDir = app.getPath("documents");
const downloadsDir = app.getPath("downloads");关键点: 目录在应用更新后会保留。应用安装目录则不会——写入该目录的数据会在更新时丢失。
userDataPattern 5: lowdb -- Lightweight JSON Database
模式5:lowdb——轻量级JSON数据库
Use when data is JSON-shaped but too complex for flat key-value (nested arrays, document collections) and does not need relational queries.
typescript
import { JSONFilePreset } from "lowdb/node";
import { app } from "electron";
import path from "node:path";
interface ProjectData {
projects: Array<{ id: string; name: string; lastOpened: string }>;
settings: { sortBy: "name" | "lastOpened" };
}
const DB_FILE = "projects.json";
const defaultData: ProjectData = {
projects: [],
settings: { sortBy: "lastOpened" },
};
const db = await JSONFilePreset<ProjectData>(
path.join(app.getPath("userData"), DB_FILE),
defaultData,
);
// Read
const recent = db.data.projects.toSorted((a, b) =>
b.lastOpened.localeCompare(a.lastOpened),
);
// Write (mutate then persist)
db.data.projects.push({
id: "abc",
name: "New Project",
lastOpened: new Date().toISOString(),
});
await db.write();Why good: Plain JavaScript data access (no query language), type-safe with generics, file I/O only on explicit call
.write()When to prefer SQLite instead: Data exceeds ~10MB, you need indexes or joins, you need concurrent write safety, or you need partial reads (lowdb loads the entire file into memory).
</patterns>
<decision_framework>
当数据为JSON形态但过于复杂不适合扁平键值对(嵌套数组、文档集合)且不需要关系查询时使用。
typescript
import { JSONFilePreset } from "lowdb/node";
import { app } from "electron";
import path from "node:path";
interface ProjectData {
projects: Array<{ id: string; name: string; lastOpened: string }>;
settings: { sortBy: "name" | "lastOpened" };
}
const DB_FILE = "projects.json";
const defaultData: ProjectData = {
projects: [],
settings: { sortBy: "lastOpened" },
};
const db = await JSONFilePreset<ProjectData>(
path.join(app.getPath("userData"), DB_FILE),
defaultData,
);
// 读取
const recent = db.data.projects.toSorted((a, b) =>
b.lastOpened.localeCompare(a.lastOpened),
);
// 写入(修改后持久化)
db.data.projects.push({
id: "abc",
name: "New Project",
lastOpened: new Date().toISOString(),
});
await db.write();优势: 纯JavaScript数据访问(无需查询语言),泛型支持类型安全,仅在显式调用时才进行文件I/O
.write()何时应选择SQLite: 数据超过约10MB、需要索引或连接、需要并发写入安全、或需要部分读取(lowdb会将整个文件加载到内存中)。
</patterns>
<decision_framework>
Decision Framework
决策框架
Choosing a Storage Solution
选择存储方案
What kind of data?
|
+-- User preferences / small config (theme, window size, feature flags)?
| +-- electron-store (JSON file, schema validation, migrations)
|
+-- Secrets (tokens, API keys, passwords)?
| +-- safeStorage + electron-store or file
| +-- Never plain text, never unencrypted electron-store
|
+-- Structured / relational data (records, queries, indexes)?
| +-- better-sqlite3 (WAL mode, transactions, scales to GB)
|
+-- JSON document collections (nested objects, no joins needed)?
| +-- Small (<10MB) -> lowdb
| +-- Large or concurrent writes -> better-sqlite3 with JSON columns
|
+-- Temporary / cache data?
| +-- app.getPath("temp") + regular file I/O
|
+-- Session-only state (lost on quit)?
+-- In-memory (no persistence needed)数据类型是什么?
|
+-- 用户偏好设置 / 小型配置(主题、窗口尺寸、功能开关)?
| +-- electron-store(JSON文件、模式验证、数据迁移)
|
+-- 机密信息(令牌、API密钥、密码)?
| +-- safeStorage + electron-store 或 文件
| +-- 绝不要明文存储,绝不要使用未加密的electron-store
|
+-- 结构化 / 关系型数据(记录、查询、索引)?
| +-- better-sqlite3(WAL模式、事务、可扩展至GB级)
|
+-- JSON文档集合(嵌套对象、无需连接)?
| +-- 小型数据(<10MB) -> lowdb
| +-- 大型数据或并发写入 -> 带JSON列的better-sqlite3
|
+-- 临时 / 缓存数据?
| +-- app.getPath("temp") + 常规文件I/O
|
+-- 仅会话状态(退出后丢失)?
+-- 内存存储(无需持久化)electron-store vs better-sqlite3
electron-store vs better-sqlite3
| Criteria | electron-store | better-sqlite3 |
|---|---|---|
| Data shape | Flat key-value, small JSON | Relational, structured records |
| Data size | < 1MB | Up to several GB |
| Query capability | Get by key, dot-notation | Full SQL, indexes, joins |
| Concurrent access | Single process only | WAL mode supports multi-window |
| Schema evolution | Migrations by semver | SQL ALTER TABLE / migration scripts |
| Setup complexity | Zero (pure JS) | Native module rebuild required |
| Best for | Preferences, feature flags | Chat history, project data, logs |
| 评估标准 | electron-store | better-sqlite3 |
|---|---|---|
| 数据形态 | 扁平键值对、小型JSON | 关系型、结构化记录 |
| 数据大小 | < 1MB | 可达数GB |
| 查询能力 | 按键获取、点符号访问 | 完整SQL、索引、连接 |
| 并发访问 | 仅支持单进程 | WAL模式支持多窗口 |
| 模式演进 | 基于语义化版本的数据迁移 | SQL ALTER TABLE / 迁移脚本 |
| 配置复杂度 | 零配置(纯JS) | 需要重构原生模块 |
| 最佳适用场景 | 偏好设置、功能开关 | 聊天记录、项目数据、日志 |
safeStorage vs electron-store encryptionKey
safeStorage vs electron-store encryptionKey
| Feature | safeStorage | electron-store encryptionKey |
|---|---|---|
| Security level | OS keychain (strong) | Obfuscation only (weak) |
| Key management | OS manages keys | Key embedded in source code |
| Use for secrets | Yes | No -- not actual encryption |
| Use for obfuscation | Overkill | Yes -- prevents casual file reading |
| Platform support | macOS, Windows, Linux (varies) | All platforms |
</decision_framework>
Detailed Resources:
- examples/core.md - electron-store setup, migrations, watching, safeStorage credential manager, lowdb, storage paths
- examples/sqlite.md - better-sqlite3 setup, WAL mode, prepared statements, transactions, migrations, native rebuild
- reference.md - API quick-reference tables, path directory map, security checklist
<red_flags>
| 特性 | safeStorage | electron-store encryptionKey |
|---|---|---|
| 安全级别 | 操作系统密钥链(高) | 仅混淆(低) |
| 密钥管理 | 操作系统管理密钥 | 密钥嵌入源代码中 |
| 是否适用于机密信息 | 是 | 否——并非真正的加密 |
| 是否适用于混淆 | 大材小用 | 是——防止随意读取文件 |
| 平台支持 | macOS、Windows、Linux(差异较大) | 所有平台 |
</decision_framework>
详细资源:
- examples/core.md - electron-store配置、数据迁移、监听、safeStorage凭据管理器、lowdb、存储路径
- examples/sqlite.md - better-sqlite3配置、WAL模式、预编译语句、事务、数据迁移、原生模块重构
- reference.md - API快速参考表、路径目录映射、安全检查清单
<red_flags>
RED FLAGS
警示事项
Critical Security Issues:
- Storing tokens, API keys, or passwords in plain text (electron-store without safeStorage)
- Using 's
electron-storeoption for actual secrets -- it is obfuscation, not encryption. The key is in your source code.encryptionKey - Writing persistent data to the app installation directory -- it is deleted on update
- Giving renderer processes direct filesystem or database access -- route through IPC
Architecture Issues:
- Not enabling WAL mode with better-sqlite3 -- causes errors when reading and writing concurrently
SQLITE_BUSY - Using better-sqlite3 without -- native module version mismatch crashes the app at startup
@electron/rebuild - Using electron-store for large datasets (>1MB) -- the entire file is read and written on every change
- Running database operations in the renderer process instead of the main process
- Not checking before encrypting -- crashes on Linux without a secret service
safeStorage.isEncryptionAvailable()
Common Mistakes:
- Calling methods before
safeStorage-- encryption is unavailable until the app is readyapp.whenReady() - Forgetting to on
db.close()-- risks WAL file corruptionbefore-quit - Using async functions inside transactions -- the transaction commits at the first
better-sqlite3, not at function endawait - Not using for better-sqlite3 in packaged builds -- the native binary fails to load from inside ASAR archives
asarUnpack - Storing objects directly in electron-store -- they serialize incorrectly. Convert to base64 strings.
Buffer
Gotchas & Edge Cases:
- requires Electron 30+ and is ESM-only (no CommonJS)
electron-store - on Windows (DPAPI) protects data per-user but not per-app -- another app running as the same user could theoretically decrypt
safeStorage - on Linux depends on the desktop environment's secret service (gnome-keyring, KWallet) -- falls back to plaintext if none is available
safeStorage - 's
electron-storevalidation uses JSON Schema draft-2020-12 via ajv -- not Zodschema - on
Object.groupByresult rows works but rows are plain objects with a null prototype -- usebetter-sqlite3notObject.hasOwn()hasOwnProperty
</red_flags>
<critical_reminders>
严重安全问题:
- 将令牌、API密钥或密码以明文形式存储(未使用safeStorage的electron-store)
- 使用的
electron-store选项存储实际机密信息——这只是混淆,并非加密。密钥会暴露在源代码中。encryptionKey - 将持久化数据写入应用安装目录——该目录会在更新时被删除
- 让渲染进程直接访问文件系统或数据库——应通过IPC路由
架构问题:
- 使用better-sqlite3时未启用WAL模式——并发读写时会导致错误
SQLITE_BUSY - 使用better-sqlite3时未使用——原生模块版本不匹配会导致应用启动时崩溃
@electron/rebuild - 使用electron-store存储大型数据集(>1MB)——每次变更都会完整读写整个文件
- 在渲染进程中运行数据库操作而非主进程
- 加密前未检查——在没有秘密服务的Linux系统上会崩溃
safeStorage.isEncryptionAvailable()
常见错误:
- 在之前调用
app.whenReady()方法——应用就绪前加密不可用safeStorage - 在事件中忘记调用
before-quit——可能导致WAL文件损坏db.close() - 在事务中使用异步函数——事务会在第一个
better-sqlite3时提交,而非函数结束时await - 打包构建时未为better-sqlite3使用——原生二进制文件无法从ASAR归档中加载
asarUnpack - 将对象直接存储在electron-store中——序列化会出错。需转换为Base64字符串。
Buffer
陷阱与边缘情况:
- 要求Electron 30+版本,且仅支持ESM(不支持CommonJS)
electron-store - Windows上的(DPAPI)按用户保护数据,但不按应用保护——同一用户运行的其他理论上可以解密
safeStorage - Linux上的依赖桌面环境的秘密服务(gnome-keyring、KWallet)——如果没有则会回退到明文存储
safeStorage - 的
electron-store验证通过ajv使用JSON Schema draft-2020-12——不支持Zodschema - 在结果行上使用
better-sqlite3可行,但行是带有null原型的普通对象——使用Object.groupBy而非Object.hasOwn()hasOwnProperty
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
重要提醒
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,, named constants)import type
(You MUST use / for secrets -- never store tokens, API keys, or passwords in plain text or in electron-store without OS-level encryption)
safeStorage.encryptString()safeStorage.decryptString()(You MUST store all persistent data under -- never write to the app installation directory, which is replaced on updates)
app.getPath("userData")(You MUST enable WAL mode () when using better-sqlite3 -- it prevents readers from blocking writers and avoids SQLITE_BUSY errors in multi-window apps)
PRAGMA journal_mode = WAL(You MUST call before encrypting -- it returns false before the app event and on some Linux configurations)
safeStorage.isEncryptionAvailable()ready(You MUST rebuild better-sqlite3 for Electron's Node.js version using -- mismatched native bindings crash the app)
@electron/rebuildFailure to follow these rules will cause data loss, security vulnerabilities, or application crashes.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名、命名导出、导入顺序、、命名常量)import type
(你必须使用 / 存储机密信息——绝不要以明文形式存储令牌、API密钥或密码,也不要在未经过操作系统级加密的情况下将其存入electron-store)
safeStorage.encryptString()safeStorage.decryptString()(你必须将所有持久化数据存储在路径下——绝不要写入应用安装目录,该目录会在更新时被替换)
app.getPath("userData")(使用better-sqlite3时必须启用WAL模式()——它可防止读取操作阻塞写入操作,避免多窗口应用中出现SQLITE_BUSY错误)
PRAGMA journal_mode = WAL(加密前必须调用——在应用事件触发前以及部分Linux配置下,它会返回false)
safeStorage.isEncryptionAvailable()ready(必须使用针对Electron的Node.js版本重新构建better-sqlite3——不匹配的原生绑定会导致应用崩溃)
@electron/rebuild不遵循这些规则会导致数据丢失、安全漏洞或应用崩溃。
</critical_reminders>