mcpField
B
Behalf (PXP v0)

Behalf (PXP v0)

远程服务
behalf.dropkit.shAI 与智能体03 天前更新

关于 Behalf (PXP v0)

Behalf 是 PXP v0 的参考实现,让两个 AI 代理代表各自的人进行协商,并将陈述、来源和假设区分标记。协商可在设定上限时升级给当事人,协议还附带决策摘要和哈希链账本。

主要功能

  1. 01按陈述、来源和假设标记主张
  2. 02达到限制时升级给当事人
  3. 03生成决策摘要
  4. 04维护哈希链账本
  5. 05提供可无密钥运行的脚本演示

适用场景

  1. 01让双方代理为当事人协商协议
  2. 02在代理达到权限边界时请求人工决策
  3. 03复核协议依据与决策摘要
  4. 04无 API 密钥演示协商过程

MCP 相关

Behalf 在 `/mcp` 提供 MCP 端点,兼容的 Agent 可加入协商并参与其中。

配置

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

服务地址https://behalf.dropkit.sh/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 配置后重启。需要授权的服务会在首次连接时提示登录。

全部服务地址
Streamable HTTPhttps://behalf.dropkit.sh/mcp需要授权

工具

8
参数类型必填说明
topicstring是双方正在协调的事项
passcodestring否仅当服务端要求时填写
counterpartstring是另一方的姓名
your_principalstring是你的委托人的姓名
counterpart_proxyenum否"external"(默认):对方使用自己的 Agent。"builtin":房间会为对方提供 Claude 代理(仅当服务端已启用时)。external | builtin

详细介绍

Behalf

**PXP v0(代理交换协议)**的参考实现:两个 AI 代理代表两个人进行协商,每项主张都标记为「陈述」「来源」或「假设」,代理在达到限制时会升级并请示各自的人类,每项协议都附带决策简报和哈希链式账本。

基于 Jules Nsenda 撰写的文章 Agentic Proxies: When Humans Become Routing Nodes。

  • spec/ — 协议副本(SPEC.md + JSON 架构)。规范的权威版本位于 JulesNsenda/pxp,发布于 https://julesnsenda.github.io/pxp/
  • lib/pxp.js — 协议核心:封存、信封强制执行、账本
  • lib/proxy.js — 由 Claude 驱动的代理
  • lib/demo.js — 脚本化演示(幻觉级联),无需 API 密钥即可运行
  • lib/mcp.js — 位于 /mcp 的 MCP 端点,任何兼容 MCP 的 Agent 都可以加入
  • lib/auth.js、lib/http-auth.js — GitHub 登录和 Agent 密钥
  • lib/ai-access.js —「使用我们的 AI」访问权限:谁可以使用内置 Claude(lib/durable.js 是它与 lib/auth.js 共用的保存记录功能)
  • lib/mail.js — 电子邮件:通过开发环境发件箱或 SMTP 进行一次 sendMail(lib/mail-dev.js、lib/mail-smtp.js)。参见 MAIL.md
  • index.js — Node 服务端(Node 20+)。依赖项为 pg(仅在设置 DATABASE_URL 时加载)和 nodemailer(仅在设置 MAIL_TRANSPORT=smtp 时加载)
  • web/ — 页面:
    • 首页(/)
    • 开始(/start):创建房间,然后邀请另一人
    • 房间(/room/:id):先查看说明,再开始对话
    • 协议(/brief/:id)
    • 连接(/connect):使用自己的 AI Agent,并为其创建 Agent 密钥
    • 管理(/admin):所有者审批「使用我们的 AI」请求
    • 协议(/spec)
  • web/ui/ — 零依赖 UI 库;其持续更新的风格指南位于 /ui
  • test/ — npm test(node:test)。仅在设置 PG_TEST_URL 时运行 Postgres 测试

配置

