Files
qiming/qimingclaw/crates/agent-electron-client/docs/architecture/device-id.md

150 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
version: 1.1
last-updated: 2026-03-05
status: implemented
---
# Device ID 生成方案
## 概述
为 Electron 客户端生成稳定、安全的设备唯一标识符deviceId用于设备识别、授权绑定等场景。
---
## 方案
### 核心公式
```
deviceId = SHA-256(machineId + appSalt)
```
- `machineId`:操作系统级机器标识(通过 `node-machine-id` 获取原始值)
- `appSalt`:应用固定盐值,避免不同应用使用相同 machineId 产生碰撞
### 实现
```typescript
import { machineIdSync } from "node-machine-id";
import { createHash } from "crypto";
import * as os from "os";
import log from "electron-log";
const APP_SALT = "qiming-agent";
let cachedDeviceId: string | null = null;
export function getDeviceId(): string {
if (cachedDeviceId) return cachedDeviceId;
let raw: string;
try {
raw = machineIdSync(true);
} catch (e) {
log.warn("[DeviceId] Failed to read machineId, using hostname fallback:", e);
raw = os.hostname();
}
cachedDeviceId = createHash("sha256")
.update(raw + APP_SALT)
.digest("hex");
log.info(`[DeviceId] ${cachedDeviceId}`);
return cachedDeviceId;
}
```
### 各平台 machineId 数据源
| 平台 | 数据源 | 说明 |
|------|--------|------|
| **macOS** | `ioreg``IOPlatformUUID` | 硬件 UUID无需管理员权限 |
| **Windows** | 注册表 `HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid` | 系统安装时生成 |
| **Linux** | `/var/lib/dbus/machine-id` | systemd 机器 ID |
### 依赖
| 包名 | 版本 | 说明 |
|------|------|------|
| `node-machine-id` | ^1.1.12 | 跨平台机器 ID 读取,零 native 依赖 |
`crypto` 为 Node.js 内置模块,无需额外安装。
---
## 设计要点
### 1. 稳定性
- 同一台机器每次调用返回相同值
- 进程内缓存,避免重复读取系统命令
### 2. 安全性
- SHA-256 单向 hash不暴露原始 machineId
- 加入应用盐值,防止跨应用关联
### 3. 唯一性
- machineId 由操作系统保证唯一
- 加盐 hash 后不同应用产生不同 deviceId
### 4. 位置
- 实现在 **main process**`src/main/services/` 下)
- renderer 通过 IPC + preload bridge 获取
---
## 架构集成
```
Main Process
├── src/main/services/system/deviceId.ts # 实现 + 缓存 + 日志
├── src/main/services/system/index.ts # re-export
├── src/main/main.ts # 启动时调用并输出日志
└── src/main/ipc/appHandlers.ts # IPC handler
Preload
└── src/preload/index.ts # contextBridge 暴露
Renderer
└── window.electronAPI.app.getDeviceId() # 调用
```
### IPC 通道
```typescript
// main (appHandlers.ts)
ipcMain.handle("app:getDeviceId", () => getDeviceId());
// preload (index.ts)
app: {
getDeviceId: () => ipcRenderer.invoke("app:getDeviceId"),
}
```
---
## 特性对比(为何选此方案)
| 方案 | 稳定性 | 安全性 | 依赖 | 备注 |
|------|--------|--------|------|------|
| **hash(machineId + salt)** | 高 | 高 | 1 个 npm 包 | **选用** |
| 纯 machineId | 高 | 低(暴露原始值) | 1 个 npm 包 | 不推荐 |
| hw-fingerprint硬件指纹 | 中(换硬件会变) | 高 | 重依赖 systeminformation | 过重 |
| 本地持久化 UUID | 低(删文件会变) | 中 | 无 | 不绑定设备 |
| hash(machineId + randomUUID) | 无(每次不同) | - | - | 错误方案 |
---
## 注意事项
1. **容器/镜像环境**:基于镜像的环境可能共享相同 machine-idLinux 下可用 `dbus-uuidgen` 重新生成
2. **Windows 系统更新**MachineGuid 在重大系统更新后极少数情况下可能变化
3. **macOS 权限**IOPlatformUUID 无需额外权限,区别于 IOPlatformSerialNumber新版 macOS 受限)
---
*参考: 此方案为 SaaS 客户端常用的设备标识模式*