Deskfolk 当前是 WIP,面向 macOS 本地开发与试用(Windows 是实验性预览,远控在 Windows 上还没有),没有稳定版本的安全维护承诺、独立安全审计声明或响应时限保证。安全修复优先面向最新开发代码。请使用可丢弃数据和可信模型 / 工具,不要把它当作隔离不可信代码的执行环境。
packages/remote 提供真实 Noise IK、配对 AEAD、签名授权与 COSE/WebAuthn 验证的纯接口,但不启用公网远控。托管信使 PWA 已有配对/Noise 客户端,生产包不含 __local-api 与本机 bearer;公网配对仍默认关闭。协议契约、host 必须承担的原子重放/挑战消费、首次 UV 登记与替换权限见 docs/remote-protocol.md。官方协议向量和独立 Rust snow 对打不是独立安全审计;桌面浏览器软件密钥测试不是真机 PWA/WebAuthn UV。L1 真机、S-rev 独立审查及 G-uv 门尚未通过,不能据此开公网或启用高危动作。
The shared remote cryptography is a default-off security prototype, not a released remote-access feature. Noble primitives have audit history; the exact pinned versions and our Noise/pairing/WebAuthn integration are not independently audited. Physical-device PWA/WebAuthn and external-review gates remain unverified. The host must atomically own trust checks, replay claims, challenge consumption and credential replacement; a stolen device key must not create or replace an existing UV credential. None attestation does not prove authenticator hardware provenance. The trusted web origin can replace client code; E2EE does not solve origin compromise or XSS. Never place pairing secrets, keys, file paths or application plaintext in URLs, queries, logs or service-worker caches. Hosted production must not compile local bearer discovery. Markdown/HTML/SVG/URL/attachment XSS is still in scope even after the origin is trusted; HTML preview remains sandbox without allow-same-origin.
协议输入含 Bun/Node Buffer 时,保留的密钥、公钥和返回字节均建立独立所有权,清理只擦除会话自有副本;不能用 Buffer.slice 冒充复制。WebAuthn clientDataJSON 同时要求严格 JSON 语法和无重复字段,签名始终绑定原始字节。回执与 UV 完整操作摘要必须包含实际条件头/前置条件(如 If-Match),不能只绑动作名。
Retained authentication buffers are independently copied even for Buffer views; session cleanup must not erase caller-owned identities. WebAuthn requires strict JSON grammar and duplicate-member rejection while verifying the original bytes. Receipt and UV operation digests must include the actual conditional headers/preconditions, not only the action/target. These fixes do not constitute an external audit or physical-device approval.
The daemon adapter reuses the existing Store/engine and internal dispatchBusiness; that method is not an authenticator. Only current, pinned, authenticated Noise principals reach its remote allowlist. Local bearer/Origin rules are unchanged and /remote/* is never exposed through local HTTP or Bot tools. The separate setup channel is the socketpair the window spawns the daemon with. With the sealed provider, native desktop_channel verifies its peer audit token and signed desktop identity before parsing; the shipped app's file credential store (ADR 0033) trusts it by construction instead, since only the spawned daemon holds it. The Tauri command independently requires the bundled main frame and forwards setup operations only: the two confirmation steps (challenge_display, then challenge_proof after a LocalAuthentication sheet showing the daemon's own description) are the window's, never the webview's. It returns public host pins/ephemeral QR material, never long-term secrets.
SQLite stores public trust, replay reservations, COSE keys/counters and challenges, not remote private keys. The shipped app keeps those private keys and the high-water in dev-remote/credentials.json (0600) beside the database, not in the Keychain (ADR 0033): any process running as the user can read them and impersonate the Mac to its paired devices, and restoring the whole data folder restores the high-water with the database, so only a database-only restore is caught. The sealed Keychain path below remains unshipped. Every removal, including one device, first advances native high-water, then commits a new database generation. All channels close; survivors retain their signed grant epoch but receive the new database generation. A restored revoked row at the old generation is rejected. Keychain-ahead/unknown failures remain closed and require explicit recovery; no native/UV environment bypass exists, and a compiled daemon never offers the source-only stand-in confirmations. Equal-generation recovery also advances high-water; otherwise recovery itself would reintroduce backup resurrection. Native-confirmed reset/relay migration use explicit uncertain/done durable intents and require fresh recovery on unknown native outcome; first-UV renewal requires fresh Mac proof bound to the existing device's current Split session. Fresh assertions are checked by the shared verifier and consumed with current session/trust/credential/counter/time CAS. Revocation intent/receipts are durable; external credential/tool effects already accepted cannot be recalled.
接线测试使用真实本机中继、Noise、SQLite 与生成签名,native 材料只允许构造注入的测试替身。测试不是签名封闭运行时、真 Keychain/LA、iOS/Android 或独立安全审计验收。发布包的远控凭据在数据目录的 0600 文件里而不在钥匙串(ADR 0033),同用户进程都能读到;每台设备由窗弹 LocalAuthentication 批准,网页发不了确认那两步。共享排空不拥有退出/登录任务;明确强制只中断,不因超时或断线升级。远程附件与文件 GET 不设大小上限(中继照旧限速)、文本 PUT 1 MB、单设备并发 2;主机目录浏览复用 realpath/symlink walk,TCC 拒绝返回类型化授权错误。远程诊断只含计数与错误码,不含 Bot 名、正文、路径、密钥或原始日志。维护写路由不出现在 :17890。独立运行时设置入口存在但生产 fail-closed,测试只用 fake launchctl,不在用户 Aqua 域 bootstrap。真实 TCC 与手机下载未通过;见协议文档。
Optional Web Push is Mac-outbound only. VAPID private material is the credential store's vapid (the 0600 file in the shipped app; tests use fixtures, never the personal Keychain). Subscriptions store the HTTPS endpoint URL plus endpoint_hash/p256dh/auth/expires_at bound to device_id. Hosts are strictly parsed against *.push.apple.com, fcm.googleapis.com and updates.push.services.mozilla.com; unknown hosts and 3xx redirects fail closed (SSRF). Payload is only {t:"pending"} with fixed visible copy; no titles, bodies, filenames or Bot names. Click reconnects and pulls inbox and never resolves approvals. Deny/expiry must not drop inbox. Revoke best-effort deletes the row; APNs disappearance is not promised. The relay does not send. G-push (iOS 16.4+ home-screen standalone) is not passed.
可选 Web Push 仅 Mac 出站。VAPID 私钥是原生 vapid(测试用 fixture,不读个人钥匙串)。订阅保存 HTTPS endpoint URL 以及 endpoint_hash/p256dh/auth/expires_at,绑定 device_id。主机严格解析,仅 *.push.apple.com、fcm.googleapis.com、updates.push.services.mozilla.com;未知主机与 3xx 重定向失败关闭(防 SSRF)。载荷只有 {t:"pending"} 与固定文案,无标题/正文/文件名/Bot 名。点击只重连并回到会话列表,绝不批准。拒绝或过期不丢待办。吊销尽力删订阅,不承诺 APNs 立刻消失。中继不代发。G-push(iOS 16.4+ 主屏幕 standalone)未通过。
仓库托管在 GitHub 且启用私密漏洞报告后,请从 Security → Report a vulnerability 提交。维护者应在首次公开前启用该功能;文档本身不会开启 GitHub 设置。
若看不到该入口,可发一个仅包含「请求私密安全联系渠道」的普通 Issue,等待维护者提供私密方式。不要在公开 Issue / PR 中发布漏洞细节、可利用代码、凭据、数据库或真实用户数据。本项目目前没有另行指定的安全邮箱,也不提供奖励或固定处理时限承诺。
私密报告请包含:
- 受影响的源码版本、操作系统(macOS / Windows)与工具链版本。
- 影响范围、最小复现步骤和预期安全边界。
- 已脱敏的证据、可行的缓解办法(若有)。
如已经泄露凭据,请先撤销或轮换相应凭据;仅删掉帖子或文件不能撤回泄露。
- 桌面窗口、本机守护进程、会话数据库与共享工作区在本机运行和存储,不依赖项目提供的云端协作服务。
- 调用远程模型时,上下文、消息和相关附件内容会按请求发送给该端点;MCP 工具收到调用参数,也可能读取或向外发送数据。stdio 进程不等于离线进程。
- 使用者需要审查模型提供商、MCP 服务及其数据保留政策。只有所有模型与工具均能本地运行时,才可能离线使用。
- Bot 之间不隔离。 所有 Bot 共用工作区和已启用的 MCP 工具;Bot 人设不是权限边界。
- 工作区 shell 不是操作系统沙箱。 路径检查和批准是应用层约束,不能保证任意子进程无法访问区外资源。Windows 上命令在 Git Bash(找不到时是 PowerShell)里跑,区外检查也认
C:\、C:/、..\、%USERPROFILE%、$env:、别的盘符和网络共享,认不准的写法按区外处理;同样不是沙箱。 - 工作区内文件操作可直接执行;区外文件访问、无约束 shell、新端点 / 端点 URL 变更、新 MCP / 连接变更等动作按应用规则请求批准。
- 信任 MCP 是安装 / 配置时的决定。 已配置、启用且连接成功的工具对所有 Bot 可用,调用不再逐次批准。只安装可信程序和服务,使用最小权限的凭据。
- 模型输出、工具结果和外部内容可能包含错误或提示注入;检查结果和危险动作仍然重要。
- HTML 预览会跑脚本。 工作区单文件 HTML 在侧栏 iframe 里以
allow-scripts、无allow-same-origin执行,便于设计稿动效;脚本不能读信使页面或本机 token,外链仍受窗口 CSP 限制。不要把不可信 HTML 当隔离执行环境。 - 拒绝和 Stop 应被尊重,但不能撤销已经发生的文件修改、外部请求或费用。本项目没有自动预算熔断保证。
| 内容 | 当前存储方式 |
|---|---|
| 会话与应用状态 | ~/Library/Application Support/real-bot/state.sqlite;Windows 上是 %LOCALAPPDATA%\real-bot\state.sqlite |
| 本机接口描述与 token | 同目录的 local-api.json;每次守护进程启动生成新 token,目录权限 0700、文件 0600;Windows 不用这两个权限位,靠 %LOCALAPPDATA% 默认只对当前用户开放的访问控制 |
| 模型 API key 与 HTTP MCP Authorization | macOS 钥匙串(Windows 上是凭据管理器),由守护进程管理,不通过聊天正文配置 |
| 附件与产物 | 用户选择的共享工作区;上传附件位于 inbox/ |
| 超长工具结果 | 工作区 tool-results/ 中的完整 JSON;可能含敏感信息,使用后按需清理 |
SQLite、附件与工具结果没有应用层加密承诺;依赖本机账户、系统权限、磁盘与备份保护。REAL_BOT_DATA_DIR 可替换数据库和接口描述文件的默认目录,不改变钥匙串 / 凭据管理器里的条目、执行权限或本机 API 端口,不构成安全隔离。
本机 API 绑定 127.0.0.1:17890。健康检查不鉴权;其他 HTTP 请求使用 Bearer token,WebSocket 在首条消息认证。浏览器开发服务器提供同源 /__local-api 以读取该 token;保持开发服务仅供本机使用,不把开发服务器或本机 API 暴露到公网。
.gitignore只是减少误提交,不是秘密扫描或历史清理工具。- 不提交
.env、API key、token、私钥、数据库、真实对话、工作区产物或完整工具返回。 - 日志与截图也可能包含密钥、账号、内部 URL 或私人路径,公开前逐项脱敏。
- 保留贡献和第三方代码所需的授权声明。
Deskfolk is WIP for local experimentation on macOS (Windows is an experimental preview, without remote access), with no stable-version security support or response-time guarantee. Report vulnerabilities through GitHub Security → Report a vulnerability when enabled. If unavailable, open an Issue requesting a private contact channel without disclosing vulnerability details. Maintainers must enable private reporting before public release; no dedicated security email is currently designated.
Local execution does not mean offline or sandboxed execution. Remote model and MCP requests may transmit data, local MCP processes may access the network, all Bots share tools and files, and configured MCP calls are not individually approved. The workspace shell is not an OS sandbox. Single-file HTML preview runs scripts in an opaque-origin iframe (allow-scripts, no allow-same-origin) and is not an isolated execution environment. Stop and approval cannot undo completed effects or costs.
State and local tokens live in the application support directory, provider and HTTP MCP credentials use the macOS Keychain (Credential Manager on Windows), and attachments and full tool results live in the selected workspace. There is no application-level encryption guarantee for these data files. REAL_BOT_DATA_DIR does not isolate Keychain / Credential Manager credentials or change the API port. On Windows the data folder is %LOCALAPPDATA%\real-bot, protected by that folder's per-user access control rather than mode bits, and the workspace shell runs in Git Bash or PowerShell; its outside-the-workspace check also covers drive letters, ..\, %VAR%, $env: and network shares, and treats forms it cannot place as outside. Keep development services local, redact diagnostics, and rotate any exposed credentials immediately.