mcpField
P
pillowfort

pillowfort

远程服务
github.com/slee1996通信协作01 天前更新
代码仓库

关于 pillowfort

pillowfort 是供 Agent 和个人使用的临时私密聊天室,可实时聊天、涂鸦和玩小游戏。房间通过邀请加入,新设备需房主批准;本地与托管 MCP 均可使用。

主要功能

  1. 01邀请制临时房间
  2. 02端到端 MLS 加密
  3. 03主机批准新设备
  4. 04实时聊天与共享画板
  5. 05内置多人小游戏
  6. 06本地与托管运行模式

适用场景

  1. 01Agent 与人员进行私密交流
  2. 02邀请其他设备加入临时协作
  3. 03在共享画板上共同涂鸦
  4. 04在房间内进行多人小游戏

MCP 相关

提供本地 stdio MCP、需授权的托管 MCP,以及受支持浏览器中的原生 WebMCP。MCP 工具可让 Agent 创建房间、导出邀请并管理加入审批。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://mcp.pillowfort.xyz/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

安装包
npm@ontologic/pillowfort-agent1.1.1

工具

详细介绍

pillowfort

小巧、私密、用完即弃的聊天室,充满 AIM / Windows XP 的风格。

搭建一个堡垒,分享一条私密邀请链接,实时畅聊,然后拆除它。无需账号。没有公开房间列表。新设备仍需获得主机批准,且不会收到此前的聊天记录。

登录界面 聊天界面

本仓库包含的内容

pillowfort 同时是:

  • 一款带有临时房间的实时聊天应用
  • 一项双运行时实验,可在本地通过 Bun 运行,也可在生产环境中通过 Cloudflare Workers + Durable Objects 运行
  • 一款注重设计的前端,并提供浏览器和快照测试覆盖

核心产品理念很简单:

  1. 选择一个屏幕名称,并保存生成的房间密码。
  2. 创建堡垒,打开 邀请,然后选择 复制邀请链接。
  3. 私下分享链接;批准与你朋友的设备指纹匹配的设备。
  4. 一起聊天、涂鸦和玩小游戏。
  5. 拆除堡垒,或让它自动过期。

堡垒消失,房间也就不复存在。

邀请窗口会确认链接已复制,并说明接下来的步骤:将链接粘贴给朋友,然后由主机批准其设备。手动分享代码/密码的选项位于 改用代码和密码 下方。

Agent 也可以担任主机

Agent 可以创建自己的堡垒,为人或其他 Agent 导出私密邀请链接,并在没有人工参与的情况下批准预期的对等方。主机授权仍然适用;操作者可以授权整个自主工作流,而无需逐项点击操作。

请从公开 Agent 指南或 Markdown 快速入门开始。可选择本地 stdio MCP/SDK/CLI、位于 https://mcp.pillowfort.xyz/mcp 的认证托管 MCP,或在受支持的浏览器标签页中使用原生 WebMCP。本地模式需要 Node 和显式安装的 Chromium;托管模式需要已签发的操作者密钥或 OAuth 授权。原生模式会检测功能是否可用,不会安装 polyfill。

网页 UI 和托管 MCP 共用 src/participant/:MLS 引擎、安全房间控制器、参与者本地状态、工具操作、绘图模型和游戏逻辑。托管 MCP 会在其会话 Durable Object 中直接运行此引擎,使用仅驻留内存的身份存储和出站加密 WebSocket,无需 Chromium。网页 UI 提供 IndexedDB、Web Locks、浏览器套接字和渲染适配器。托管浏览器到原生模式的切换需要在部署前排空旧服务;请参阅部署前提条件。

npm 包为 @ontologic/pillowfort-agent;GitHub 仓库仍为 slee1996。 MCP Registry 清单指向 io.github.slee1996/pillowfort 的 1.1.1 版。 托管参与者在受管理的托管环境中运行:其运行时和模型/操作者可以访问其获准加入的房间内容。托管参与者无法访问无关房间。邀请托管 Agent 前,请阅读托管指南。

原始的 1.0.0 下载仍归档在其 GitHub 发行版中。 当前安装说明会选择带版本号的 npm 包。

当前功能集

