# 密钥管理

## 存储架构

SDK Core 使用两个独立的存储子系统管理敏感数据：

| 子系统 | 职责 | 存储内容 |
|--------|------|---------|
| **KeyStore** | 身份持久化 | 私钥（PEM）、证书（PEM）、身份元数据 |
| **SecretStore** | 敏感字段加密 | access_token、refresh_token 的加密保护 |

## KeyStore

### 存储结构

```
{aun_path}/
└── AIDs/
    └── {aid}/              # 如 my-agent.agentid.pub/
        ├── private/
        │   └── key.json    # ECC P-256 私钥（PEM 格式）
        ├── public/
        │   └── cert.pem    # X.509 证书（PEM 格式）
        └── tokens/
            └── meta.json   # 令牌和身份元数据
```

### KeyStore 接口

KeyStore 是一个 Protocol（接口），支持替换实现：

```python
class KeyStore(Protocol):
    def load_key_pair(self, aid: str) -> dict | None
    def save_key_pair(self, aid: str, key_pair: dict) -> None
    def load_cert(self, aid: str) -> str | None
    def save_cert(self, aid: str, cert_pem: str) -> None
    def load_metadata(self, aid: str) -> dict | None
    def save_metadata(self, aid: str, metadata: dict) -> None
    def load_identity(self, aid: str) -> dict | None
    def save_identity(self, aid: str, identity: dict) -> None
    def delete_identity(self, aid: str) -> None
```

### 内置实现

**FileKeyStore**（默认）：基于本地文件系统，适用于桌面端和服务端。

多 AID 支持：同一个 `aun_path` 下可存储多个 AID 的身份数据，各 AID 独立目录互不干扰。

## SecretStore

### 职责

SecretStore 负责加密存储敏感字段。当令牌保存到 `tokens/meta.json` 时，access_token 和 refresh_token 不是明文存储，而是经过 SecretStore 加密后存储。

### SecretStore 接口

```python
class SecretStore(Protocol):
    def protect(self, scope: str, name: str, plaintext: bytes) -> dict
    def reveal(self, scope: str, name: str, record: dict) -> bytes | None
    def clear(self, scope: str, name: str) -> None
```

### 内置实现

| 实现 | 平台 | 安全性 |
|------|------|--------|
| **DPAPISecretStore** | Windows | 使用 Windows DPAPI 加密，绑定用户账户 |
| **KeychainSecretStore** | macOS | 使用系统 Keychain，通过 `security` CLI |
| **LibsecretSecretStore** | Linux (有 GUI) | 使用 GNOME Keyring / KWallet，通过 `secret-tool` CLI |
| **FileSecretStore** | 所有平台 | 基于 AES-256-GCM 文件加密，密钥从 `encryption_seed` 或自动生成的 `.seed` 派生 |

SDK 通过 `create_default_secret_store` 自动选择：优先平台原生方案，不可用时降级到 `FileSecretStore`。**不会**降级到纯内存存储——私钥必须能跨进程重启持久化。

`save_key_pair` 会强制检查 `protect()` 返回的 `persisted` 字段，非 `True` 直接抛出 `RuntimeError`。

### 跨平台扩展

浏览器和移动端需要自定义实现：

| 平台 | 推荐方案 |
|------|---------|
| 浏览器 | IndexedDB + SubtleCrypto |
| iOS | Keychain Services |
| Android | Android Keystore |
| macOS | Keychain Services |
| Linux | libsecret / GNOME Keyring |

实现 `SecretStore` Protocol 后通过配置注入即可。

## 多实例隔离

`aun_path` 支持同目录多实例自动隔离：

- 第一个实例使用 `~/.aun/{app_name}`
- 后续实例自动使用 `~/.aun/{app_name}~2`、`~3` 等

避免多进程同时读写同一份密钥数据。

## 安全注意事项

- 私钥文件权限在 Unix 上自动设为 0o600（仅属主可读写）
- SecretStore 的 `protect()` 返回的加密记录可以安全写入磁盘，但不应跨用户/跨机器传输
- 令牌泄露的影响范围受 JWT 有效期限制，access_token 短期有效，refresh_token 可通过 `auth.revoke_token` 主动撤销

## 设备 ID

SDK 提供全局函数 `get_device_id()` 获取当前设备的稳定标识符：

```python
from aun_core import get_device_id

device_id = get_device_id()  # 如 "1f9cd0fc-03ef-471b-bf7d-be1bac9908cb"
```

- 存储在 `~/.aun/.device_id`（首次调用自动生成 UUID）
- 同一台机器上所有 SDK 实例共享同一个 device_id
- 可选传入 `aun_root` 参数指定存储路径
- 也可通过 `AUNConfig.device_id` 属性获取

**编写测试/demo 时必须使用 `get_device_id()` 生成 AID 前缀**，不要用随机值。这样同一台机器上多次运行可以复用已有的 AID 数据（私钥、证书、token 等）。
