内容
# 🔐 Merkle
> **上下文与凭证之间的门户。每次访问都留下了哈希值。**
一个 **本地优先的MCP秘密保险箱**,它为你的LLM提供不透明的句柄——永远不会是明文。每次揭露都需要人类批准,每次访问都被写入一个防篡改的哈希链中。100% 在你的机器上运行。
[](https://github.com/farchanjo/merkle)
[](LICENSE)
[](rust-toolchain.toml)
[](Cargo.toml)
[](docs/arch/integrations/claude-code-wiring.md)
[](#-development)
[-lightgrey)](#project-status)
---
## 什么是Merkle?
LLM编码代理非常出色——直到它们需要一个秘密。 当生产密码或SSH密钥进入模型的上下文窗口时,它就会被渗透到提供商,缓存到记录中,并且可能被记录下来。 你无法取消发送秘密。
**Merkle改变了协议。** 与其给模型一个秘密,不如给它一个不透明的句柄,比如`vault://acme/password/db-prod`。 模型可以使用该句柄——运行SSH命令,发出HTTPS调用,为子进程创建临时文件——但明文在本地守护进程中被引用,并且**从未跨越MCP传输**。 当确实需要显示明文时,**人类**会通过带外批准,而该行为会被永久记录。
三个保证支撑着它:
1. 🪪 **LLM持有句柄,而不是秘密。** 代理工具操作凭证;只有过滤后的结果返回。
2. 🙋 **人类批准每次揭露。** 授权通过模型无法到达的通道到达。
3. 🔗 **所有操作都记录在防篡改哈希链中。** 截断或重写它,验证就会失败。
并且它是 **100% 本地运行的** —— 一个长期运行的守护进程拥有你的密钥,一个SQLite数据库和审计链。没有任何东西会打电话回家。
> [!NOTE]
> **为什么叫"Merkle"?** 命名自 **Ralph Merkle** —— 公钥密码学的共同发明者(Merkle谜题,1974年)和 **Merkle树** 的发明者。 他的哈希链想法正是使Merkle的审计日志具有防篡改性的原因:每个条目都与其前一个条目的哈希绑定,因此任何编辑都会破坏后续的所有链接。
---
## 为什么使用Merkle?
| 没有保险箱 | 使用Merkle |
|---|---|
| 秘密粘贴到提示中会泄露给提供商、记录和日志 | LLM只会看到不透明的**句柄**;明文保留在本地守护进程中 |
| 模型可以“决定”读取秘密 | 每次显示都需要**操作员确认**,模型无法伪造 |
| 一个泄露的密钥 = 无声泄露 | **高敏感度**秘密需要**带外(OOB)**人类确认 |
| 没有记录谁访问了什么,什么时候 | 一个带有固定头的**BLAKE3哈希链**,使篡改可检测 |
| 云秘密管理器,网络往返,出口风险 | **本地优先**:SQLite + 操作系统密钥库,Unix套接字IPC,无网络依赖 |
| 沉重的运行时和边车 | 三个小的**静态Rust二进制文件** —— 代理,CLI,MCP服务器 |
---
## 工作原理
Merkle采用 **六边形架构** (DDD + 端口/适配器,参见 [ADR-0002](docs/arch/adr/0002-adopt-agent-plus-mcp-adapter-topology.md) / [ADR-0024](docs/arch/adr/0024-mcp-adapter-consumes-companion-socket-client.md))。 领域核心从不导入基础设施。
模型与 `ssh-agent` 和 `gpg-agent` 相同:**一个单一的长期运行的守护进程拥有所有危险状态**,所有其他部分都是与之通信的薄客户端 Unix 域套接字。
### 进程拓扑
```mermaid
flowchart LR
CC["🖥️ Claude Code<br/>(任意窗口)"]
OP["👤 操作员"]
MCP["merkle-mcp<br/>(stdio MCP服务器)"]
CLI["merkle<br/>(操作员CLI)"]
AG["merkle-agent<br/>(守护进程 / 组合根)"]
APP["merkle-application<br/>应用上下文"]
SQL[("SQLite<br/>vault.db")]
KC[("操作系统密钥库 /<br/>keystore.age")]
AUD["审计哈希链<br/>+ 固定头"]
CC -- "JSON-RPC 2.0 / stdio" --> MCP
OP --> CLI
MCP -- "HTTP/1.1 over Unix socket" --> AG
CLI -- "HTTP/1.1 over Unix socket" --> AG
AG --> APP
APP --> SQL
APP --> KC
APP --> AUD
```
> [!IMPORTANT]
> `merkle-mcp` 和 `merkle` **从不链接** 领域、存储或密钥库crate。 它们是 `merkle-companion-client` 的薄客户端,它通过Unix套接字使用HTTP/1.1进行通信——**单个入口端口**,称为 **伴侣套接字**。
### 六个边界上下文
```mermaid
flowchart LR
IdentityAndSealing["🔑 身份和密封"]
SecretStorage["🗄️ 秘密存储"]
AccessMediation["🚪 访问仲裁"]
AuditCompliance["📋 审计和合规"]
BackupRecovery["💾 备份和恢复"]
PolicyPermissions["📜 策略和权限"]
IdentityAndSealing -->|"解开DEKs"| SecretStorage
SecretStorage -->|"由"| AccessMediation
AccessMediation -->|"通过"| AuditCompliance
SecretStorage -->|"快照"| BackupRecovery
PolicyPermissions -->|"管理"| AccessMediation
PolicyPermissions -->|"管理"| SecretStorage
```
| 边界上下文 | 一句话描述 |
|---|---|
| **身份和密封** | 拥有密钥层次结构(MasterKey → VaultRootKey → NamespaceDEKs)和密封/未密封状态机 |
| **秘密存储** | 建模具有版本控制、类别、敏感度和每个blob的AEAD加密的`Secret`聚合体 |
| **访问仲裁** | 评估显示/代理请求;颁发`UseToken`s;管理OOB确认和伴侣设备 |
| **审计和合规** | 在SQLite中维护具有HMAC密钥的`PinnedHead`的防篡改BLAKE3哈希链 |
| **备份和恢复** | 类似Anacron的调度器;`age`加密的双收件人导出;灾难恢复恢复计划 |
| **策略和权限** | 命名空间策略、速率限制、显示策略、跨命名空间访问控制、安全配置文件 |
### 端口和适配器概览
- **一个入口(驱动)端口** —— **伴侣套接字**:Unix域套接字上的HTTP/1.1(~33个路径/35个操作),对等凭证身份验证(仅限相同UID),30秒每请求超时,8 MiB最大正文。
- **五个出口(被驱动)端口** —— `StoragePort`、`KeychainPort`、`CryptoPort`、`OobNotifierPort`、`ExternalServicesPort`,由SQLite、密钥库、加密、OOB和外部服务适配器实现。
查看 [工作空间布局](#workspace-layout) 中的完整crate映射。
---
## 揭示模型
这是Merkle安全故事的核心:**LLM永远无法授权自己的揭示。** 揭示由两个独立标志控制——模型无法控制这两个标志(参见 [ADR-0011](docs/arch/adr/0011-slash-only-reveal-with-oob-for-high-sensitivity.md))。
| 标志 | 设置者 | 通道 | 所需 |
|:---|:---|:---|:---|
| `slash_command` | Claude Code **客户端** | MCP会话上下文——*不是*工具参数 | **所有**敏感度 |
| `oob_ack` | **OOB通知器** | 独立的操作系统通道(桌面通知/TTY提示/ `localhost`确认) | `sensitivity=high`(**与** `slash_command` **结合使用**) |
在此基础上,`vault.reveal` 和 `vault.delete` 咨询第三个独立信号:**操作员确认**,在 MCP 的 `_meta` 字段中通过键 `dev.fapp.merkle/operator_confirmation` 传递(MERK-001)。LLM 仅控制工具 `arguments` 对象; `_meta` 由客户端传输写入。`VaultRevealInput` 和 `VaultDeleteInput` 故意 **没有** `operator_confirmation` 字段——因此模型尝试通过参数设置它会失败。
> [!WARNING]
> 提示注入无法伪造任何这些。`slash_command` 来自客户端的转折解析器,`oob_ack` 需要在 **单独的操作系统通道上进行物理操作**,而 `operator_confirmation` 必须在 `_meta` 中为 JSON 布尔值 `true`。 字符串 `"true"` 或任何其他形状都评估为 `false`。
### 高灵敏度揭示,端到端
```mermaid
sequenceDiagram
actor 操作员
participant CC as Claude Code 客户端
participant LLM as LLM 上下文
participant MCP as merkle-mcp (stdio)
participant Agent as merkle-agent (守护进程)
participant OOB as OOB 通知器
操作员->>CC: 类型 /merkle-reveal {handle} "原因"
CC->>LLM: 注入 slash_command + 操作员确认 (_meta)
LLM->>MCP: vault.reveal { handle, purpose }
Note over LLM,MCP: 参数中没有操作员确认
MCP->>Agent: POST /v1/reveal (slash_command=true)
Agent->>Agent: 验证 slash_command + 灵敏度=高
Agent->>OOB: 发出 OobChallenge (id, nonce, expires_at)
OOB->>操作员: 桌面通知 / TTY / localhost 页面
操作员->>OOB: 批准 -> Ed25519 签名(id, nonce)
OOB->>Agent: OobResolution { oob_ack=true, 签名 }
Agent->>Agent: 验证 Ed25519 + 随机数绑定 + 速率限制
Agent->>Agent: 追加审计条目(预提交)
Agent->>MCP: 明文
MCP->>LLM: 工具结果
```
对于低/中灵敏度,跳过OOB步骤;仅需要`slash_command`(和操作员确认)。如果需要OOB但尚未确认,代理返回 `{ "oob_pending": true, "oob_channel": ..., "expires_at": ..., "request_nonce": ... }`,调用者重新发出确认后。
> [!TIP]
> 对于代理操作,优先使用 **`vault.use`** 而不是 `vault.reveal`。它会生成一个256位、单次使用的 **UseToken** (默认TTL 60秒),由使用者工具(`vault.ssh.exec`、`vault.spawn`、`vault.write_tempfile`,...)在内部引用。明文永远不会出现在MCP响应中——模型只看到令牌和命令的输出。
---
## 密封和解密封生命周期
在冷启动时,保险箱是 **密封的** —— Vault Root Key(VRK)不在内存中,所有读写操作都被拒绝。 解密封加载并解密VRK;密封时将其清零。
```mermaid
stateDiagram-v2
[*] --> Sealed : 保险箱初始化 / 冷启动
Sealed --> Unsealing : merkle unseal (开始写入锁定)
Unsealing --> Sealed : 任何失败(密钥库 / AEAD / HMAC)—— 回滚
Unsealing --> Unsealed : 发布HMAC密钥 + 状态翻转 + 审计操作=Unseal
Unsealed --> ShuttingDown : merkle seal / SIGTERM
ShuttingDown --> Sealed : 排水完成(30秒硬超时)
note right of Unsealing
随机数(24) || 密文 AEAD解密
AAD = b"vault-root-key"
HMAC 密钥发布 **在** 状态翻转 **之前**
直接 Sealed → Unsealed 是 **拒绝** 的
end note
```
**解密封操作顺序:** 获取写锁 → 从密钥库读取 `vrk-master-v1` blob → AEAD解密它(AAD `b"vault-root-key"`)→ 派生审计HMAC密钥 `BLAKE3_keyed(vrk_bytes, b"merkle vault hmac key v1")` → **发布HMAC密钥,然后** 将状态翻转为 `Unsealed` → 追加 `op=Unseal` 条目。 读取和发布之间的任何失败都会回滚到 `Sealed`。
---
## 审计哈希链
每个对秘密的操作都记录在一个只追加的、防篡改链中——Ralph Merkle的哈希链,应用于您的审计日志。
```mermaid
flowchart LR
G["Genesis<br/>prev_hash = blake3:0000…0000<br/>hmac = keyed(hash ‖ id)"]
E1["Entry 1<br/>hash = BLAKE3(canonical ‖ prev)<br/>hmac = keyed(hash ‖ id)"]
E2["Entry 2<br/>hash = BLAKE3(canonical ‖ E1.hash)<br/>hmac = keyed(hash ‖ id)"]
PH[("PinnedHead<br/>head_hash ‖ head_seq ‖ entry_count<br/>MAC under same key")]
G --> E1 --> E2 --> PH
```
- **无密钥哈希:** `current_hash = BLAKE3(canonical_fields || prev_hash)` —— 编辑任何条目都会使每个后续哈希分发。
- **密钥HMAC:** `hmac = BLAKE3_keyed(hmac_key, current_hash || id_uuid)` —— 密钥由解密封时VRK字节派生,单独存储。
- **固定头** (单个SQLite行)绑定链提示,在同一密钥下,防止 *truncate-then-rebuild* 攻击。
- `ChainVerifier::verify_full` 报告精确的拒绝结果:`GenesisAnchorMissing`、`BrokenAtEntry`、`HmacMismatch`、`MissingHmac`、`HeadMacMismatch`、`TruncationDetected`、`HeadHashMismatch`等。
> [!NOTE]
> 仅哈希的 `Intact` 结果(当没有可用密钥时) **不是** 完全的防篡改——必须为 `hmac_checked` 为 **true** 以获得加密保证。审计条目和固定头保存在 **SQLite** 中(`pinned_head` 表);`audit.jsonl` / `audit_head.json` 配置路径是 [ADR-0009](docs/arch/adr/0009-merkle-style-audit-hash-chain.md) 修订案的漂移,而不是实时持久性路径。
---
## 加密和安全
### 原语
| 原语 | 板块 | 用途 |
|:---|:---|:---|
| XChaCha20-Poly1305 | `chacha20poly1305` | 每个blob的AEAD加密(24字节随机数,16字节标签) |
| BLAKE3哈希 + 密钥 | `blake3` | 审计哈希链完整性,HMAC替代品,KDF(XOF) |
| Argon2id(RFC 9106) | `argon2` | 密码KDF——地板`m_cost≥65536`,`t_cost≥3`,`p_cost≥1` |
| age / X25519 | `age` 0.11 | 双收件人备份加密 + 文件密钥库(scrypt `log_n`固定为18) |
| Ed25519 | `ed25519-dalek` | OOB解析签名,操作员证明JWT |
| X25519 ECIES | `x25519-dalek` | OOB挑战有效载荷加密([ADR-0019](docs/arch/adr/0019-ecies-encryption-for-oob-challenge-payload.md)) |
| OsRng | `rand` 0.10 | 所有密钥/随机数生成 — 熵门`assert_entropy_gate()` |
无OpenSSL。所有原语都是纯Rust(`openssl`/`openssl-sys`/`git2`在`deny.toml`中被禁止)。
### 密钥保证
- **归零:** `MasterKey`、`VaultRootKey`、`NamespaceDek`和`PrivateBlob`在删除时归零;`Debug`用 `[REDACTED]` 替换;`MasterKey::clone()` 故意 **不** 复制密钥字节。
- **SSRF / DNS重绑定防御:** `DestinationPolicy::strict()` 拒绝非HTTPS、环回、链路本地(包括 `169.254.x.x` 云元数据IP)、私有、CGNAT、组播和IPv6 ULA/链路本地 —— **在** 附加凭证之前。`ValidatingDnsResolver` 在连接时重新应用相同的检查,以关闭TOCTOU间隙,关闭时失败。
- **同UID凭证套接字身份验证(GAP-007):** 父目录强制 `0700`,绑定期间掩码 `0177`,套接字上显式 `chmod 0600`。macOS使用 `LOCAL_PEERCRED`,Linux使用 `SO_PEERCRED` + `/proc/<pid>/exe`;仅限同UID,通过 **每次** 请求验证。
- **安全配置文件( `[security] security_profile`**:
| 配置文件 | OOB要求 | 空闲超时 | mlock |
|:---|:---|:---|:---|
| `relaxed` | 无 | 可配置 | 可选 |
| `balanced` *(默认)* | `sensitivity=high` | 30分钟(1800秒) | 推荐 |
| `paranoid` | 所有灵敏度 | 5分钟 | 要求 |
- **备份不变式([ADR-0006](docs/arch/adr/0006-age-encryption-for-backups-and-recovery.md)):** 恰好两个不同的age收件人(MasterPubkey + RecoveryPublicKey),`secret_count > 0`,**加密后MAC**(BLAKE3密钥MAC,覆盖age密文)。文件名`merkle-bk-<utc-iso8601>.merkle.age`。
- **发布不变式:** `panic = "abort"` —— 没有超出中毒/不变式冲突状态的展开。永远不会改变。
完整的分析位于 [STRIDE威胁模型](docs/arch/threat-model/stride-analysis.md) 和 [SECURITY.md](SECURITY.md) 中。
---
## 🚀 快速入门
### 要求
- Rust **稳定版**,MSRV **1.85** (通过 `rust-toolchain.toml` 固定)
- Linux构建依赖:`libsqlite3-dev pkg-config libdbus-1-dev`(`libdbus-1-dev` 由 `keyring` Secret Service后端需要)
- macOS / Windows:仅需Rust工具链(SQLite已捆绑)
### 构建
```bash
git clone https://github.com/farchanjo/merkle.git
cd merkle
cargo build --release # 生成 target/release/{merkle, merkle-agent, merkle-mcp}
```
### 启动代理
守护进程必须在初始化之前运行 **——** `merkle init` 通过伴侣套接字发送初始化仪式,因此代理必须接收并执行它。
```bash
cargo run -p merkle-agent # 开发模式(或:只是代理)
```
在macOS上,您通常会将代理作为每个用户的LaunchAgent运行 - 请参见 [LaunchAgent设置](#-development) 和 `deploy/launchd/`中的资产。
> [!NOTE]
> **无头/文件后端设置。** 当操作系统密钥库不可用(CI,无头Linux或macOS上未签名的开发二进制文件)时,`auto`密钥库探测失败并回退到 **文件** 后端,除非设置了以下内容,否则它会拒绝启动:
> ```bash
> export MERKLE_KEYSTORE_PATH=~/.local/share/merkle/keystore.age
> export MERKLE_KEYSTORE_PASSPHRASE='<a-strong-passphrase>'
> export MERKLE_RECOVERY_RECIPIENT='age1…' # 真实的收件人,而不是占位符
> ```
### 初始化保险箱
```bash
merkle init # 生成保险箱并打印一次恢复密钥
```
> [!WARNING]
> **`merkle init` 仅打印一次恢复密钥。** 它是一个 `age1…` 收件人,可以在密钥库丢失时解密备份。 **请立即写下来并安全存储** —— 它永远不会被再次显示,也没有办法恢复它。稍后使用 `merkle verify-recovery-key` 验证它。
### 解密封并确认
```bash
merkle unseal # 将Vault Root Key加载到受保护的内存中
merkle status # 确认sealed=false
```
### 存储您的第一个秘密
```bash
merkle bind acme # 创建 + 绑定“acme”命名空间
# 中等敏感度API令牌
echo -n "ghp_supersecrettoken" | merkle put vault://acme/token/github-pat \
--sensitivity medium --tag project:my-app --tag role:ci \
--description "GitHub PAT for CI"
# 高敏感度密码(env:* 标签是必需的,--expose 禁止)
echo -n "hunter2" | merkle put vault://acme/password/db-prod \
--sensitivity high --tag env:prod --tag role:backend
```
### 连接 Claude 代码
将以下内容添加到 `~/.claude.json`:
```json
{
"mcpServers": {
"merkle": {
"command": "/usr/local/bin/merkle-mcp",
"args": []
}
}
}
```
然后重启 Claude Code(或运行 `/mcp restart merkle`)并通过询问 Claude 验证:
> *"调用 `vault.doctor` 并显示完整结果。"*
您应该看到 `"sealed": false`。连接成功。
| 可选环境变量 | 默认值 | 用途 |
|---|---|---|
| `MERKLE_SOCKET` | 自动发现 | 覆盖 Companion Socket 路径 |
| `MERKLE_LOG` | `info` | 标准错误日志级别 |
---
## 🔧 CLI 参考
`merkle` 二进制文件是操作员 CLI。它仅与正在运行的 `merkle-agent` 通过 Companion Socket 通信,并且从不直接触及密钥或存储。
**全局标志**(在每个子命令上):
| 标志 | 环境变量 | 默认值 | 用途 |
|---|---|---|---|
| `--socket <PATH>` | `MERKLE_SOCKET` | 平台默认值 | 覆盖 Companion Socket 路径 |
| `--output <FORMAT>` / `-o` | — | `human` | `human`、`json` 或 `plain` |
**生命周期**
| 命令 | 执行操作 |
|---|---|
| `merkle init [--non-interactive]` | 首次运行仪式 — 生成保险库,打印恢复密钥 **一次**。 `--non-interactive` 抑制 TTY 提示(CI)。 |
| `merkle unseal [--passphrase]` | 将 VRK 加载到内存中(Sealed → Unsealed)。 `--passphrase` 从 TTY 读取而不是 OS 密钥链。 |
| `merkle seal` | 归零 VRK(Unsealed → Sealed)。 |
| `merkle status` | 代理健康状况、密封状态、版本。 |
**命名空间和密钥**
| 命令 | 执行操作 |
|---|---|
| `merkle bind <namespace_label>` | 将命名空间绑定到会话(如果不存在则创建)。 `put`/`list`/`get` 之前需要。 |
| `merkle put <handle> [flags]` | 创建或覆盖密钥(从 stdin 读取纯文本)。 |
| `merkle list [<namespace>] [flags]` | 列出密钥( `--filter`、`--category`、`--sensitivity`、`--limit`,默认为 50)。 |
| `merkle get <handle> [--reason <text>]` | 为低/中安全性密钥获取一次性访问令牌(无纯文本)。 |
| `merkle describe <handle>` | 显示只读公共元数据(类别、敏感度、标签、版本)。 |
| `merkle reveal <handle> --reason <text>` | 揭示纯文本。触发 `high` 密钥的 OOB;如果 OOB 待处理,则打印重新运行提示。 |
| `merkle rotate <handle> [--purpose <text>] [--base64]` | 旋转活动值(新有效载荷来自 stdin;版本号严格增加)。 |
| `merkle delete <handle> --confirm` | 永久删除密钥和所有版本。 `--confirm` 是必需的。 |
| `merkle search <namespace> <query> [--limit N]` | FTS5 全文搜索(默认限制 20)。 |
**`put` 标志**
| 标志 | 默认值 | 备注 |
|---|---|---|
| `--sensitivity <low\|medium\|high>` | `medium` | `high` 需要至少一个 `--tag env:*` 并禁止 `--expose` |
| `--tag <KEY:VALUE>` | — | 可重复。允许的键: `env`、`project`、`role`、`provider`、`team` |
| `--category <note\|ssh\|password\|token\|...>` | `note` | **不可变** 创建后 |
| `--description <text>` | — | 公共元数据注释 |
| `--force` | false | 覆盖同名现有密钥 |
| `--base64` | false | 将 stdin 视为 Base64 二进制(SSH 密钥、TLS 证书、JWK 块) |
句柄格式:`vault://<namespace>/<category>/<name>` 或简写形式 `<namespace>/<cat>/<name>`。
**审计、备份、恢复、设备、诊断**
| 命令 | 执行操作 |
|---|---|
| `merkle audit [--op <type>] [--since <iso8601>] [--limit N]` | 查询只读审计日志(默认限制 50)。 |
| `merkle backup now [--note <text>]` | 触发按需备份。 |
| `merkle backup list [<namespace>] [--limit N]` | 列出备份快照(默认限制 20)。 |
| `merkle restore plan <backup_id> [--mode <newest_wins\|merge>]` | 预览恢复计划(干运行)。 |
| `merkle restore execute <plan_id>` | 应用先前创建的恢复计划。 |
| `merkle device pair --name <name> --class <hw\|enclave\|software>` | 配对 Companion 设备;打印配对代码。 |
| `merkle device list` | 列出已注册的 Companion 设备。 |
| `merkle device revoke <device_id>` | 撤销 Companion 设备。 |
| `merkle verify-recovery-key [--identity-file <PATH>]` | 验证存储的恢复密钥是否与 `recovery_pubkey` 匹配。默认情况下从 TTY(回声关闭)读取。 |
| `merkle doctor [--durability] [--chain] [--all]` | 自我诊断。 `--chain` 验证审计哈希链; `--all` 运行所有检查。 |
> [!TIP]
> 将 `-o json` 附加到任何命令中,以获取适合 `jq` 管道的机器可解析输出。
---
## 🧰 MCP 工具和提示
`merkle-mcp` 是一个薄的 stdio MCP 服务器(rmcp 1.8)。每个 Claude Code 窗口一个进程;它代理所有调用到守护进程,并且从不导入领域或存储代码。它公开了 **30 个工具** 和 **4 个提示**。
| 组 | 工具 |
|---|---|
| **身份** (3) | `vault.unseal` · `vault.seal` · `vault.bind` |
| **密钥** (8) | `vault.put` · `vault.get` · `vault.list` · `vault.describe` · `vault.rotate` · `vault.delete` · `vault.search` · `vault.history` |
| **显示** (1) | `vault.reveal` ⚠️ 需要操作员确认 |
| **使用令牌** (4) | `vault.use` · `vault.write_tempfile` · `vault.write_fifo` · `vault.revoke_tempfile` |
| **代理** (10) | `vault.ssh.exec` · `vault.ssh.copy` · `vault.ssh.port_forward` · `vault.ssh.shell`\* · `vault.http.request` · `vault.http.download` · `vault.http.upload` · `vault.spawn` · `vault.crypto.sign` · `vault.crypto.decrypt` |
| **审计** (1) | `vault.audit.query` |
| **备份** (2) | `vault.backup` · `vault.restore` |
| **诊断** (1) | `vault.doctor` |
> \* `vault.ssh.shell` 已连接,但守护进程返回 HTTP 501(交互式 PTY 尚未实现)。对于非交互式命令,请使用 `vault.ssh.exec`。
**4 个提示** — 在 Claude Code 中显示为 `/mcp__merkle__<name>` ([ADR-0028](docs/arch/adr/0028-mcp-prompts-for-slash-commands.md)):
| 提示 | 映射到 | 操作员确认? |
|---|---|---|
| `merkle-doctor` | `vault.doctor` | 否 |
| `merkle-show` | `vault.describe` | 否 |
| `merkle-reveal` | `vault.reveal` | **是** |
| `merkle-rollback` | `vault.history` + `vault.rotate` | **是** |
每个会话都应以 `vault.bind { label }` 开头(每个会话最多一次,每个 [ADR-0026](docs/arch/adr/0026-idempotent-bind-and-session-state-atomicity.md) 的两阶段提交)。 `cwd_hash` 在内部从工作目录的 BLAKE3 导出,并且永远不会跨越传输。详细内容请参阅 [Claude Code 接线指南](docs/arch/integrations/claude-code-wiring.md)。
---
## 📚 文档
规范 **是** 真相的来源 — `docs/arch/` 通过 `spec validate` 阻止所有更改。
| 区域 | 路径 | 内容 |
|---|---|---|
| 架构概述 | [docs/arch/README.md](docs/arch/README.md) | DDD + 六边形栈,目录布局 |
| 词汇表 | [docs/arch/glossary.md](docs/arch/glossary.md) | 按有界上下文的规范词汇 |
| ADRs (28) | [docs/arch/adr/](docs/arch/adr) | MADR 决策记录 `0001`–`0028`,全部接受 |
| 领域叙述 | [docs/arch/domain/](docs/arch/domain) | 有界上下文模型和不变式 |
| CUE 模式(50) | [docs/arch/schemas/](docs/arch/schemas) | DTO 和类别的类型契约 |
| Rego 策略(18) | [docs/arch/policies/](docs/arch/policies) | Conftest 策略门 |
| Gherkin 特性(15) | [docs/arch/specs/features/](docs/arch/specs/features) | 验收场景(Cucumber) |
| 威胁模型 | [docs/arch/threat-model/](docs/arch/threat-model) | STRIDE,信任边界,攻击面 |
| SLO(43) | [docs/arch/slo/](docs/arch/slo) | OpenSLO 服务级别目标 |
| 形式化模型(2) | [docs/arch/formal/](docs/arch/formal) | TLA+ 规格(TLC 在完整通道中检查) |
| 集成 | [docs/arch/integrations/](docs/arch/integrations) | Companion Socket OpenAPI,Claude Code 接线,入职 |
| C4 模型 | [docs/arch/architecture/workspace.dsl](docs/arch/architecture/workspace.dsl) | Structurizr 工作区 |
> **新人阅读顺序:** [docs/arch/README.md](docs/arch/README.md) → [词汇表](docs/arch/glossary.md) → [ADR-0002](docs/arch/adr/0002-adopt-agent-plus-mcp-adapter-topology.md) → 0021 / 0024 / 0026 / 0028 → `crates/merkle-application/src/commands/` → Companion Socket OpenAPI。
---
## 🧪 开发
### 前 5 分钟
```bash
cargo build --workspace # 调试构建(快速)
cargo test --workspace --no-fail-fast # ~753 通过 / 0 失败 / ~18 忽略
cargo clippy --workspace --all-targets -- -D warnings # lint(镜像 CI)
~/bin/spec validate # 中等通道 — 必须保持 9/9 绿色
just doctor # 检查 + clippy + 测试(无规格通道)
```
### `just` 配方
| 配方 | 命令(s) | 备注 |
|---|---|---|
| `just build` / `just build-release` | `cargo build --workspace [--release]` | 调试 / 优化 |
| `just check` | `cargo check --workspace --all-targets` | 快速类型检查 |
| `just test` / `just test-fast` | `cargo test --workspace [--lib --bins]` | 完整 / 跳过文档测试 |
| `just lint` | `cargo clippy --workspace --all-targets -- -D warnings` | CI 门控 |
| `just fmt` / `just fmt-check` | `cargo fmt --all [--check]` | 格式 / 干运行 |
| `just deny` / `just audit` | `cargo deny check` / `cargo audit` | 许可证 + 禁止 / 建议 |
| `just cov` | `cargo llvm-cov --workspace --html` | HTML 报告 |
| `just spec-fast` / `just spec-medium` / `just spec` | `~/bin/spec validate [--lane …]` | 4 / 9 / 14 验证器 |
| `just doctor` | 检查 + clippy + 测试 | 一次性健康门控(无规格通道 — 单独运行 `~/bin/spec validate`) |
| `just agent` | `cargo run -p merkle-agent` | 启动守护进程(开发) |
| `just cli args=…` | `cargo run -p merkle-cli -- <args>` | 运行 CLI |
### 规格通道
规格门控强制执行规格作为 [ADR-0018](docs/arch/adr/0018-full-coverage-validation-as-architectural-contract.md) 的源头。
| 通道 | 命令 | 时间 | 验证器 | 用途 |
|---|---|---|---|---|
| **快速** | `just spec-fast` | ~1.5 秒 | 4: `lint_cue`、`lint_ddd_role`、`lint_openapi`、`lint_features` | 快速健康检查 |
| **中等** *(默认)* | `just spec-medium` | ~10 秒 | 9: 快速 + `lint_structurizr`、`lint_md`、`lint_mermaid`、`lint_madr`、`lint_yaml` | **必须保持 9/9 绿色** |
| **完整** | `just spec` | ~60 秒 | 14: 中等 + `lint_conftest`、`lint_vale`、`lint_slo`、`lint_asyncapi`、`run_tlc` | **CI 合并门控** |
每次行为变更必须在 **同一提交** 中更新 `docs/arch/`,并且所有本地门控(`build`、`clippy -D warnings`、`cargo deny`、`spec validate` 中等 9/9)必须为绿色。
### 测试分类法
| 层级 | 如何运行 | 备注 |
|---|---|---|
| **单元** | `cargo test --workspace --lib` | `#[cfg(test)]` 行内 |
| **集成** | `cargo test --workspace` | 每 crate `tests/`,真实或模拟适配器 |
| **BDD** | `cargo test -p merkle-bdd` | Cucumber;退出代码为 0,即使存在待处理场景 |
| **E2E** | `cargo build --bins && cargo test -p merkle-e2e -- --ignored` | 启动代理程序,针对临时目录;所有 `#[ignore]` |
| **实时烟雾** | `cargo test -p merkle-cli -- --include-ignored` (等) | 所有 `#[ignore]`;需要正在运行的、未密封的守护进程或环境设置 |
> [!NOTE]
> `cli_smoke` 需要 `--include-ignored`(不是 `--ignored`)**和** 正在运行的、未密封的守护进程;其套接字路径为 `$TMPDIR/merkle/companion.sock`,与生产默认值不同。
### 工作区布局
**22 名成员 — 19 个库 crate + 3 个二进制文件。** 2024 版,解析器 3,MSRV 1.85。
<details>
<summary><strong>基础和领域</strong></summary>
| Crate | 角色 |
|---|---|
| `merkle-types` | 共享值对象(`Handle`、`Sensitivity`、`UuidV7`、`Blake3Hash`、`AuditOp`,...)。零基础设施依赖。 |
| `merkle-domain-identity` | 身份和密封 — `VaultIdentity`、`MasterKey`、`VaultRootKey`、`SealedState`、`UnsealProtocol` |
| `merkle-domain-secret-storage` | 密钥存储 — `Secret`、`SecretVersion`、`Namespace`、`PrivateBlob`、`RetentionPolicy` |
| `merkle-domain-access-mediation` | 访问调节 — `RevealRequest`、`UseToken`、`Tempfile`、`Fifo`、`OobChallenge`、`decision::evaluate` |
| `merkle-domain-audit-compliance` | 审计和合规 — `AuditEntry`、`AuditLog`、`PinnedHead`、`ChainVerifier` |
| `merkle-domain-backup-recovery` | 备份和恢复 — `Backup`、`BackupScheduler`、`RestorePlanner`、`AnacronState` |
| `merkle-domain-policy-permissions` | 策略和权限 — `NamespacePolicy`、`RevealPolicy`、`RateLimit`、`PolicyEvaluator` |
</details>
<details>
<summary><strong>端口、适配器、客户端、应用程序和二进制文件</strong></summary>
| Crate | 角色 |
|---|---|
| `merkle-ports` | 纯粹的 trait 定义:`StoragePort`、`KeychainPort`、`CryptoPort`、`OobNotifierPort`、`ExternalServicesPort` |
| `merkle-adapter-sqlite` | sqlx + SQLite WAL;每个 blob 的 AEAD、FTS5、append-only audit 触发器、`pinned_head` |
| `merkle-adapter-keychain` | 跨操作系统密钥链(`keyring`,服务 `dev.fapp.merkle`);文件支持的 `age` 密钥库后备 |
| `merkle-adapter-crypto` | `RustCryptoAdapter`:XChaCha20-Poly1305、BLAKE3、Argon2id、age、Ed25519、X25519 ECIES |
| `merkle-adapter-oob` | OOB 确认(桌面/TTY/本地主机);验证 Ed25519 `OobResolution` |
| `merkle-adapter-external-services` | SSH 执行 + HTTP(reqwest/rustls)与 SSRF/DNS-rebind 保护 |
| `merkle-adapter-companion-socket` | axum HTTP/1.1 Unix 套接字 — 独一的入口端口。 ⚠️ 只有 `deny` 放松 `unsafe_code` 至 `allow` 和 `missing_docs` 至 `allow` 的 crate |
| `merkle-adapter-mcp` | rmcp MCP 服务器:30 个工具 + 4 个提示。依赖于 `merkle-companion-client`,**不依赖** 领域 |
| `merkle-companion-client` | 可重用的 HTTP/1.1-over-Unix-socket 客户端(hyper + 自定义 `UnixConnector`) |
| `merkle-application` | 用例编排:`AppContext`、命令和查询处理程序。导入零基础设施 |
| `merkle-bdd` | Cucumber 验收测试;加载 15 个 `.feature` 文件 |
| `merkle-e2e` | 黑盒 E2E;启动 `merkle-agent`;所有 `#[ignore]` |
| `bin/merkle-agent` → `merkle-agent` | 守护进程 / 组合根 |
| `bin/merkle-cli` → `merkle` | 操作员 CLI |
| `bin/merkle-mcp` → `merkle-mcp` | 薄的 stdio MCP 服务器 |
</details>
### 限制基线
限制配置已 **锁定** — 绝不编辑 `[workspace.lints]`、`clippy.toml` 或 `rust-toolchain.toml`。修复代码以符合要求。
| 规则 | 级别 | 范围 |
|---|---|---|
| `clippy::all` / `clippy::pedantic` | `deny` (优先级 -1) | 工作区 |
| `unsafe_code` | `forbid` | 工作区(除了 `merkle-adapter-companion-socket`:`deny`) |
| `missing_docs` | `warn` | 工作区(除了 `merkle-adapter-companion-socket`:`allow`) |
| `unused_must_use` | `deny` | 工作区 |
提交遵循 Angular `<type>(<scope>): <subject>`;绝不一次提交所有文件;需要 DCO 签名(`git commit -s`)。
---
## 🛡️ 安全
发现漏洞?请阅读 **[SECURITY.md](SECURITY.md)** 获取披露流程。完整的威胁分析(STRIDE、信任边界、攻击面)在 [docs/arch/threat-model/](docs/arch/threat-model) 中。
## 🤝 贡献
欢迎贡献 — 请参阅 **[CONTRIBUTING.md](CONTRIBUTING.md)** 和 **[行为准则](CODE_OF_CONDUCT.md)**。每次行为变更必须在同一提交中更新 `docs/arch/` 中的规范,并且所有本地门控(`build`、`clippy -D warnings`、`cargo deny`、`spec validate` 中等 9/9)必须为绿色。
## 📄 许可证
根据 **Apache License 2.0** 授权 — 请参阅 [LICENSE](LICENSE)。
---
## 项目状态
Merkle **已实现并为绿色**,**预 1.0**。完整的 22-crate 工作区已构建完成;所有本地门控均通过 — `cargo build`、`cargo clippy -D warnings`、`cargo deny` 和中等规格通道(9/9 验证器)— **~753 测试通过 / 0 测试失败**(加上 ~18 个 `#[ignore]` E2E 和实时烟雾测试)。当前版本为 **0.1.0(未发布)**。API 和磁盘格式在 1.0 之前可能仍会更改。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器