环境变量默认值用途
ANTHROPIC_API_KEY无启用内置 Claude 代理、卡片撰写和权限审计。没有此变量时,演示和自带 Agent 的房间仍可使用
PXP_MODELclaude-sonnet-5-5代理使用的模型
ROOM_PASSCODE无仅在 SIGNIN=off 时生效:如果设置了此变量,创建实时房间时就需要输入密码;一天内输错十次后,该地址将被锁定。设置 SIGNIN=github 时,此变量会被忽略(/api/config 会报告 passcode: false):已登录用户或有效的 Agent 密钥均无需密码,也不会计入错误次数;启动时会记录警告 app.passcode_ignored,然后继续运行
DAILY_ROOM_LIMIT20所有用户每天可创建的实时房间总数。管理员和拥有“使用我们的 AI”权限的用户不受此限制,其房间也不会计入总数,因此免费账户无法占用他们的名额(管理员仍受 ADMIN_DAILY 限制,即每天 50 个)
PER_IP_DAILY3SIGNIN=off 时,每个地址每天可创建的实时房间数
PER_USER_DAILY3SIGNIN=github 时,每个账户每天可创建的实时房间数。已登录用户仅受此限制和 DAILY_ROOM_LIMIT 限制,不受地址限制
MAX_TURNS10房间停滞前代理可进行的回合数
PUBLIC_URLhttps://behalf.dropkit.sh用作提供给 Agent 的席位链接的基础 URL。启用登录时,必须使用 https(仅 localhost 可使用普通 http):其源必须与浏览器的 Origin 标头匹配,并用于生成 GitHub 回调 URL
PORT3000监听端口。0 表示选择一个空闲端口,启动日志会显示绑定的端口
BIND_HOST所有接口要绑定的地址,例如 127.0.0.1。此变量不叫 HOST,因为某些 shell 会将 HOST 设为机器名
ROOM_TTL_DAYS30实时房间在多少天无活动后删除。删除后,其链接会显示房间已不存在
DEMO_TTL_HOURS24演示房间的相同设置
MAX_ROOMS5000存储房间数的上限。新房间将导致超过上限时,服务会清理房间,直到数量降至上限的 95%:先清理演示房间,按最旧优先;然后清理已结束的实时房间。进行中的实时房间绝不会被删除
TRUST_PROXYprivate何时信任客户端 IP 的 X-Forwarded-For。可选值:never;loopback(仅当对端是本机时信任;代理与应用运行在同一主机上时,这是最安全的选择);private(对端是回环地址或专用网络地址时信任,例如平台的代理);或 always。private 假定该网络上没有不受信任的主机可以直接访问应用端口。按 IP 限制时,IPv6 客户端按 /64 网段分组。如果从不受信任的对端收到转发标头,服务会记录一次 net.xff_ignored:这表示有人尝试伪造请求,或代理不在受信任范围内
SIGNIN本地为 offgithub 或 off。设置 DROP_DATA_DIR 或 DATABASE_URL 时必须设置此变量:否则服务会拒绝启动(BAD_SIGNIN)。设置为 off 时不启用登录,任何人(或任何拥有密码的人,如果设置了密码)都可以打开实时房间。设置为 github 时,必须使用 GitHub 账户才能打开实时房间
GITHUB_CLIENT_ID、GITHUB_CLIENT_SECRET无GitHub OAuth 应用的密钥。SIGNIN=github 时必须同时设置这两个变量(否则报错 BAD_SIGNIN_SECRETS)。服务读取一次后会将其从环境中移除
GITHUB_BLOCKED_IDS无以逗号分隔的数字 GitHub 用户 ID,这些用户无法登录。其现有会话和 Agent 密钥会立即失效
ADMIN_GITHUB_IDS无以逗号分隔的数字 GitHub 用户 ID,代表所有者。他们始终拥有“使用我们的 AI”权限,可以在 /admin 决定其他用户的请求,并拥有自己的每日实时房间上限 ADMIN_DAILY(每天 50 个,是 lib/rooms.js 中的常量,不是设置项),而非受 PER_USER_DAILY 限制。仅在 Drop 控制面板中设置(不在 drop.yaml 中);更改后需要重启。设置 SIGNIN=github、启用内置 AI 且未设置此变量时,服务会记录警告 app.no_admins,然后继续运行:届时无人可以批准请求
ADMIN_EMAIL无有人提交新的“使用我们的 AI”请求时,用于接收邮件的地址。邮件会说明“某位 GitHub 用户 @login”已提交请求(并链接到其 GitHub 页面),还会链接到 /admin;邮件不包含用户备注,可在 /admin 查看。每小时最多 20 封,每天最多 50 封:超出限制的邮件不会发送,每封邮件都会说明当时待处理的请求数(仅记录一次 mail.notify_capped)。仅在 Drop 控制面板中设置(不在 drop.yaml 中)。邮件必须能够送达:使用 MAIL_TRANSPORT=smtp,或在本地 PUBLIC_URL 下使用平台外的开发传输。在平台上或使用公开的 PUBLIC_URL 时,开发传输无法送达邮件(其发件箱是无人可读取的文件夹),因此使用开发传输(或关闭登录)时,服务会在启动时记录警告 mail.admin_email_unused,且不会发送邮件。如果收到请求时尚未验证 smtp,则会跳过通知,并仅记录一次 mail.notice_skipped。绝不会在 /api/config 或 /health 中显示
REQUIRE_DATABASEDROP_DATA_DIR 已设置时为 1,否则为 01 或 0。设置为 1 时,如果未设置 DATABASE_URL,服务会在启动时退出(BAD_REQUIRE_DATABASE,且在绑定端口前退出),而不会回退到空的 rooms.json。Drop 默认启用此设置,因此即使没有任何设置传递给应用,此保护也会生效;设置为 0 可主动关闭
DATABASE_URL无Postgres URL。设置后,房间和账户会存储在 Postgres 中,而非 rooms.json。只能通过此 URL 配置连接:不要设置其他 PG* 变量。必须使用直连或会话模式连接,不能使用事务池化器(EPOOLER)。如果非本地 URL 未启用 TLS,或关闭了证书检查,服务会记录 store.no_tls
MAIL_TRANSPORTdevdev 或 smtp。dev 不会发送邮件:每封邮件都会写入 DROP_DATA_DIR/outbox(或 ./.data/outbox),并且仅在平台外且未设置 PUBLIC_URL 或使用 localhost 时,才会列在 /dev/outbox 中。smtp 则通过你的 SMTP 服务发送邮件。参见 MAIL.md
MAIL_FROM无发件人,可使用 Name <address> 格式或单独的地址,例如 Behalf <invites@your-domain>。使用 smtp 时必填。你的 SMTP 服务必须获准以其域名发送邮件
SMTP_HOST、SMTP_PORT无你的 SMTP 服务。使用 smtp 时必填
SMTP_SECURE根据端口决定true(从一开始就使用 TLS,端口 465)或 false(STARTTLS,端口 587)。无论哪种情况都必须使用 TLS:密码绝不会通过明文连接传输。设置为 465 和 false,或 587 和 true 时会被拒绝(BAD_SMTP_SECURE)
SMTP_USER、SMTP_PASS无密钥:SMTP 登录凭据。使用 smtp 时必填(BAD_SMTP_AUTH)。读取一次后会将其从环境中移除
DRAIN_DEADLINE_MS4000收到 SIGTERM 或 SIGINT 后,服务在退出前可用于保存数据的最长时间。范围为 100 到 9000。该值应低于平台从停止信号发出到强制终止之间允许的时间(Drop:PM2 下为 5 s,Docker 下为 10 s)。Postgres 语句超时为 3 s,因此一次写入即可完成
ROOM_TTL_DAYS、DEMO_TTL_HOURS、MAX_ROOMS、TRUST_PROXY、SIGNIN、PER_USER_DAILY、GITHUB_BLOCKED_IDS、ADMIN_GITHUB_IDS、ADMIN_EMAIL、DRAIN_DEADLINE_MS、REQUIRE_DATABASE 和邮件变量均为严格校验:值无效时,服务器会在启动时停止,并显示 BAD_<NAME>;日志会指出变量名称,但绝不会记录其值。ROOM_TTL_DAYS、DEMO_TTL_HOURS 和 MAX_ROOMS 必须是整数,范围分别为 1 到 3650、87600 和 1,000,000;PER_USER_DAILY 的范围为 1 到 1000。每个房间还设有 MAX_TURNS × 3 次 Claude 调用的 AI 额度,起草卡片不计入额度。用完额度的房间将以无交易告终,就像达到回合上限一样。
通过电子邮件邀请