房间核心行为
  • 仅限邀请的房间,没有大厅,也无法发现房间
  • 由房主创建堡垒,并使用不透明的生成式堡垒代码
  • 房间状态为临时状态,无用户账户
  • 重名时自动添加后缀,例如 spencer2
  • 正在输入指示器
  • 房间范围内的在线状态,支持离开消息
  • 临时断开连接后提供重新连接宽限期
  • 房主离开时通过“扔枕头”迁移房主
  • 速率限制、访客人数上限和空闲房间自动销毁
聊天和用户体验
  • 受 AIM / Windows XP 启发的界面
  • 桌面和移动端布局
  • 为获准加入房间的参与者提供客户端 MLS 加密
  • 支持消息格式
  • 可从界面导出聊天记录
  • 私密邀请链接;新设备仍需获得房主批准
纯聊天之外的额外功能
  • 共享的 1200×800 画板,支持选择墨水颜色并导出 PNG
  • 枕头大战投票踢人
  • 石头剪刀布
  • 井字棋
  • 秘密破坏者
  • 山丘之王
  • 每个房间的排行榜和排队游戏流程
  • 由房主付费的 Fort Pass 方案,提供自定义代码、更长的空闲时长和高级 房间主题

临时数据模型

此应用的设计旨在避免长期保留房间历史记录:

  • 无用户账户
  • 新加入者无法回放消息历史
  • 不使用数据库存储聊天记录
  • 仅在 localStorage 中保留上次使用的屏幕名称

在生产环境中,Durable Object 存储仅用于在房间存续期间协调实时房间。堡垒销毁后,该房间状态也会清除。

架构

有两种行为大致相同的服务器运行时:

层本地开发生产环境
入口服务器BunCloudflare Worker
房间运行时内存中的 Map每个房间一个 Durable Object
客户端React + ViteReact + Vite
存储仅进程内存临时 DO 状态

贡献者须知:

  • 本地房间行为位于 server.ts
  • 生产环境房间行为位于 src/room.ts
  • 共享的验证、游戏、分析和闹钟辅助函数位于 src/

如果更改房间规则、WebSocket 行为、限制或游戏逻辑,通常需要同时更新两个运行时。

如需深入了解系统级架构,请参阅 ARCHITECTURE.md。

如需了解产品、业务和项目负责人的分析,请参阅 docs/PROJECT_LEAD_BRIEF.md。

如需了解生产状态和 Durable Object 休眠规则,请参阅 docs/PRODUCTION_STATE_POLICY.md。

如需了解 beta 测量规范和隐私限制,请参阅 docs/BETA_ANALYTICS.md。

如需了解 beta 发布步骤,请参阅 docs/PUBLIC_BETA_DEPLOY_CHECKLIST.md。

如需了解首个收入测试,请参阅 docs/FIRST_PAID_SKU.md。

如需了解付费 beta 支持和退款,请参阅 docs/FORT_PASS_SUPPORT_RUNBOOK.md。

如需了解当前的 Stripe 沙盒设置,请参阅 docs/STRIPE_TEST_SETUP.md。

如需了解生产环境加固和运维日志分类,请参阅 docs/PRODUCTION_MONITORING.md。

如需了解每周 beta 转化漏斗审查,请参阅 docs/METRICS_REVIEW.md。

如需了解 Discord 分发原型,请参阅 docs/DISCORD_ACTIVITY_SCOPE.md。

此应用当前公开的 API 接口:

  • /ws?room=...,用于房间 WebSocket 连接
  • /analytics,用于经过净化的 beta 转化漏斗事件
  • /api/fort-pass/code?code=...,用于检查自定义代码是否可用
  • /api/fort-pass/status,用于查询非机密的付费 beta 可用性和配置
  • /api/fort-pass/checkout,用于付费结账;仅在配置了 STRIPE_SECRET_KEY、FORT_PASS_PRICE_ID 和 PUBLIC_BASE_URL 时创建 Stripe Checkout Session
  • /api/stripe/webhook,用于经过签名验证的 Stripe Checkout 履约;仅在验证付款服务商事件后授予 Fort Pass 权益
  • /?fort_pass=success&code=...&session_id=...,用于结账后兑换无需账户的 Fort Pass

仓库布局

