Skip to content

接口面(Surfaces)

核心模型

Coffer 的每个接口面都是通向同一底层守护进程的入口点。守护进程持有全部状态;接口面是读取或修改该状态的只读或可读写窗口。通过 CLI 添加一个 MCP 服务器,Web UI 会立即显示它——因为两者都在调用同一个守护进程。断开桌面应用连接,守护进程仍在运行,所以你的 MCP 客户端依然正常工作。

所有接口面共享的内容

所有接口面都建立在同一个 coffer-daemon 进程之上。守护进程是一个 FastAPI 应用,绑定到 127.0.0.1:<auto-port>,并暴露两个顶级端点组:

  • /api/v1/* — 管理面(REST,JSON),使用 X-Coffer-Token 鉴权。
  • /mcp — MCP 协议端点(HTTP/SSE),同样使用 X-Coffer-Token 鉴权。

每个接口面都通过 loopback HTTP 与守护进程通信,从 ~/.coffer/daemon.json(权限 0600,仅 owner 可读)读取端口和 token。没有任何接口面持有自己的持久化状态——它们是进入守护进程有状态内核的无状态入口点。

以下各接口面均采用统一模板描述:它是什么 · 所在进程 · 传输方式 · 生命周期 · 安全边界


REST API

它是什么。 管理面。这是执行每一种资源 kind 及跨切面操作的 HTTP JSON API。它在 /api/v1/ 下按路由组组织,每个 kind 一个家族,加上共享关注点:

路由组涵盖内容
/resources与 kind 无关的资源 CRUD、启用/禁用、生命周期。
/resources/mcp_serverMCP 能力偏好与调用日志。
/agentsAgent 注册表、配置文件、工作区切面、memory 投影。
/skills技能主存储与各 agent 的绑定。
/knowledge_basesKB 文档、上传、重建索引、检索。
/memory_storesMemory 事实、scope 与原生投影。
/channels通道绑定(Telegram、SeaTalk)、配对。
/chat/modelsChat 会话/轮次与模型目录。
/credentials/settings加密凭据存储;设置含 /settings/credentials(主密钥存储)与 /embedding 配置。
/sync多机同步运行与配置。
/fs用于配置选择器的文件系统浏览辅助。
/audit/retention/daemon审计日志、保留策略、守护进程 token/备份操作。

REST API 是规范接口——CLI、Web UI 和桌面应用都调用它。

所在进程。 守护进程(coffer-daemon)。REST API 嵌入在 FastAPI 应用中,与守护进程不可分割。

传输方式。127.0.0.1:<auto-port> 上的 HTTP/1.1。所有端点都在 /api/v1/ 下。JSON 请求体和响应体。管理面无 WebSocket,无 SSE。

生命周期。 REST API 从守护进程达到「就绪」状态起即可用。守护进程关闭时随之关闭。

安全边界。 每个请求都必须携带有效的 X-Coffer-Token 头。token 在守护进程启动时生成,存储在 ~/.coffer/daemon.json 中,从不传输到机器外部。CORS 配置为仅允许来自本地 Web UI 来源的请求。由于守护进程仅绑定到 127.0.0.1,网络本身就是第一道防线:网络上其他机器的请求无法到达 API。


MCP 协议端点

它是什么。 聚合 MCP 接口面。这是 MCP 客户端最终通信的端点。它接受通过 HTTP/SSE 传输的 JSON-RPC 2.0 消息,支持完整的 MCP 协议:initialize 握手、tools/listtools/callresources/listresources/readprompts/listprompts/get 以及 notifications/*。守护进程聚合所有已注册上游 MCP 服务器的工具、资源和提示,以 <server-name>__<capability-name> 为每项命名,呈现为来自单一 MCP 服务器的样子。

所在进程。 守护进程。MCP 端点是与 REST API 同一 FastAPI 应用的一部分,在同一端口上提供服务。

传输方式。127.0.0.1:<auto-port>/mcp 上的 HTTP/SSE。MCP 使用 HTTP POST 发送 JSON-RPC 请求,并使用 Server-Sent Events 将通知流式传回客户端(例如 tools/list_changed)。stdio shim 将其 stdin/stdout 桥接到此 HTTP/SSE 端点。

生命周期。 客户端连接时(通过 /mcp 端点或通过 shim)创建一个新的 MCPGatewaySession。客户端断开时会话被释放。会话拥有的上游子进程在释放时被回收。MCP 端点本身的生命周期与守护进程相同。

安全边界。 初始连接需要 X-Coffer-Token。由于连接通过 stdio shim(从 daemon.json 读取 token)或直接来自本地进程,有效信任边界是本地用户账号。每个会话是隔离的:一个客户端的上游子进程和能力缓存对另一个客户端的会话不可见。


CLI(coffer …

它是什么。 命令行管理接口。通过 REST API 可用的每个管理操作也可以作为 coffer 子命令使用,按 kind 与跨切面关注点分组:

  • 各 kind 分组: coffer mcpcoffer agentcoffer skillcoffer kbcoffer memorycoffer channel
  • 跨切面分组: coffer credentialscoffer synccoffer chatcoffer model
  • 与 kind 无关 / 运维分组: coffer resourcecoffer auditcoffer retentioncoffer daemon,以及顶级的 coffer backup / coffer restore(后者带 --reindex 以重建知识索引)。重建单个知识库的索引是 coffer kb reindex

典型命令读作 coffer mcp addcoffer mcp tool enable/disablecoffer auditcoffer daemon start/stop/status。CLI 是 Coffer 用于脚本化工作流、基于 dotfile 的配置和没有浏览器的远程(无头)机器的主要接口。每个子命令都支持 --json 输出,以便机器可读集成。

所在进程。 短生命周期子进程。CLI 是一个独立的 Python 进程,作为控制台脚本入口点(coffer)安装到 PATH 上。执行一条命令后退出。在两次调用之间不保持运行。

传输方式。 通过 loopback HTTP 到 127.0.0.1:<port>/api/v1/,使用来自 ~/.coffer/daemon.json 的 token。CLI 使用 detect-or-spawn 模式:如果发出 CLI 命令时守护进程未运行,CLI 将守护进程作为分离的后台进程启动,等待 daemon.json 出现,然后连接。用户永远不会看到「守护进程未运行」的错误;他们可能会在第一次调用时看到短暂的启动延迟。

生命周期。 按需启动;每条命令完成(或打印错误)后退出。CLI 退出不会停止守护进程。

安全边界。 来自 ~/.coffer/daemon.json(权限 0600)的 token。与所有其他接口面相同的文件权限边界:只有用户账号的 owner 才能读取 token,从而发出经过认证的请求。


Stdio Shim(coffer-mcp-shim

它是什么。 MCP 客户端(Claude Code、Codex、Cursor 以及任何支持 stdio MCP 服务器的工具)与守护进程 MCP 端点之间的桥接。MCP 客户端配置为 "command": "coffer-mcp-shim",而不是特定的上游服务器。shim 在客户端的 stdin/stdout 接口和守护进程的 HTTP/SSE MCP 端点之间进行转换,使守护进程从客户端角度看起来像一个普通的 stdio MCP 服务器。

所在进程。 每个连接的 MCP 客户端会话一个 shim 进程。每个 MCP 客户端在客户端启动时启动一个新的 shim 进程。三个同时运行的 MCP 客户端意味着三个 shim 进程,每个都维护自己与守护进程的 HTTP/SSE 连接,每个都产生自己独立的 MCPGatewaySession,带有自己的上游子进程集合。

传输方式。 就 MCP 客户端而言,shim 从 stdin 读取并写入 stdout。在内部,它通过 HTTP/SSE 连接到 127.0.0.1:<port>/mcp,携带 X-Coffer-Token。stdin 上到达的 JSON-RPC 消息作为 HTTP POST 请求转发到守护进程;来自守护进程 SSE 流的通知写回 stdout。

生命周期。 shim 由 MCP 客户端使用该客户端支持的任何子进程机制启动(例如,Claude Code 的 claude mcp add coffer coffer-mcp-shim)。MCP 客户端进程退出时 shim 退出。shim 退出不会停止守护进程——其他 shim 和其他客户端继续不受影响地工作。启动时,shim 应用 detect-or-spawn:如果在 daemon.json 中找不到正在运行的守护进程,它会启动一个,然后连接。

安全边界。 与 CLI 相同:来自 ~/.coffer/daemon.json 的 token,文件权限边界。由于 shim 由 MCP 客户端(以相同本地用户身份运行)启动,信任边界隐含在进程所有权链中。shim 在每个守护进程请求上携带 token;守护进程对其进行验证。


回调监听器(Callback Listener)

它是什么。 唯一可被公网访问的接口面。它是一个独立的签名回调进程,接收来自聊天平台的入站 webhook——具体为 POST /seatalk/{channel}——以该通道的签名密钥校验每个请求的 SeaTalk 签名,回应 event_verification 挑战,并将真实事件转发给守护进程,由通道运行时处理。它之所以存在,是因为 SeaTalk 将事件推送到一个 URL,而不是让 Coffer 长轮询(Telegram 采用的模型),因此需要一个可达的 HTTP 端点(规约 009,ADR-014)。

所在进程。 一个由守护进程启动的子进程,刻意与守护进程分离。它仅在至少有一个 SeaTalk 通道启用时运行。将其置于主守护进程之外,意味着可被公网访问的代码路径是一个小而隔离的接口面,在任何流量到达有状态内核之前先完成签名校验。

传输方式。127.0.0.1:<callback-port> 上的 HTTP,仅提供签名回调路径。监听器本身绑定到 loopback;来自 SeaTalk 服务器的可达性由 owner 在带外自建的用户隧道提供——Coffer 自己从不开放公网端口。

生命周期。 当某个 SeaTalk 通道启用时由守护进程启动;当最后一个 SeaTalk 通道禁用时拆除。其生命周期绑定于通道状态,而非任何客户端会话。

安全边界。 与 loopback 接口面不同,这一个接受源自机外的流量,因此其信任边界是各通道的 SeaTalk 签名:每个请求体在被转发前都以该通道的密钥经 verify_seatalk_signature 校验,未签名或签名错误的请求一律拒绝。这里的关卡不是 X-Coffer-Token——而是签名。


Web UI

它是什么。 基于浏览器的管理界面,由规约 002 规定。Web UI 提供与每个 CLI 管理操作等价的可视化操作:通过 JSON 导入注册 MCP 服务器、浏览服务器健康状态和能力列表、开/关工具/资源/提示、查看审计日志和调用历史,以及配置保留策略。信息架构反映了资源 kind 模型:侧边栏显示 Resources(已上线的 kind——MCP 服务器、Agents、Skills、Knowledge Bases、Memory、Channels)、用于直接与 agent 对话的 Chat,以及 System(可观测性,及带 Security 和 Models 子区的设置)。不显示任何「即将推出」的占位符——一个 kind 只有真正可用后才会出现。

所在进程。 浏览器进程。在生产环境中,构建好的前端由 Tauri 桌面 shell 内嵌(tauri.conf.json 中的 frontendDist);浏览器进程与守护进程在逻辑上是分离的。在开发模式下,Vite 开发服务器运行在 http://localhost:5173。所有数据都从守护进程的 REST API 获取。

传输方式。 浏览器 HTTP/REST 到 http://127.0.0.1:<port>/api/v1/。Bearer token 在启动时提供给浏览器上下文(由桌面 shell 或本地助手从 ~/.coffer/daemon.json 读取)。

生命周期。 Web UI 会话是浏览器标签页的生命周期。关闭标签页不会影响守护进程。UI 包含一个守护进程离线横幅,用于检测守护进程何时不可访问,并将 coffer daemon start 命令显示为可复制的操作建议;当守护进程重新上线时,横幅自动消失。

安全边界。 每个 REST API 调用都携带 X-Coffer-Token。CORS 配置为仅允许来自本地 Web UI 来源的请求。由于守护进程绑定到 loopback 且 token 从不传输到远程来源,信任模型与 CLI 相同:仅限本地用户账号。


桌面应用

它是什么。 Tauri 2 桌面应用,由规约 003 规定。桌面应用将规约 002 的同一 Web UI 包装在原生应用窗口中,添加了守护进程监督和系统托盘图标。对于日常桌面使用来说,它是零摩擦的入口点:安装它,Coffer 就始终在后台运行,无需任何手动 coffer daemon start 步骤。

所在进程。 原生桌面进程(Tauri 2,Rust + WebView)。它将 Web UI 嵌入为 WebView;桌面特定的功能(托盘菜单项)在前端代码中的 isTauri() 守卫后面激活。

传输方式。 内部:WebView 通过 127.0.0.1:<port>/api/v1/ 使用 REST,通过 /mcp 使用 MCP(与基于浏览器的 Web UI 相同)。Rust shell 也通过守护进程的 REST API 进行监督任务(健康检查、重启)。

生命周期。 桌面应用应用 detect-or-spawn 模式:启动时读取 daemon.json;如果守护进程已在运行,则连接;如果没有,则将 coffer-daemon 作为分离进程启动(POSIX 上的 setsid)并等待 daemon.json 出现。关闭主窗口会将其隐藏到系统托盘——守护进程保持运行。从托盘选择「退出」退出桌面进程。守护进程在桌面应用退出时的命运取决于是否还有其他入口点(shim、CLI)仍在使用它;分离的守护进程在桌面应用退出后继续存活。

桌面应用还在每次启动时将捆绑的 coffer-mcp-shimcoffer-daemon 二进制文件幂等地部署到稳定的用户可写 PATH 目录(macOS 和 Linux 上的 ~/.coffer/bin/),使用大小/mtime/版本哨兵的过期检查来检测升级。这确保了在第一次桌面启动后 coffer-mcp-shim 始终在 PATH 上——其 detect-or-spawn 也能找到同目录的 coffer-daemon——而无需用户手动编辑 shell 配置文件。

安全边界。 与 Web UI 相同:每个 API 调用携带 X-Coffer-Token,仅限 loopback 的守护进程,本地用户账号信任边界。桌面应用将 coffer-daemoncoffer-mcp-shim 捆绑为 PyInstaller sidecar——运行时不依赖系统 Python。

分发层级。 桌面应用是发布版本的 CLI+桌面层级。一个单独的仅 CLI 层级(coffer-cli-<triple>.tar.gz)将这些二进制文件打包在没有 Tauri 包装器的情况下,用于无头或服务器安装。


接口面对比

接口面进程类型到守护进程的传输由谁启动生命周期
REST API守护进程—(即守护进程本身)系统 / 手动守护进程生命周期
MCP 端点守护进程—(即守护进程本身)系统 / 手动守护进程生命周期
CLI(coffer短生命周期子进程Loopback HTTP用户 / shell每条命令
Stdio shim每会话一个HTTP/SSEMCP 客户端MCP 客户端会话
回调监听器守护进程启动的子进程Loopback HTTP(转发给守护进程)守护进程(SeaTalk 启用时)有 SeaTalk 通道启用期间
Web UI浏览器标签页Loopback HTTP(REST)用户 / 浏览器浏览器标签页会话
桌面应用原生进程Loopback HTTP(REST + MCP)用户直到从托盘退出

参见: 架构参考规约 002:UI Shell规约 003:Desktop