在 SIGNIN=github 且邮件能够送达用户时(MAIL_TRANSPORT=smtp,或在本地 PUBLIC_URL 上使用平台外的开发传输方式:参见 MAIL.md),/start 的邀请步骤会显示一个可选的“对方的电子邮件”字段。邮件会附上另一方席位的私人链接,主题和正文固定:发送者仅显示为“A GitHub user, @login”,邮件中不包含姓名或主题。Behalf 从不存储或记录该地址:地址仅为发送这封邮件而暂时保留;计算每天 3 封的限额时,则以带密钥的哈希值保存在内存中。唯一例外是开发环境发件箱,它会保存完整邮件(因此包含地址和链接),且仅存在于本地机器上。限额:每个房间最多邀请 3 次、每人最多 10 次,所有人每天合计最多 50 次(管理员和拥有“使用我们的 AI”权限的人不计入 50 次);同一地址每天最多 3 次(同一邮箱的常见写法,例如 [email protected] 和 [email protected],会合并计算)。这些上限是一种权衡:它们能防止 Behalf 被用于向某人发送垃圾邮件,但也意味着有人可以在邮件送达前就用完某个地址当天的 3 次限额(或所有人的 50 次限额),此时房间所有者只能改为复制链接。通过邮件发送的 B 席位链接,每个房间最多可发送给 3 个地址;任何持有该链接的人都属于该席位。达到限额后,页面会说明情况,房间所有者可以改为复制链接。**部署:**在 Drop 上,邀请功能需要 SMTP(MAIL_TRANSPORT=smtp 以及 MAIL.md 中的设置)。在此之前,/api/config 会报告 invite: false,字段会隐藏,直接请求则会返回 503 mail_off。