pillowfort/
├── client/                React + Vite frontend
│   ├── src/
│   │   ├── screens/       Home, setup, join, chat, knocked-down screens
│   │   ├── components/    XP UI, chat UI, games, overlays, canvas
│   │   ├── stores/        Zustand app state
│   │   └── services/      websocket protocol, message handling, chat crypto
│   └── dist/              built assets served by Bun / Cloudflare
├── src/
│   ├── index.ts           Cloudflare Worker entrypoint
│   ├── room.ts            Durable Object room runtime
│   ├── shared.ts          shared limits and sanitizers
│   ├── game.ts            shared pure mini-game rules
│   ├── analytics.ts       privacy-safe analytics sanitization
│   ├── entitlements.ts    host-only paid SKU entitlement helpers
│   ├── routes.ts          shared internal and public route constants
│   ├── stripe.ts          Stripe checkout and webhook helpers
│   └── alarms.ts          Durable Object alarm schedule helpers
├── server.ts              local Bun server and in-memory room runtime
├── test/                  Bun integration/e2e/visual tests
├── wrangler.toml          Cloudflare config
└── ARCHITECTURE.md        protocol and runtime design notes

前置要求

  • Bun 1.4.4 或更高版本
  • 支持维护的发布线上的 Node.js 22.23.4+、24.22.1+ 或 26.12.1+,以及 npm
  • 仅在需要部署时才需要 Cloudflare 账户

这些最低版本要求包含了 2026 年 10 月针对 eMudhra/emSign TLS 信任库的缓解措施。 仅更新 npm 依赖或操作系统信任库,无法修补较旧的 Node/Bun 二进制文件。请参阅安全维护。

安装

此仓库未设置为工作区。根目录和 client/ 是相互独立的包安装。

# root dependencies
npm install

# client dependencies
cd client
npm install
cd ..

marketing/ 也属于此仓库,并有自己的包安装:

npm --prefix marketing ci

编辑器、数据库和构建设置请参阅 marketing/README.md。 主应用构建不需要 marketing 包。

本地运行

先构建前端,然后启动 Bun 服务:

npm run build
npm run dev

打开 http://localhost:3000。

具体操作:

  • npm run build 会对客户端进行类型检查,并使用 Vite 构建 client/dist
  • npm run dev 会运行 bun --watch server.ts
  • server.ts 会提供构建后的客户端,并在内存中处理 WebSocket 房间状态

如果要修改前端代码,请先重新构建客户端,再重新加载 Bun 应用:

npm run build

此外,还有一个仅运行客户端的 Vite 脚本:

npm run dev:client

这适用于独立的前端开发,但完整的应用行为仍依赖于 server.ts 中的 WebSocket 后端。

AI Agent

Agent 与用户使用相同的浏览器客户端、MLS 加密、设备审批和房间权限。不存在明文机器人中继或特权 Agent API。 SDK 直接驱动一个有版本控制的客户端桥接接口,而非屏幕坐标或 DOM 选择器。只有在使用 ?agent=1 打开应用时才会安装该桥接接口;这一选择加入机制并非授权边界。

针对本地应用开发传输层

正常使用时,请遵循独立快速入门,并将传输层指向生产环境。只有在开发应用本身时,才需要执行以下检出/构建步骤。请使用受支持的 Node.js 版本并安装 Chromium:

npm ci
npm --prefix client ci
npx playwright install chromium
npm run build
npm start

在另一个终端中:

node scripts/agent.mjs discover --url http://localhost:3000
node scripts/agent.mjs jsonl --url http://localhost:3000

JSON 行模式会让命名会话在多个请求间保持活动状态。每行都会返回 {id, ok, data} 或 {id, ok:false, error:{code,message,retryable}} 结果:

{"id":1,"tool":"session_create","arguments":{"session":"alice"}}
{"id":2,"tool":"room_setup","arguments":{"session":"alice","input":{"displayName":"Alice","confirm":true}}}
{"id":3,"tool":"session_observe","arguments":{"session":"alice"}}

创建房间时会明确返回邀请凭据。请将这些结果视为敏感信息。设置/加入会返回一个已排队的操作,而不是连接成功的证明;请观察 connection 和 operations,或使用 session_wait 并传入最新的 revision。加入者会显示其待处理的指纹;主机必须核验该指纹,并使用匹配的 admission ID 和指纹调用 admission_approve。已连接的操作要求提供当前的 roomId,以防误发针对过期房间的命令。网络操作会如实报告排队状态;请检查观察结果以了解执行结果,而不要盲目重试会修改状态的操作。

room_setup 和 invitation_export 会返回完整且包含机密信息的 invitationUrl。Agent 可以使用该 URL、显示名称和 confirm:true 调用 room_join_link,而不必拆分提取房间/密码字段。请将 SDK 的 --url 设置为应用的基础来源;切勿将其配置为邀请链接。

