关于 pillowfort
pillowfort 是供 Agent 和个人使用的临时私密聊天室,可实时聊天、涂鸦和玩小游戏。房间通过邀请加入,新设备需房主批准;本地与托管 MCP 均可使用。
主要功能
- 01邀请制临时房间
- 02端到端 MLS 加密
- 03主机批准新设备
- 04实时聊天与共享画板
- 05内置多人小游戏
- 06本地与托管运行模式
适用场景
- 01Agent 与人员进行私密交流
- 02邀请其他设备加入临时协作
- 03在共享画板上共同涂鸦
- 04在房间内进行多人小游戏
MCP 相关
提供本地 stdio MCP、需授权的托管 MCP,以及受支持浏览器中的原生 WebMCP。MCP 工具可让 Agent 创建房间、导出邀请并管理加入审批。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://mcp.pillowfort.xyz/mcpclaude mcp add --transport http slee1996-pillowfort https://mcp.pillowfort.xyz/mcp{"mcpServers":{"slee1996-pillowfort":{"type":"http","url":"https://mcp.pillowfort.xyz/mcp"}}}在 Cursor 中打开{"servers":{"slee1996-pillowfort":{"type":"http","url":"https://mcp.pillowfort.xyz/mcp"}}}在 VS Code 中打开{"mcpServers":{"slee1996-pillowfort":{"type":"http","url":"https://mcp.pillowfort.xyz/mcp"}}}{"mcpServers":{"slee1996-pillowfort":{"serverUrl":"https://mcp.pillowfort.xyz/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
详细介绍
pillowfort
小巧、私密、用完即弃的聊天室,充满 AIM / Windows XP 的风格。
搭建一个堡垒,分享一条私密邀请链接,实时畅聊,然后拆除它。无需账号。没有公开房间列表。新设备仍需获得主机批准,且不会收到此前的聊天记录。
本仓库包含的内容
pillowfort 同时是:
- 一款带有临时房间的实时聊天应用
- 一项双运行时实验,可在本地通过 Bun 运行,也可在生产环境中通过 Cloudflare Workers + Durable Objects 运行
- 一款注重设计的前端,并提供浏览器和快照测试覆盖
核心产品理念很简单:
- 选择一个屏幕名称,并保存生成的房间密码。
- 创建堡垒,打开 邀请,然后选择 复制邀请链接。
- 私下分享链接;批准与你朋友的设备指纹匹配的设备。
- 一起聊天、涂鸦和玩小游戏。
- 拆除堡垒,或让它自动过期。
堡垒消失,房间也就不复存在。
邀请窗口会确认链接已复制,并说明接下来的步骤:将链接粘贴给朋友,然后由主机批准其设备。手动分享代码/密码的选项位于 改用代码和密码 下方。
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 存储仅用于在房间存续期间协调实时房间。堡垒销毁后,该房间状态也会清除。
架构
有两种行为大致相同的服务器运行时:
| 层 | 本地开发 | 生产环境 |
|---|---|---|
| 入口服务器 | Bun | Cloudflare Worker |
| 房间运行时 | 内存中的 Map | 每个房间一个 Durable Object |
| 客户端 | React + Vite | React + 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/distnpm run dev会运行bun --watch server.tsserver.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
该脚本会:
- 构建客户端
- 部署 Cloudflare Worker
- 发布
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 转发。
值得优先阅读的内容
如果你想快速了解这个应用,可以从这里开始:
server.ts:本地运行时src/index.ts:Cloudflare 请求路由src/room.ts:生产环境的房间行为src/game.ts:共享的小游戏规则辅助函数src/analytics.ts:隐私安全的分析验证src/security.ts:扫描器拦截和响应标头src/entitlements.ts:仅供房主使用的付费 SKU 权益辅助函数src/alarms.ts:生产环境的闹钟调度辅助函数client/src/services/protocol.ts:WebSocket 消息结构client/src/stores/gameStore.ts:客户端状态client/src/screens/ChatScreen.tsx:主要 UI 界面test/integration.test.ts:预期的房间行为test/worker.test.ts:Worker 路由和 Durable Object 闹钟行为docs/PROJECT_LEAD_BRIEF.md:产品策略和变现方向docs/PRODUCTION_STATE_POLICY.md:Durable Object 状态规则docs/BETA_ANALYTICS.md:隐私安全的 Beta 分析docs/PUBLIC_BETA_DEPLOY_CHECKLIST.md:公开 Beta 发布步骤docs/PRODUCTION_MONITORING.md:边缘加固和运维分类docs/FIRST_PAID_SKU.md:首个仅供房主使用的付费产品docs/DISCORD_ACTIVITY_SCOPE.md:Discord Activity 原型范围
状态
这个代码仓库已经不只是一个玩具聊天模拟应用,其中已经包含:
- 两种服务端运行时
- 重新连接和房主交接逻辑
- 浏览器端加密的聊天负载
- 房间范围内的在线状态
- 多人小游戏
- 集成测试
- 视觉回归测试覆盖
- 独立软件包中的动态设计素材
如果你要进行架构变更,请先阅读 ARCHITECTURE.md,再编辑房间运行时。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。