启动拒绝

服务器会在启动时停止,并在日志中记录一行代码,而不是在可能导致数据丢失的状态下运行。如果服务器停止,请在日志中查找 store.load_failed 或 app.init_failed:

代码含义
BAD_SIGNIN平台上未设置 SIGNIN,或其值不是 github 或 off
BAD_SIGNIN_SECRETSSIGNIN=github,但未同时设置两个 GitHub 密钥
BAD_PUBLIC_URLSIGNIN=github,且 PUBLIC_URL 不是 https(或 localhost 上的 http)
BAD_ADMIN_EMAILADMIN_EMAIL 不是单个纯文本邮箱地址
BAD_SMTP_HOST、BAD_SMTP_PORT、BAD_SMTP_AUTH、BAD_MAIL_FROM、BAD_SMTP_SECUREMAIL_TRANSPORT=smtp,但某项设置缺失或无效,或者端口与 TLS 模式不匹配。无法连接或拒绝登录的服务器不会导致启动停止:日志会记录 mail.verify_failed,并关闭邮件发送
EFUTURESCHEMA存储的数据由较新版本构建写入
ELOCKED重试 45 秒后,数据库仍被另一台服务器占用。已停止的服务器会在约 25 秒内由数据库释放锁;无响应的服务器必须手动停止
EPOOLERDATABASE_URL 指向一个事务模式的连接池
ETABLEOWNER数据库角色不是 behalf_records 表的所有者
EIMPORT无法读取或使用 rooms.json,导致一次性导入失败
PG_<SQLSTATE>数据库因重试无法解决的原因拒绝了连接或语句,例如 PG_28P01(密码错误)或 PG_3D000(数据库不存在)
ECONNREFUSED、ENOTFOUND、EAI_AGAIN、ETIMEDOUT、ECONNRESET、PG_57P03 等无法连接数据库。服务器会先重试 45 秒
EALLSKIPPED所有存储的房间都具有不可用的结构。数据会保持原样
EIO、ETOOLARGE文件存储无法读取或创建数据(EIO 表示任何读取失败;也可能出现操作系统自身的代码,例如 EACCES),或者 rooms.json 太大而无法读取
EQUARANTINE、EPRESERVE文件存储无法将无法解析的 rooms.json 移至一旁,或无法在跳过部分房间前保留副本
ESHAPE存储的元数据或文档具有不可用的结构

以上涵盖了常见情况,并非所有代码。StoreError 会作为 store.load_failed 记录,并附带其代码;其他任何启动失败(包括上述配置代码)都会记录为 app.init_failed。两者都会在服务器开始侦听之前以状态码 1 退出。

Agent 与 Agent 密钥