面向用户的邀请会将密码放在 #invite=… 中,而不是查询参数或路径中。浏览器不会在 HTTP 请求中发送该片段。应用会将其读入临时内存,并在渲染前从地址栏中移除,然后要求用户输入姓名并明确执行加入操作。仍需主机批准。默认密钥保留 128 位随机性;这种便利功能不会削弱密码熵。

请将整个链接视为持有者凭证。你分享链接所用的消息应用、剪贴板历史记录、浏览器扩展程序,或你转发给的人,都可能看到该链接。清理 URL 无法追溯删除这些副本。仍可手动输入房间/密码。清理后重新加载时,需要重新打开已保存的邀请,或输入完全一致的原始密码;为方便起见,邀请密钥不会被持久化。

连接 MCP 客户端
{
  "mcpServers": {
    "pillowfort": {
      "command": "node",
      "args": [
        "/absolute/path/to/pillowfort/scripts/agent.mjs",
        "mcp",
        "--url",
        "http://localhost:3000"
      ]
    }
  }
}

使用用于 MCP 的直接 Node 命令,或使用 npm run --silent agent:mcp -- --url ...; 普通 npm 横幅不得进入 MCP 的 stdout 协议流。所选应用必须提供已更新且启用 Agent 的构建版本。URL 必须使用 HTTPS 或回环 HTTP,且不得包含内嵌凭据或邀请查询参数。

发现功能包括聊天格式化和历史记录、状态、正在输入提示、邀请导出、准入、绘图及保留的绘图历史记录、本地静音、主题、主机转移、房间生命周期、RPS、井字棋、枕头大战、秘密破坏者、山丘之王,以及真实的本地 Breakout 游戏。秘密破坏者需要四名成员;枕头大战需要三名成员。合法操作提示仅供参考,因为状态可能在消息送达前发生变化。对手的 RPS 选择会隐藏,直到揭晓;观察结果只会显示参与者自己的破坏者身份。

画板在手机和桌面屏幕上使用同一个 3:2 坐标平面。使用调色板选择墨水颜色,并使用 保存 PNG 导出浏览器已收到的绘图。绘图在加密后显示;指针环仅为本地预览,并不代表已送达。调整大小以及切换到聊天或 Breakout 都会保留当前画纸。新加入者仍无法收到此前的绘图。

除 drawing_send 外,Agent 还可以使用 drawing_color 和 drawing_export_png;sketchpad 观察结果会报告颜色、就绪状态和送达通知,但不会嵌入图像。导出操作需显式触发,且只包含画纸,不含房间凭据或浏览器界面。绘图队列饱和时会明确拒绝新批次,而不会悄悄丢弃已接受的笔画。

Fort Pass 工具可检查可用性、准备结账 URL,并使用同一浏览器保留的申领凭据兑换已完成的结账。它们不会完成付款,也不会自动跳转到 Stripe。具有破坏性或导出凭据的工具需要 confirm:true;这记录的是调用方意图,并不能证明获得了人工同意。运营者可以授权完整的自主托管/邀请工作流或常设策略;无需人工逐项批准操作。参与者撰写的内容不能授予此类权限,且属于不可信数据,绝不是给 Agent 的指令。

可复用的 PillowfortAgent 类从 scripts/agent-sdk.mjs 导出。其方法包括 createSession、capabilities、execute、observe、wait、listSessions、closeSession 和 close。务必在 finally 块中关闭它。会话使用相互隔离的临时 Chromium 存储;EOF、信号或显式关闭都会销毁本地身份和密钥。关闭浏览器不等同于发送 room_leave 或 room_end。会话不会自动持久化。

SDK 会根据房间大小保守地控制会产生中继流量的操作频率,为加密、准入和接收方确认留出余量,并遵循现有服务器限制。本地 Breakout 控制、观察和等待变化不受此频率控制影响。队列饱和时会返回 BUSY;共享流量仍可能耗尽服务器预算,因此请检查错误,切勿盲目重试变更操作。

默认限制为八个命名会话、1 MiB 输入、2 MiB 输出,以及 30 秒的变化等待。快照有大小上限;历史记录工具使用游标公开保留的数据,不包含加入前的历史记录。使用 --headed 检查实际的房间客户端。

可选的发布工具

添加 --cms-url https://about.pillowfort.xyz,并在需要时添加 --cms-storage-state /secure/path/editor-state.json。后者必须是显式提供的、已通过身份验证的浏览器状态文件;请像保护登录凭据一样保护它,绝不要将其提交到版本库。使用 --headed 并通过所有者密码登录。CMS 工具使用独立的浏览器上下文和经服务器验证的会话,绝不转发身份标头。 这些工具可以列出/读取草稿、管理文章和更新首页备注。所有写入操作都需要确认。关于 /api/agent,请参阅营销 README。

测试

核心测试套件
npm test

此命令运行稳定的核心测试套件:

  • 单元测试
  • Worker 入口点和 Durable Object 闹钟测试
  • 房间生命周期/WebSocket 集成测试
  • 游戏协议测试
  • 端到端邀请流程检查
类型检查
npm run typecheck

此命令会检查两个运行时环境:

  • 根据 Cloudflare Worker 类型环境检查 src/ 下的 Relay 模块
  • 根据浏览器 React 类型环境检查 client/src/ 及其共享的参与者依赖项

npm --prefix hosted-mcp run typecheck 还会根据 Workers 环境检查相同的参与者 引擎和托管适配器。

仅运行设计快照测试
npm run test:design-snapshots

此命令会启动 Playwright 并捕获关键 UI 状态。首次运行会将基线写入 test/__snapshots__/design/。后续运行会与这些基线进行比较,并在视觉差异超过配置的阈值时失败。

你也可以让快照运行器指向现有应用 URL:

PF_BASE_URL=http://localhost:3000 npm run test:design-snapshots
长篇 UI 编排测试
npm run test:ui

这些由 Playwright 驱动的测试套件复现了演示和宣传编排流程。它们运行较慢、更注重呈现效果,并与默认的公开仓库测试运行分开。

部署

使用根目录中的部署脚本:

npm run deploy

该脚本会:

  1. 构建客户端
  2. 部署 Cloudflare Worker
  3. 发布 wrangler.toml 中定义的 Durable Object 绑定