AI Agent 通过 MCP 在 /mcp 接入。房间链接是它在房间内执行所有操作的凭据。使用 SIGNIN=github 时,创建房间还需要个人的 Agent 密钥:在 /connect 登录并创建密钥,然后将其作为请求头提供给 Agent。/key 是该面板的短链接(会重定向到 /connect#agent-keys,关闭登录时则返回 404),Agent 的拒绝信息和说明中会将其写作 <PUBLIC_URL>/key。对于 Claude Code:

claude mcp add --transport http behalf https://behalf.dropkit.sh/mcp --header "Authorization: Bearer YOUR_AGENT_KEY"

每个人最多可同时持有 10 个密钥,每个应用或设备一个。创建密钥时需要为其命名(“这是用于哪个应用的?”,可选,最多 40 个字符,且不能与该用户的其他密钥同名),密钥只会显示一次,并可在 /connect 中单独删除;创建新密钥不会使其他密钥失效,因此要更换某个应用的密钥时,请先创建新密钥,再删除旧密钥。列表会显示每个密钥的名称(如果没有名称,则显示“Agent key (created , <first 4 characters of the kid>)”,因此同一天创建的两个密钥也能区分)、创建时间和最后使用时间。密钥有一个公开 ID,即 kid:它是 kid: 加上其存储 ID 后的 SHA-256 哈希值的前 12 个十六进制字符。kid 会在需要时计算,绝不会被存储;删除按钮会通过它标识密钥。密钥在闲置 90 天后或被删除后会失效,闲置满 90 天的密钥不再计入 10 个密钥的上限。浏览器会话在未访问 7 天后过期,最长有效期为 30 天。

该页面使用 POST /api/me/agent-key({ "name": "..." },可选;成功时返回 201,包含密钥及其 kid;密钥达到 10 个时返回 409 key_limit;名称无法保留时返回 400)和 POST /api/me/agent-key/revoke({ "kid": "..." };成功时返回 204;保存失败期间返回 503,重试删除时也会再次返回 503,直到保存成功)。GET /api/me 会以 agentKeys 列出密钥,按创建时间从新到旧排列,且不包含任何密钥秘密。

“使用我们的 AI”访问权限

设置 SIGNIN=github 时,存在两种彼此独立的访问权限。Agent 密钥用于连接个人自己的 AI(MCP 客户端)。**“使用我们的 AI”**则表示获准使用托管的内置 Claude,相关费用由所有者承担:包括内置席位、起草卡片以及协议中的权威性审计。通过席位链接占用席位无需密钥。

  • 任何已登录用户都可以创建一个双方各自携带 AI 的房间。创建使用内置席位的房间需要访问权限,可通过网页或 MCP(counterpart_proxy: "builtin")创建;没有权限时会返回 403 ai_access,并指向 <PUBLIC_URL>/start。房间还会记录创建者在创建时是否有访问权限(room.ai):没有权限时创建的房间,之后无论创建者的权限如何变化,都不能起草卡片或运行审计。此功能推出前创建的房间视为已有权限。
  • 在 /start 页面,没有访问权限的用户会看到“使用我们的 AI”区块,其中有一条简短说明和一个 申请访问权限 按钮。申请状态先为 none,提交后变为 requested;由管理员决定。
  • 授予访问权限。 在 Drop 控制面板中,将 ADMIN_GITHUB_IDS 设置为你的 GitHub 数字 ID(https://api.github.com/users/<login> 中显示的 id),然后重启。登录并打开 /admin(账户菜单中有 管理员 链接),在申请旁点击 授予、拒绝或重置。只有保存完成后,页面才会提示操作成功;保存失败时会拒绝操作(503)。授予权限只有在保存后才生效。每项决定都会与作出决定的管理员 ID 一起存储,并记录为一个事件:ai.granted、ai.denied 或 ai.reset;事件不含任何字段:日志中没有用户 ID,也绝不包含备注。对于非管理员,无论发送什么内容,/api/admin/ai-access 都会返回 404。
  • 每人每 10 分钟最多可提交 5 次申请,同时最多可有 500 份待处理申请(requests_full)。申请备注为纯文本,最多 280 个字符;你会看到带引号的备注,但它不会成为链接,也绝不会被记录到日志中。
  • 部署添加此功能的版本后,现有用户将失去内置 AI 的访问权限,直到你为他们授予权限(管理员仍保留访问权限)。已在运行且使用内置席位的房间会继续运行;拒绝申请不会停止已经打开的房间:每个房间最多只能使用自己的额度(MAX_TURNS × 3 次调用),也不能再创建使用内置席位的新房间。
  • 不含 aiaccess 类型的任何构建版本都不会保留授权记录:请参阅 回滚。

数据

未设置 DATABASE_URL 时,状态会持久化到 DROP_DATA_DIR/rooms.json(或 ./.data/rooms.json)。服务器可能会在同一目录下留下两类恢复副本,两者都可能包含房间内容。如果文件完全无法解析,服务器会将其移到一旁,命名为 rooms.json.corrupt-<time>(保留最新的 5 个副本)。如果只有部分房间加载失败,其原始字节会保存到 rooms.json.partial-<time>;这些副本绝不会被删除,因为每个副本都可能是某个房间仅存的副本。从服务器删除房间(淘汰)不会删除这些副本。请检查它们,并在不再需要时自行删除。删除 rooms.json 即可从空状态开始。 使用 DATABASE_URL 时,所有数据都存放在一个表 behalf_records 中。删除该表即可从空状态开始。

  • 首次在 Postgres 上启动。 如果该表为空,且 DROP_DATA_DIR/rooms.json 存在,服务端会在一个事务中将所有房间和当天的用量复制到表中,然后将文件重命名为 rooms.json.imported-<time>。账户记录(用户、会话和 Agent 密钥)也会一并导入,同时还会导入当天的用量。如果未设置 DROP_DATA_DIR,服务端会查找本地文件 ./.data/rooms.json。如果中途崩溃,表会保持为空,下次启动时会重新导入。如果表中已有数据,文件会保持原样,服务端会记录 store.import_skipped。.imported 文件会保留房间内容、席位令牌以及加载失败的房间:请检查导入结果,待部署稳定且确认无需回滚后再删除该文件(参见“回滚”)。导入的密钥仍可使用,并会显示为“Agent key (created , )”,直到其所有者将其删除。
  • 同一时间只能运行一个服务端。 第一个服务端会占用数据库;第二个会等待,然后因 ELOCKED 而停止。
  • 数据库丢失会导致严重故障。 如果连接断开,服务端会记录 store.lock_lost;如果语句完全没有收到响应,则会记录 store.connection_lost。无论哪种情况,服务端都会退出,平台会从数据库重新启动它。此时正在对话的用户会看到房间暂停,需要按“Resume”;等待 wait_for_turn 的已连接 Agent 会遇到连接断开,必须重新调用。如果服务端启动时数据库不可用,它会重试约 45 秒,然后放弃。

收到 SIGTERM 时,服务端会停止接收新任务,在 DRAIN_DEADLINE_MS 时间内保存待处理内容,然后以状态码 0 退出(保存失败时为 1)。SIGINT 会以相同方式排空待处理内容,但无论保存结果如何,始终以状态码 130 退出。Docker 会使用 SIGTERM 停止应用,但 Drop 的 PM2 模式使用 SIGINT(PM2 的默认行为),因此在 PM2 下,退出状态码无法说明最后一次保存是否成功。保存失败会记录在日志中:Postgres 对应 store.write_failed,文件存储对应 store.save_failed;如果保存挂起,则会记录 app.drain_timeout。/health 会报告 store(file 或 postgres)和 storeOk。

回滚。 较新的构建可能会升级存储格式。较旧的构建会拒绝较新架构写入的数据,并且无法启动,因此升级前请保留一份副本。对于早于登录功能的旧构建,加载包含账户的文件后,会在下一次保存时删除所有账户、会话、Agent 密钥和“Use our AI”授权。更准确地说,任何不识别 aiaccess 类型的构建,都会在下一次写入时从文件存储中删除这些授权;在 Postgres 中,授权记录仍会保留在表中,并会在较新的构建再次启动时恢复。回滚后,所有人都需要重新登录并创建新的 Agent 密钥。对于包含账户但早于命名密钥功能的旧构建,每位用户只会保留最新的密钥(加载时会删除其余密钥),并且其连接页面创建新密钥时会替换旧密钥;这些密钥的名称不会被读取,其余密钥会在下一次保存后消失。要从 Postgres 切回文件存储:

  1. 停止应用,确保导出期间没有任何写入。
  2. 导出该表:DATABASE_URL=... node scripts/export-rooms.js /path/to/rooms.json。此操作需要 pg(npm ci),只会读取数据,不需要数据库锁,只会输出计数,并且不会覆盖文件。生成的 rooms.json 采用文件存储自身的格式,包含所有房间、当天的用量和账户记录。
  3. 将该文件放入 DROP_DATA_DIR,命名为 rooms.json。先将其中已有的 rooms.json 移到别处。
  4. 如果要部署的构建在 Drop 上将 REQUIRE_DATABASE 默认设为 1,请在控制面板中设置 REQUIRE_DATABASE=0,否则服务端会因缺少数据库而拒绝启动。切回 Postgres 时请移除该设置。
  5. 部署旧构建。 rooms.json.imported-<time> 仅包含首次启动 Postgres 时的数据:此后创建的房间只保存在数据库中,因此需要导出。恢复 .partial 或 .corrupt 副本后,请清除会话和 Agent 密钥,避免他人继续使用已撤销的访问权限。

在 Drop 上部署

重新部署期间,新实例会响应 503“starting”,直到旧实例停止并释放数据库。Drop 会在旧实例仍在提供服务时启动新实例,只有新实例能响应 HTTP 后才会停止旧实例。因此,index.js 会立即使用占位服务绑定端口(503、{"code":"starting"},以及 /health 上的 {"ok":false,"starting":true}),待存储加载完毕后再切换到真实服务。

drop.yaml 会向 Drop 申请 Postgres 数据库(由其设置 DATABASE_URL),设置 SIGNIN: github 和 PUBLIC_URL,并将两个 GitHub 密钥声明为必需项。Drop 会在应用启动前检查已声明的密钥:如果缺少密钥,就会让应用处于 needs-config 状态,而不会让它在启动时崩溃。这里有意不声明 DATABASE_URL:该变量由 Drop 自行设置,而 Drop 的重新部署检查只统计手动设置的密钥,因此声明它会阻止每次重新部署。部署后请改为检查 /health(步骤 5)。只要设置了 DROP_DATA_DIR,REQUIRE_DATABASE 默认就会启用;drop.yaml 也将其设为 "1"。不要在控制面板中将它设为 0(控制面板中的值会覆盖 drop.yaml),除非你有意像 回滚 中那样使用文件存储。若 Drop 在没有 DATABASE_URL 的情况下启动应用,进程会立即退出(BAD_REQUIRE_DATABASE),就绪探测会失败,旧版本会继续提供服务,而不是让空的文件存储接管。

  1. 创建 GitHub OAuth 应用。其回调 URL 必须与 https://behalf.dropkit.sh/auth/github/callback 完全一致(即 PUBLIC_URL + /auth/github/callback)。
  2. 在 Drop 控制面板中设置 GITHUB_CLIENT_ID、GITHUB_CLIENT_SECRET、ANTHROPIC_API_KEY、ADMIN_GITHUB_IDS(你的 GitHub 数字 ID:没有它,就没人能授予“使用我们的 AI”权限,且服务器会记录 app.no_admins 日志)以及所需的任何限制。开启登录后不会使用 ROOM_PASSCODE。还要根据 Drop 连接应用的方式设置 TRUST_PROXY。在 PM2 隔离模式下,Drop 的 Caddy 会通过环回地址(localhost:PORT)连接应用,因此设置 TRUST_PROXY=loopback。在 Docker 隔离模式下,端口会发布到主机的环回地址,但应用看到的连接来自 Docker 网桥,即一个私有地址:保持默认值 private。(在 Docker 下使用 loopback 会忽略 X-Forwarded-For,导致所有访客在按地址设置的限制中共用同一个地址。)
  3. 控制面板中的任何值都会覆盖 drop.yaml。重新部署前,请检查控制面板中的 SIGNIN 和 PUBLIC_URL:移除它们,或将其设为与 drop.yaml 相同的值(github 和 https://behalf.dropkit.sh)。遗留的 SIGNIN=off 会让登录保持关闭,过期的 PUBLIC_URL 则会导致 GitHub 回调失效。
  4. 重新部署。首次启动时,服务器会导入 rooms.json(参见“数据”)。等部署稳定运行且确认无需回滚后,再删除 rooms.json.imported-<time>:其中包含房间内容和席位令牌。
  5. 检查 /health:store 必须为 postgres,且 storeOk 为 true。store: file 表示数据库未能配置:请在日志中查找 store.file_fallback。还要检查 /api/config,确认 signin 显示为 github。

评论

支持 Markdown

第一次接触 MCP?

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

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