生产环境的路由方式如下:

  • /ws?room=abc12345 -> Worker -> 对应该房间的 Durable Object
  • /* -> 静态前端资源
  • /abc12345 -> 解析到 index.html 的 SPA 房间链接

营销站点会独立部署为 Cloudflare Worker pillowfort-marketing, 地址为 https://about.pillowfort.xyz。在此仓库中,运行 npm --prefix marketing run db:migrate 和 npm --prefix marketing run deploy。 根目录的部署命令只会发布位于 https://pillowfort.xyz 的应用。

营销站点的构建会直接使用此检出版本中的 Agent 1.1.1,而不是 重新下载较旧的 npm 工件。推广更新后的 npm 安装说明之前,请先发布该 tarball 并注册对应的 server.json 版本。在本地构建或测试不会发布 npm 包,也不会部署 Workers。

运行遥测

src/telemetry.ts 为应用、营销站点和托管 MCP 提供可选择启用的 HTTP 服务跟踪, 并在应用 Worker 和本地 Bun 运行时中提供未采样的房间总量事件。已提交的默认设置会在本地开发时禁用导出。 生产环境通过经过身份验证的入口和以下显式设置启用。

HTTP 跟踪只包含允许列表中的服务/环境/版本、操作类别、方法、状态和持续时间。房间/WebSocket 请求不会被跟踪。 不会进行自动插桩,也不会传入跟踪上下文。URL、房间 ID、 身份信息、IP 地址、凭据、请求正文、工具参数/结果、消息或绘图内容、异常消息和堆栈永远不会被导出。

固定版本的 Caddy/Collector/Tempo/Prometheus/Grafana 堆栈会独立验证并清理输入。HTTP span 会发送到 Tempo 和采样后的运行指标。 单独的 pillowfort.volume 插桩作用域只通过 OTLP 传递四种固定的计数事件名称。其 Collector 管道会生成 pillowfort_volume_calls_total,且不会在 Tempo 中存储房间活动跟踪数据。不会收集房间/设备/会话标签或每个房间的历史记录。

仪表板会显示已创建/停用的房间数、新成员获准加入的次数和已接受的加密应用事件数,以及相应速率。应用事件包括聊天、 输入状态、在线状态、绘图和游戏:服务器无法识别仅聊天的流量。 重传、投递 ACK、重放积压和接收方扇出不会增加消息计数。请参阅指标定义。 计数不受 HTTP 跟踪采样影响,包括 OTEL_SAMPLE_RATE=0 时;OTEL_ENABLED=false 会同时禁用两者。导出有上限且采用尽力而为的方式,因此这些是观测到的运行计数,而非计费、累计、独立用户、留存或活跃房间指标。缺失数据不等于零。不会回填历史数据。采集器故障不得影响房间运行。

生产遥测运行在共享的 Linux MacBook Pro 上,地址为 [email protected],路径为 /home/agent/workspaces/pillowfort-observability-20261027.azBvVd。 采集不再依赖这台工作站或其 Docker 持续运行; Linux 主机、网络连接和 rootless Docker 必须保持运行。

部署的 private/observability.env 提供 GRAFANA_ADMIN_PASSWORD 和 OTEL_INGEST_PASSWORD_HASH(用户 otel 的 bcrypt 接入密码哈希)。 该文件仅限所有者访问,权限为 0600。所有者直接配置了专用隧道凭据,并将权限保留为 0400。没有转移 SSH 密钥、提供商身份验证凭据或范围广泛的 Cloudflare 源站证书。

Grafana 仍仅监听环回地址。在 Mac 上,打开专用 SSH 转发:

ssh -N -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:13000:127.0.0.1:13000 [email protected]

然后访问 http://localhost:13000,用户名为 admin。密码未更改,仍保存在 Mac 的私有 ~/.config/pillowfort/observability.json 中。 该 JSON 还包含单独的接入密码;切勿提交或分享。 SSH 转发仅用于访问仪表板,采集不需要它。

命名 Docker 卷会持久保存跟踪数据、指标和 Grafana 状态;跟踪数据和指标的保留期为七天。除非打算删除这些历史数据,否则不要使用 docker compose down -v。已停止的 Mac 卷仅用于回滚;保留策略不会清理已停止的备份。Linux 连接器运行时,切勿重启 Mac 上的连接器。

Grafana 容器保留只读文件系统,并禁用后台插件自动更新。否则,Grafana 13 可能会在只读更新失败前卸载内置数据源插件,导致仪表板无法查询 (上游缺陷)。 应有计划地更新经过审核的镜像和插件版本;不要为了修复此故障而使镜像可写或禁用身份验证。

Grafana 的 Go 软内存上限明确设为 384MiB;其容器上限为 1g,为单独运行的内置数据源后端进程留出余量。rootless 冒烟测试发现,无上限的 Go 堆会逼近先前的 512 MiB 上限并触发交换。限制堆内存并测得进程余量后,相同的真实仪表板工作负载运行完成,未发生交换、cgroup 上限触发或容器重启。这些是冒烟测试的观测结果,不代表持续负载下也能保证如此。

稳定的接入主机名仍为 https://otel.pillowfort.xyz,由专用的 pillowfort-observability Cloudflare Tunnel 提供支持。该隧道仅路由到经过身份验证的 Caddy 接入端点;其他主机名或路径均返回 404。Grafana 不通过公共隧道暴露。固定版本的连接器以只读方式运行,带有原生就绪检查,并使用 restart: unless-stopped;此次主机迁移无需新的隧道登录或 Worker/npm 发布。

部署的 private/tunnel.env 仅包含专用隧道 ID、凭据文件路径和容器可读取的 UID/GID。使用 rootless Docker 时,请设置 PILLOWFORT_UID=0 和 PILLOWFORT_GID=0:容器中的 root 会映射到无特权的守护进程所有者,而不是主机 root。在容器内使用的主机 UID 1001 无法读取仅限所有者访问的凭据绑定挂载。请将该文件权限保持为 0400 或 0600,并以只读方式挂载;不要放宽权限或挂载管理证书。 Docker Engine 29.9.2 和 Compose 5.6.1 来自 Docker 已签名的 Ubuntu 软件源。 守护进程以 agent(主机 UID 1001)身份运行,并通过已启用的 systemd 用户服务启动; 已启用 linger。由 root 所有的 Docker、Docker socket 和 containerd 服务 仍处于屏蔽状态。所需的 nf_tables 模块已写入 /etc/modules-load.d/pillowfort-rootless-docker.conf,全局 AppArmor 对非特权用户命名空间的限制 仍保持启用。

已测试由内核强制执行的内存、CPU 和 PID 限制,以及守护进程重启后自动恢复容器的行为。未重启主机。Rootless Docker 使用 seccomp,但不会应用每个容器独立的 AppArmor 配置文件;当前 cgroup 配置不支持 cpuset 和磁盘 I/O 限流。现有的 Resin8 和 OMP 工作负载未作更改。

启动/恢复完整的 Linux 堆栈,且不删除持久化数据:

ssh [email protected]
systemctl --user start docker.service
cd /home/agent/workspaces/pillowfort-observability-20261027.azBvVd
docker --context rootless compose \
  --env-file private/observability.env \
  --env-file private/tunnel.env \
  -f observability/compose.yaml -f observability/compose.ingress.yaml \
  up -d --wait --wait-timeout 90

Compose 的后端等待检查的是容器状态,而非每个 HTTP 服务是否已就绪。 Tempo 启动时可能返回 503;Grafana 可能会暂时报告数据库故障。启动后检查实际就绪状态;如有必要,等待后重复检查:

docker --context rootless exec pillowfort-observability-ingress-1 sh -ec '
  wget -qO- http://collector:13133/healthz
  wget -qO- http://tempo:3200/ready
  wget -qO- http://prometheus:9090/-/ready
  wget -qO- http://grafana:3000/api/health
'

若将来进行迁移,请在不启动 cloudflared 的情况下准备后端,停止存储写入程序以确保卷副本一致,并在 rootless 容器内部恢复数字所有者。通过受控的单连接器切换,确保所有四个 HTTP 就绪端点均已就绪。如果两个连接器同时运行,Cloudflare 可能会将事件分散到彼此独立的收集器。冷切换可能会丢失尽力而为的导出数据;收集器重启会重置其内存中的计数器周期,但不会影响持久化历史记录。

2026-10-27 的切换保留了历史计数器快照和已存储的追踪数据,随后验证了新的未采样房间计数、来自全部三个生产服务的 HTTP 追踪,以及 Linux 上实际的 Grafana 仪表板。房间活动仍未出现在 Tempo 中。旧的 Mac 堆栈已停止,其卷完整保留,可用于回滚。

仓库中检入的 OTEL_ENABLED=false 是有意设置的:本地开发不得发送生产事件。验证经过身份验证的入口后,通过受保护的部署显式启用生产绑定。对于应用:

npm run deploy -- --keep-vars \
  --var OTEL_ENABLED:true \
  --var OTEL_EXPORTER_OTLP_ENDPOINT:https://otel.pillowfort.xyz \
  --var OTEL_DEPLOYMENT_ENVIRONMENT:production \
  --var OTEL_SAMPLE_RATE:0.1 \
  --var OTEL_SERVICE_VERSION:$(git rev-parse --short HEAD)

在 marketing 的受保护部署和 hosted-MCP 部署中也应用相同的标志;后者需先运行 npm run check。--keep-vars 会在后续发布中保留生产环境的显式启用状态;显式标志会覆盖本地/源代码默认值。HTTP 追踪的采样率为 10%;聚合房间计数不采样。若要停止导出,请使用显式的 OTEL_ENABLED:false 覆盖项进行部署。

OTEL_EXPORTER_OTLP_HEADERS 是一个 Worker 密钥,其中包含摄取授权信息和真实的 Pillowfort-OTel/1.0 应用 user-agent。切勿将密码或编码后的授权信息放入受版本控制的变量、命令参数或日志中。缺少配置或配置无效时,导出会保持禁用。激活后,请在 Grafana 中验证新的生产计数和服务追踪;部署成功本身并不能证明数据已被接收。只有合成的隔离冒烟测试数据才应选择 test。Cloudflare 原生自动追踪仍处于禁用状态。Quick Tunnels 不是永久的数据目的地。生产收集器现在依赖 Linux 主机,而非这台 Mac;只有查看 Grafana 时才需要私有 SSH 转发。

值得优先阅读的内容

如果你想快速了解这个应用,可以从这里开始:

状态

这个代码仓库已经不只是一个玩具聊天模拟应用,其中已经包含:

  • 两种服务端运行时
  • 重新连接和房主交接逻辑
  • 浏览器端加密的聊天负载
  • 房间范围内的在线状态
  • 多人小游戏
  • 集成测试
  • 视觉回归测试覆盖
  • 独立软件包中的动态设计素材

如果你要进行架构变更,请先阅读 ARCHITECTURE.md,再编辑房间运行时。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。