mcpField
W
waitbus

waitbus

本地服务
github.com开发工具24 周前更新

关于 waitbus

waitbus 是面向单台工作站的事件总线,让 Agent、脚本和本地服务广播并等待 CI、测试、容器及文件系统事件。事件写入 SQLite 并支持离线回放,等待无需轮询,也不依赖云端。

主要功能

  1. 01汇集五类本地事件源
  2. 02阻塞等待并组合事件条件
  3. 03事件广播与 SQLite 回放
  4. 04插件式事件源接入
  5. 05Linux 与 macOS 守护进程支持

适用场景

  1. 01等待测试、构建或 CI 完成
  2. 02在不同 Agent 间传播失败事件
  3. 03文件或容器事件触发脚本执行

MCP 相关

waitbus 使用 MCP SDK,但输入没有说明它向 MCP 客户端公开的具体接口。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"astrogilda-waitbus": {
"command": "uvx",
"args": [
"waitbus"
]
}
}
}
启动命令uvx waitbus

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypiwaitbus0.2.0

工具

5

收录的工具列表 · 更新于 2 周前

参数类型必填说明
repostring|null否用于筛选的 owner/repo;null 返回所有已配置的筛选条件(pr_aggregate 视图必填)。
viewenum是要返回的 CI 投影。'status':每个仓库最新的 workflow_run。'failed_jobs':近期失败的 workflow_job 行。'pr_aggregate':某个拉取请求的所有运行和作业(需要 repo 和 pr_number)。status | failed_jobs | pr_aggregate
limitinteger否failed_jobs 视图最多返回的失败作业数(按最新优先)。
pr_numberinteger|null否拉取请求编号;仅在 pr_aggregate 视图中必填。

详细介绍

waitbus:工作站本地、跨框架的状态总线。面向 Agent 的 D-Bus,带有重放日志和等待原语

CI Python 3.11+ License: MIT Ask DeepWiki Context7

一个 wait 动词即可同时阻塞等待并查询所有本地来源。当你机器上的任何 Agent、CI 任务、测试或容器完成或失败时,这台机器上的其他工具都会收到通知:你的 Claude Code、Cursor、脚本和 CI。支持服务通知的客户端会收到推送;其他客户端则会执行一次阻塞式 waitbus wait。没有 waitbus 云服务、没有账户、没有遥测;所有处理都在你的机器上进行。

下面展示了一个 Pydantic AI Agent 和一个 LangGraph Agent:两个使用不同框架的 Agent 作为独立 OS 进程运行,同时等待同一个本地 waitbus 总线。一个 Agent 失败后,使用另一种框架的对等 Agent,以及实时的 waitbus top 视图,都会响应这次失败广播。

waitbus 跨框架演示:两个不同的 Agent 框架使用一条本地总线;一个 Agent 失败后,另一个 Agent 作出响应

使用真实框架和真实的 waitbus 订阅/发送事件;Agent 的 LLM 使用确定性模拟实现,失败则通过注入事件触发,因此整个片段可完全离线运行。复现这一结论(而不只是观看录屏):运行 uvx waitbus demo,体验更简单的单 Agent 演示(无需克隆或安装);或者,要复现上方展示的双框架验证,请克隆仓库并运行 uv run --group agent-recipes python -m examples.hero_swarm(已在 Linux 上验证;会打印 PROVEN: two DIFFERENT frameworks woke on ONE peer's failure)。更高画质的 MP4 文件见 hero.mp4。维护者使用 make hero 从 docs/demo/.waitbus-demo/ 重新渲染此录屏(仅供开发使用;需要 vhs、ttyd、tmux 和 JetBrains Mono)。

立即试用,无需安装: uvx waitbus demo 会启动一个真实的本地总线,并在事件到达时立即返回,全程离线(约 8 s)。


waitbus 是工作站本地、跨框架的状态总线,让同一台机器上的工具能够彼此通知。它提供三项功能:接收事件、广播事件和重放事件。

**接收:**阻塞式等待/发送原语让任何 Agent 或脚本(跨 Cursor、Claude Code 以及机器上的任何工具)都可以等待或发送来自五种内置来源的事件(GitHub Actions CI、pytest 会话、Docker 容器生命周期、文件系统变更和 Prometheus Alertmanager),以及来自任何通过 waitbus.sources.v1 入口点组注册的插件来源的事件。

**广播:**事件到达时会立即写入 SQLite,广播守护进程会在约 1 毫秒内通过本地套接字将每一行事件分发给所有已连接的使用者(经测量;参见 benchmarks/BENCHMARKING.md)。你的 Agent 会阻塞等待 waitbus wait(也可以使用 --all-of/--first-of 同时等待多个来源),无需在 Agent 端轮询,也不会在所关注的事情发生前占用 CPU;waitbus on <predicate> -- <command> 则会在事件发生时立即运行命令(在 CLI 进程中运行,而非守护进程中;通过不使用 shell 的 execve 执行;事件值以带命名空间的 WAITBUS_* 环境变量和 $WAITBUS_EVENT_FILE 传递,而绝不会作为 argv 传递,因此事件内容无法注入标志或覆盖 PATH)。由于机器上的所有 Agent 共用同一条总线,它也能充当同机协作的骨干:一个 Agent 发送事件,其他 Agent 随即唤醒。 **回放:**持久化的回放日志(since=)意味着离线的使用者可以追赶进度,而不是错过事件;waitbus events analyze 则可查询总线存储的内容。这是操作系统本身已采用的本地选择加入式事件广播模型(D-Bus 信号、inotify、journald),在此基础上增加了回放日志和等待谓词。(GitHub Actions 是接入的第一个来源,因此示例以 CI 开头,但 waitbus 并非仅用于 CI。)依赖栈精简:typer、msgspec、platformdirs、pydantic-settings、prometheus_client、stamina 和 mcp SDK,不依赖笨重的 TUI 或加密库。守护进程空闲时的 RSS 约为 40 MB(在基准测试主机上测得的 p50,其中大部分是 CPython 解释器的基准占用);它基于 SQLite 事件存储和采用长度前缀帧格式的 AF_UNIX SOCK_STREAM 广播总线运行。支持 Linux(systemd-user)和 macOS(launchd)。


适用对象

如果你符合以下情况,就适合使用本工具……

  • 你运行着异构的 Agent 集群(Claude Code、Cursor 和后台脚本)在同一台工作站上,并希望它们能获知彼此的完成和失败。
  • 你需要跨来源等待。“阻塞直到测试通过、构建完成且 CI 变绿”,用一个谓词即可在脚本中实现(waitbus wait --all-of ...)。没有其他工具能表达这一点。
  • 你希望将零 LLM 令牌、零轮询的等待作为日常习惯,用于许多本地任务,并愿意运行守护进程来实现。
  • **你关心本机上的守护进程是否可信:**可复现构建、离线运行、无云服务、无账户、无遥测。

如果你符合以下情况,则不适合使用本工具……

  • **你只需等待一个 GitHub 仓库的 CI,别无其他需求。**请使用 gh run watch。它无需配置;对于这单一任务,我们的优势不足以证明运行守护进程的合理性。
  • **你只需响应文件变更。**请使用 inotifywait/entr。它们在原始文件系统延迟方面快得多(我们的基准测试也证实了这一点),且无需守护进程。
  • **你需要云端、跨机器或团队协作。**按设计,这不属于本地核心的范围。
  • 你希望将面向特定对象的 Agent 间消息传递作为产品核心(线程、收件箱、路由)。本工具的核心是广播式来源接入;虽然提供了 request()/respond() 功能,但专用消息队列更适合这种核心需求。

60 秒快速体验

uvx waitbus demo          # one command, no install needed

waitbus demo 会分配临时状态目录并启动广播守护进程,然后运行两个阶段。**阶段 1,重点:**一个 Agent 使用 waitbus wait(与实际命令相同的事件出口引擎)阻塞等待,不进行轮询,也不占用空闲 CPU;一旦收到 GitHub workflow_run 事件,等待便会返回,演示程序会打印实测的事件到解除阻塞延迟。**阶段 2,广度:**同一原语可传递所有来源的事件。pytest_session、docker_container 和 fs_change 事件会广播给实时订阅者。不会触及临时目录以外的任何本机内容。

waitbus 单 Agent 演示:GitHub Actions workflow_run 事件到达时,阻塞等待立即返回,随后打印实测的事件到解除阻塞延迟

录制好的 MP4 和 GIF 演示位于 docs/demo/.waitbus-demo/demo.mp4 和 demo.gif (录制脚本为 demo.tape)。可在该目录中运行 make demo 重新生成 (需要 VHS ≥ 0.10.0、ttyd 和 ffmpeg);Makefile 会强制执行 VHS 的最低版本要求,并拒绝使用较旧的二进制文件渲染,以免其在不知情的情况下改变录制脚本语义。

演示发出的四个事件均在进程内合成。没有真实的 HTTP 监听器、没有实际运行 pytest、没有真实的 Docker 守护进程,也没有真实的 watchdog。每次发出事件前都会显示提示横幅,以明确说明这一点;这与 waitbus stats 的输出方式一致。若要将 waitbus 接入真实的 GitHub webhook 投递,请参阅下文的快速入门。


快速开始

约 10 秒验证这个基本机制——启动一个前台守护进程,发送一个事件,无需安装服务。安装 CLI 并初始化状态:

uv tool install waitbus          # or: pipx install waitbus
waitbus init                     # one-time setup: state dirs, SQLite schema, scaffolds

然后,在两个终端中,观察阻塞中的 wait 如何在 emit 事件进入总线的瞬间返回:

# Terminal A -- run the broadcast daemon in the foreground (Ctrl-C to stop):
waitbus broadcast serve
# Terminal B -- block on the next agent event, then emit one from the same shell:
waitbus wait --source agent --match 'fields.event_type="agent_message"' --timeout 30s &
sleep 1   # let the backgrounded wait attach to the bus before we emit (a
          # long-lived subscriber would already be attached; this one-liner
          # just compresses "wait, then something happens" into one shell)
waitbus emit \
  --delivery-id demo-1 --source agent --event-type agent_message \
  --owner local --repo local --received-at "$(date +%s)" \
  --payload-json '{"hello":"world"}' --ingest-method manual
wait   # the backgrounded `waitbus wait` prints "matched on source=agent" and exits 0

这个往返过程——阻塞、发送、解除阻塞,且完全不轮询——就是整个基本机制。下面的内容会将它连接到实际数据源,并可选择在你的服务管理器下永久运行守护进程。

永久运行这组服务(可选)
waitbus install-credentials github-webhook-secret   # optional: only for GitHub-CI waiting (enables the webhook listener)
waitbus install-systemd          # Linux: copy + enable the 8 systemd-user units
waitbus install-launchd          # macOS: copy + bootstrap the 4 LaunchAgent plists
waitbus read-events watch        # live tail of incoming events

install-systemd 仅适用于 Linux;install-launchd 仅适用于 macOS。 每个命令在其他平台上都会拒绝运行,并提示应使用的命令。

事件开始流动后,可用 waitbus wait 让任何脚本或 Agent 阻塞等待下一个匹配事件——支持任何来源、任何字段,退出代码会传递判定结果。wait 端始终无需任何设置;但它等待的事件仍必须由某个来源产生(选择加入的发送器或来源监视器),因此下面每个示例都会注明需要由什么发送事件才能返回:

# pytest: wait for any session to finish.
# wait is zero-setup; a pytest_session event needs the opt-in emitter --
# run your suite with:  pytest -p waitbus.sources.pytest_emit --waitbus-emit
waitbus wait --source pytest --match 'fields.event_type="pytest_session"' --timeout 10m

# Docker: wait for a container to exit.
# wait is zero-setup; a docker_container event needs the docker watcher running --
# start it with:  waitbus source docker   (see `waitbus source docker --help`)
waitbus wait --source docker --match 'fields.event_type="docker_container"' --timeout 30s

# Filesystem: wait for a watched path to change.
# wait is zero-setup; an fs_change event needs the fs watcher running --
# start it with:  waitbus source fs <dir>  (see `waitbus source fs --help`)
waitbus wait --source fs --match 'fields.event_type="fs_change"' --timeout 5m

# GitHub CI: wait on a commit's terminal conclusion (needs webhook wiring -- see below)
waitbus wait --sha 7f3a1b2 --timeout 5m

如果你用过 watchexec 或 entr 在文件变更时重新运行命令,那么 waitbus wait 也是同一思路,但覆盖范围更广:它既能阻塞等待文件变更,也能等待 CI 运行、pytest 会话、容器退出或其他 Agent 的事件,并通过退出代码传回判定结果。底层总线还会将每个事件广播给这台机器上的所有其他工具。


架构

waitbus 架构:Webhook 和进程内数据源将事件写入 SQLite 事件存储;广播守护进程将每一行事件分发到 CLI、MCP 服务和 Python SDK,它们可以通过 since= 重放

根据 docs/architecture.mmd 渲染(使用静态图片,以便图表能在 PyPI 等非 Mermaid 页面上显示,而不只是在 GitHub 上显示)。使用 mmdc -i docs/architecture.mmd -o docs/architecture.png 重新生成。

两类入口会将事件写入同一个存储:远程 Webhook(GitHub、 Alertmanager)通过 HTTPS 到达,并经由 waitbus listener serve 处理;而进程内数据源(pytest、docker、fs、插件以及任何 Agent)则调用公开的 emit() API 直接写入记录,无需监听器,也无需经过网络。对于无法接收 Webhook 的仓库(上游仓库、Fork),系统会每隔 45 秒运行一次基于 ETag 的轮询回退机制。所有路径都会通过相同的 INSERT OR IGNORE 写入,因此重复投递具有幂等性;每条记录都会触发广播通知铃,使订阅者能在约一毫秒内唤醒,无论事件来自哪个入口。

关于 waitbus 与 MCP Tasks(task=True)以及本地 Agent 间总线(agent-message-queue、claude-code-inter-session)的关系,请参阅 docs/COMPETITIVE_LANDSCAPE.md。


安装

作为 Python CLI 工具
uv tool install waitbus              # or: pipx install waitbus
waitbus init                         # one-time setup: state dirs, SQLite schema, scaffold files
waitbus install-credentials github-webhook-secret   # optional: HMAC for the GitHub webhook listener (opt-in)
waitbus install-credentials alertmanager-hmac       # optional: alertmanager / watchdog HMAC
waitbus install-systemd              # Linux: copy + enable the 8 systemd-user units
waitbus install-launchd              # macOS: copy + bootstrap the 4 LaunchAgent plists

每条命令都是幂等的。重新运行 install-credentials <name> 会轮换凭据(轮换后请重新配置 GitHub webhook 和 Alertmanager,然后运行 systemctl --user restart waitbus-listener.service)。

安装后,PATH 中会有一个控制台脚本:waitbus。所有守护进程入口点和管理命令都可通过子命令访问:

子命令用途
waitbus init初始化状态目录、SQLite 架构和脚手架文件
waitbus install-systemdLinux:复制并启用 systemd-user 单元
waitbus install-launchdmacOS:复制并引导 LaunchAgent plist
waitbus install-credentials将 HMAC 密钥存入 0600 权限的密钥文件(读取 --file 或标准输入)
waitbus doctor验证安装(路径、二进制文件、凭据存储、单元、/metrics)
waitbus status运维仪表板:数据库行数、守护进程存活状态
waitbus verify-plugin验证 .claude-plugin/plugin.json
waitbus listener serveHTTP webhook 接收器(回环地址 :9000)
waitbus broadcast serveAF_UNIX 广播中枢守护进程
waitbus etag-poll runGitHub API ETag 轮询工作进程
waitbus mcp serveMCP 服务(stdio),将广播事件重新发出为通知
waitbus read-events watch订阅广播总线,实时流式传输事件
waitbus read-events list打印本地缓存中的近期事件
waitbus pr-monitor tick将作业事件汇总为各 PR 状态
waitbus watchdog-check run检测数据摄取静默的工具

此外,根据平台不同,还会有 ~/.config/systemd/user/ 中的 8 个 systemd-user 单元(Linux):

waitbus-listener.service    waitbus-broadcast.socket
waitbus-broadcast.service   waitbus-etag-poll.service
waitbus-etag-poll.timer     waitbus-watchdog.service
waitbus-watchdog.timer      [email protected]

……或者 ~/Library/LaunchAgents/ 中的 4 个 LaunchAgent plist(macOS):

dev.waitbus.listener.plist     dev.waitbus.broadcast.plist
dev.waitbus.etag-poll.plist    dev.waitbus.watchdog.plist

macOS plist 中包含 __BIN_DIR__ / __LOG_DIR__ / __RUNTIME_DIR__ 占位符,waitbus install-launchd 会在写入前将它们替换为运维人员的实际路径。Agent 通过 launchctl bootstrap gui/$UID <plist> 加载(这是已弃用的 launchctl load -w 的现代替代方式)。

作为 Python 库
from waitbus import subscribe, wait_for, EventFrame

# Block until one matching event arrives (returns the frame, or None on timeout).
frame: EventFrame | None = wait_for('fields.event_type="pytest_session"',
                                    source="pytest", timeout=600)
if frame is not None:
    print(frame.event_type, frame.fields)

# Or stream every matching event as it lands.
for frame in subscribe(source="docker"):
    print(frame.event_type, frame.fields)

Agent 也可以在同一总线上彼此发送消息——一方发送请求并阻塞等待关联的回复,另一方则从收件箱中作答:

from waitbus import request, respond, wait_for

# responder (agent_b), in its own process
msg = wait_for(to="agent_b", source="agent", timeout=5.0)
if msg is not None:
    respond(msg, '{"answer": 42}')             # sender defaults to msg's recipient

# requester (agent_a)
reply = request("agent_b", '{"ask": "meaning"}', sender="agent_a", timeout=5.0)
# reply is the answer EventFrame, or None on timeout.

请参阅 docs/AGENT_MESSAGING.md,了解请求/回复约定、收件箱流以及相同 UID 信任模型。

稳定的公共 API 包括 emit、subscribe、asubscribe、wait_for、request、respond、EventFrame,以及插件钩子 register_source、register_condition、register_evaluator(完整签名见 docs/CONSUMER_API.md)。要生成事件,最简单的方式是使用 waitbus emit CLI;emit() 的 Python 用法请参阅 examples/emitters/。以下划线开头的所有内容(waitbus._db、waitbus._paths……)都是私有内部实现,可能会在不同版本间发生变化。

运行时依赖(完整必需依赖,与上面的精简技术栈相同):typer(CLI 总入口)、msgspec(线路帧处理)、platformdirs(状态和运行时路径解析)、pydantic-settings(配置加载器)、prometheus_client(指标)、stamina(重试策略)和 mcp SDK(MCP 服务)。面向消费者的两个额外依赖组(waitbus[analyze]、waitbus[fs])会按需引入 duckdb / watchdog,也是仅有的可选包。其他所有内部模块都只使用标准库:导入时不访问网络、不使用 C 扩展,可在任何 Python 3.11+ 环境中安全导入。软件包根日志记录器使用 NullHandler,因此库的使用者不会看到泄漏的日志记录。

作为 MCP 服务(PyPI + uvx)

waitbus mcp serve 是一个 stdio MCP 服务(基于 https://modelcontextprotocol.io/specification 中的规范,通过 JSON-RPC 2.0 通信)。客户端能用它做什么,取决于该客户端实现了哪些 MCP 功能,因此实际的功能划分如下:

  • 拉取,适用于任何支持 Tools 的 MCP 客户端。 调用 waitbus 工具来查询状态并阻塞等待事件;tail_events 是有界长轮询,因此即使等待也无需轮询循环。这是兼容性最广的方式。
  • 推送,适用于支持资源订阅的客户端。 waitbus 会为 waitbus://repo/... 订阅者发送符合规范的 notifications/resources/updated。客户端是否会呈现此类推送(而非仅调用工具)取决于客户端;如今大多数编码 Agent MCP 客户端仅支持拉取,已发布的客户端矩阵会跟踪 Tools / Resources / Prompts,但不包含通知呈现这一列。
  • 内联呈现,适用于 Claude Code。 waitbus 还会发送 Anthropic 私有的 notifications/claude/channel,由 Claude Code 内联呈现;根据 JSON-RPC 2.0 的规定,其他客户端会安全地忽略未知方法。

已验证: 一项可执行测试端到端地验证了整个路径,而非仅验证“符合规范”:Claude Code(MCP 发送路径,tests/test_mcp_e2e.py);以及通过公开的 Python SDK(waitbus.wait_for / subscribe)使用的 Pydantic AI 和 LangGraph Agent(tests/test_agent_integration_*.py);此外还包括语言代码片段和 bash waitbus wait 封装脚本。任何其他 stdio-MCP 客户端都可以使用上述拉取方式,但这属于配置说明,并非经过测试的端到端保证。

命令始终是 uvx --from waitbus waitbus mcp serve;不同客户端的区别仅在于 MCP 配置的存储位置。配置位置和格式会随客户端版本变化,因此请将下表作为起点,并查阅客户端当前的 MCP 文档:

{
  "mcpServers": {
    "waitbus": {
      "command": "uvx",
      "args": ["--from", "waitbus", "waitbus", "mcp", "serve"]
    }
  }
}
客户端MCP 配置位置(请对照客户端当前文档确认)
Claude Code~/.claude/.mcp.json(或项目级 ./.mcp.json)
Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.json(macOS)或 %APPDATA%\Claude\claude_desktop_config.json(Windows)
Cursor~/.cursor/mcp.json(或项目级 .cursor/mcp.json)
Cline (VS Code)~/.config/Cline/cline_mcp_settings.json(Linux),或工作区级 .vscode/cline_mcp_settings.json
Codex CLI (OpenAI)~/.codex/config.toml(位于 [mcp_servers.waitbus] 表中;Codex 使用 TOML,而非 JSON)
Gemini CLI (Google)~/.gemini/settings.json(位于 mcpServers 键下)
Continue~/.continue/config.json(位于 mcpServers 键下)

此处有意不列出 Aider:截至 2026-05,Aider 没有内置 MCP 客户端(上游跟踪记录见 aider-AI/aider FR #4506;唯一的 MCP 接入方式是单独的 AiderDesk 配套应用)。对于 Aider 风格或非 MCP Agent,请通过 Python SDK(waitbus.wait_for)或 waitbus wait CLI 使用 waitbus。

uv 必须位于 PATH 中(curl -LsSf https://astral.sh/uv/install.sh | sh)。 请参阅平台支持。

作为 Claude Code 插件

在发布市场 / MCP 注册表条目之前,请从本仓库的本地检出目录加载插件。.claude-plugin/plugin.json 中的清单引用了仓库根目录下的 .mcp.json,因此 --plugin-dir 必须指向检出目录的根目录(而非子目录):

git clone https://github.com/astrogilda/waitbus
claude --plugin-dir ./waitbus --dangerously-load-development-channels

从本地目录加载尚未发布到市场的插件时,必须使用 --dangerously-load-development-channels(与 CONTRIBUTING.md 中记录的命令相同)。市场条目发布到 MCP Registry 后(当前状态请参阅 CHANGELOG.md),请改为从该处安装。无论使用哪种方式,该插件都会在任何 Claude Code 会话中启用 /waitbus 斜杠命令和 waitbus 技能。


用法

读取事件
# Live tail — one line per event; monitoring-friendly cadence
waitbus read-events watch

# Query the last hour in JSON (pass extra flags after the sub-command)
waitbus read-events list --since 1h --json

# Roll job events into per-PR state (watch PRs 7 and 9)
waitbus pr-monitor tick --pr 7 --pr 9

主要功能

  • 亚秒级检测 workflow_job 失败。 GitHub 发送 Webhook 后不到 1 秒即可捕获矩阵单元格失败(从接收 Webhook 起按实际时间计算,而非从 CI 失败时起算),无需等待父级 workflow_run.completed 到达。即使其他矩阵单元格仍在运行,单个作业的失败也会立即显现。

  • 支持幂等写入的 SQLite 事件存储。 对 event_id(接收时分配的 ULID)执行 INSERT OR IGNORE。Webhook 重复投递和 ETag 轮询产生的重复事件会被静默丢弃;无需去重队列即可保持数据库一致。

  • 使用长度前缀分帧的 AF_UNIX SOCK_STREAM 广播总线。 每个线路帧由 4 字节大端长度和后续 JSON 负载组成,可在 Linux 和 macOS 上移植(macOS 不支持 SOCK_SEQPACKET)。使用非阻塞套接字和逐订阅者滞后计数器;连续丢弃达到 LAG_LIMIT 后关闭慢速订阅者,并通过 EOF 触发游标重置来重新连接。

  • HMAC-SHA256 Webhook 签名验证。 使用 waitbus install-credentials github-webhook-secret 将密钥一次性写入用户状态目录下权限为 0600 的 secrets.json,并在单元启动时读取。缺失或无效的签名会在读取负载前被拒绝并返回 401。

  • SIGTERM 排空保障。 广播守护进程收到 SIGTERM 后停止接受新订阅者,将所有进行中的帧发送给已连接的订阅者,以 EOF 关闭连接,删除其套接字文件,然后以状态码 0 退出。执行 systemctl restart waitbus-broadcast 时不会丢失事件。

  • ETag 轮询备用方案。 每隔 45 秒使用 If-None-Match 轮询 watched_repos.txt 中列出的仓库,以避免耗尽 GitHub API 速率限制。可与由 Webhook 驱动的仓库共存。

  • Prometheus 指标端点。 http://127.0.0.1:9000/metrics 以 Prometheus 文本格式公开摄取事件和摄取错误计数器。看门狗定时器会检测并记录摄取静默情况。


配置

配置文件(可选): ~/.config/waitbus/config.toml

[prometheus]
owner = "my-org"
repo  = "my-repo"

环境变量覆盖项:

变量用途
WAITBUS_PROM_OWNER覆盖 Prometheus 告警标签:所有者
WAITBUS_PROM_REPO覆盖 Prometheus 告警标签:仓库
WAITBUS_STATE_DIR覆盖状态目录(SQLite 数据库、脚手架、游标)。默认通过 platformdirs 确定。
WAITBUS_RUNTIME_DIR覆盖运行时目录(广播和门铃 AF_UNIX 套接字)。默认通过 platformdirs 确定。
状态目录解析

路径解析位于 waitbus._paths,并遵循与其余配置链相同的先匹配优先规则:

  1. WAITBUS_STATE_DIR / WAITBUS_RUNTIME_DIR 环境变量(由运维人员控制)。
  2. platformdirs.PlatformDirs("waitbus") 按平台提供的默认值:
    • **Linux:**状态目录为 ~/.local/state/waitbus/(遵循 XDG_STATE_HOME); 套接字位于 /run/user/$UID/waitbus/(遵循 XDG_RUNTIME_DIR)。
    • **macOS:**状态目录为 ~/Library/Application Support/waitbus/; 套接字位于由 tempfile.gettempdir() 派生的稳定路径 (platformdirs 的 macOS user_runtime_dir 属于可被 Apple 清理的缓存, 不适用于 AF_UNIX 套接字)。

随附的 systemd 单元使用 StateDirectory=waitbus 和 RuntimeDirectory=waitbus,因此在 Linux 上,systemd 会在守护进程启动前以 0700 权限 创建目录,无需运维人员手动创建。

密钥(通过 waitbus install-credentials <name> 暂存):

密钥名称必需用途
github-webhook-secret仅在启用 webhook 监听器时用于验证 GitHub webhook 签名的 HMAC 密钥
alertmanager-hmac否用于验证 Alertmanager / watchdog webhook 的 HMAC 密钥

广播总线本身不需要密钥:它是一个由内核同 UID 对端凭据检查保护的 AF_UNIX 套接字,因此无需订阅者令牌。密钥存储在用户 状态目录下一个权限为 0600 的 secrets.json 文件中(<state_dir>/secrets.json);install-credentials 从 --file 或标准输入读取值,并通过原子替换将其合并写入,绝不会 将其暴露在命令行中。文件静态加密由主机的全盘加密(FileVault / LUKS)负责;请参阅 SECURITY.md。waitbus doctor 会报告每个必需的 密钥是否存在,但不会打印其值;如果启用的单元缺少所需密钥,则以状态码 1 退出。


安全

完整的 HMAC 威胁模型、平台假设和漏洞报告流程,请参阅 SECURITY.md。

**同 UID 信任模型。**广播总线不提供进程间 机密性:任何以你的用户身份运行的进程都可以读取总线上的每个事件, 也可以使用任意发送者名称发出事件。waitbus 是面向单用户工作站的工具,并非用于隔离你运行的进程的 安全边界。对端身份验证依靠内核验证的 UID(SO_PEERCRED),因此其他用户 无法连接;但按设计,同一用户下的隔离不在支持范围内。

如需验证发布制品(需要 gh CLI、网络访问权限,以及一个包含证明的 已发布 GitHub release——因此,这并非在离线克隆中运行的检查):

gh attestation verify $(python -c "import waitbus; print(waitbus.__file__)") \
  --repo astrogilda/waitbus

完整的来源验证流程(包括 SLSA、PEP 740 和 SBOM 检查),请参阅 SECURITY.md。


支持

如需报告缺陷、提出功能请求或咨询问题,请参阅 SUPPORT.md;其中列出了安全、滥用/行为规范和 一般渠道,并说明由单一维护者尽力提供支持的预期。安全漏洞应通过 SECURITY.md 中的私密流程报告,切勿通过公开 issue 报告。


运行

# Start the core daemons
systemctl --user start waitbus-listener.service
systemctl --user start waitbus-broadcast.socket

# Start the background timers
systemctl --user start waitbus-etag-poll.timer
systemctl --user start waitbus-watchdog.timer

# Health check — exits 1 on any missing required config or stopped unit.
# A non-zero exit right after install (before you start the daemons) is
# expected, not a failure: it just reports that the units are not up yet.
waitbus doctor

# Watch webhook deliveries arrive in real time
waitbus read-events watch

启用单元,使其在登录时自动启动:

systemctl --user enable waitbus-broadcast.socket \
    waitbus-etag-poll.timer waitbus-watchdog.timer
# The webhook listener is opt-in; `install-credentials github-webhook-secret` enables it.

轮换 webhook HMAC 密钥:

# Pipe the new value in; install-credentials atomically replaces the staged secret.
openssl rand -hex 32 | waitbus install-credentials github-webhook-secret
# Then update the GitHub repository webhook secret and Alertmanager config.
systemctl --user restart waitbus-listener.service

无头服务器(仅 SSH/无图形会话)

在没有图形登录会话的无头机器上,还需要进行两项额外设置,才能让 systemd 用户实例在没有打开交互式会话时持续运行:

  1. 让用户管理器在注销后继续运行。 运行一次:

    loginctl enable-linger <username>
    

    否则,最后一个交互式会话结束时,systemd 用户管理器及其所有单元都会停止。

  2. 无需解锁密钥环。 密钥存放在用户状态目录下权限为 0600 的 secrets.json 文件中,通过普通文件读取,不需要密钥环,也不要求 D-Bus 会话,且不依赖 pam_gnome_keyring.so,因此守护进程栈无需修改即可在完全无头的机器上运行。

验证 linger 设置是否生效。 打开一个新的 SSH 会话(先不要手动启动单元),然后运行:

systemctl --user is-active waitbus-listener

结果应为 active。如果未手动运行 systemctl --user start 时单元并未处于活动状态,则 linger 设置没有生效。

启用了 linger 的主机上的 XDG_RUNTIME_DIR。 启用 linger 后,systemd 会自动设置 XDG_RUNTIME_DIR=/run/user/$(id -u),并在其下创建广播套接字路径。无需操作人员采取任何措施;在任何会话中首次调用 systemctl --user 时,该路径即可用。


接入仓库

本地源无需逐个仓库接入。 pytest、Docker 和文件系统事件都在本机产生,因此无需设置 webhook 转发器。守护进程运行后(见上文“快速开始”),并且相应的事件源正在发送事件——pytest 可选启用的事件发送器,或 waitbus source docker/waitbus source fs 监视器——waitbus wait --source pytest|docker|fs 无需任何逐仓库配置。下面两个小节仅适用于 GitHub CI,因为其事件通过 webhook 到达,因此需要转发器。

事件驱动(推荐用于你拥有的仓库):

gh webhook forward \
    --repo=<owner>/<repo> \
    --events=workflow_run,workflow_job \
    --url=http://127.0.0.1:9000/webhook \
    --secret="<the same plaintext value you piped to install-credentials>"

gh webhook forward 会在 GitHub 端创建一个临时 webhook,并通过隧道将投递内容转发到本地监听器。密钥必须与通过 waitbus install-credentials github-webhook-secret 暂存的明文一致。

对于由 systemd 监督的转发器(随附的 [email protected] 模板),无需操作人员采取任何措施:该单元会在启动时从 secrets.json(通过 $STATE_DIRECTORY)读取 github-webhook-secret 键,并将其传递给 gh webhook forward --secret。

ETag 轮询(可以读取但无法接收 webhook 的仓库):

将 owner/repo 行追加到解析后的状态目录下的 watched_repos.txt 中(waitbus doctor 会打印路径;在未覆盖 WAITBUS_STATE_DIR 的 Linux 上,路径为 ~/.local/state/waitbus/watched_repos.txt)。45 秒计时器会自动读取这些条目。

仅使用 SSH 的机器请参阅无头服务器,了解 loginctl enable-linger 的要求。


平台支持

组件Linux + systemdmacOS + launchd备注
Python 监听器是是绑定到回环地址,仅使用标准库
SQLite 事件存储是是单文件数据库,无需服务器
广播守护进程是是AF_UNIX SOCK_STREAM + 长度前缀帧格式
门铃原语os.eventfdSOCK_STREAM ping在绑定位置内联分派
对端凭据 UID 检查SO_PEERCRED通过 ctypes 调用 getpeereid()特意不使用 LOCAL_PEERPID(参见 SECURITY.md)
安装入口waitbus install-systemdwaitbus install-launchd两者均会拒绝在另一平台上运行
进程管理器systemd-user 单元launchd LaunchAgents8 个 systemd 单元、4 个 launchd plist,全部通过 waitbus 驱动
MCP 服务是是stdio MCP,可在任何符合规范的客户端中运行
Claude Code 插件是是插件清单位于 .claude-plugin/

两个平台都运行完整的守护进程栈。Linux 使用带套接字激活的 systemd-user 单元;macOS 使用 launchd LaunchAgents,并手动绑定套接字。对端凭据 UID 检查、门铃原语和安装入口点均在叶子调用位置内联分派;没有类似 IPCBackend 的抽象层,因为这三个叶子位置是唯一存在平台差异的代码。 详情请参见 SECURITY.md。

不支持原生 Windows。 waitbus 的信任模型依赖 AF_UNIX 套接字以及用于检查对端是否为同一 UID 的 SO_PEERCRED,而 Win32 没有等效机制。Windows 用户可在 WSL2 下运行 waitbus;WSL2 使用真正的 Linux 内核:在 WSL2 发行版中运行 pip install waitbus(或 uv tool install waitbus),效果与在原生 Linux 上完全相同。平台选择的原因请参见 docs/ARCHITECTURE.md。

默认零出站流量,不收集遥测数据。 广播守护进程、每个订阅者(waitbus wait、waitbus read-events、waitbus mcp serve)以及 emit() 路径都不会发起出站网络调用;不会向外发送任何数据。唯一的网络接口是可选的入站 GitHub webhook 监听器(回环地址 127.0.0.1:9000,经 HMAC 验证),此外还会访问本地 OS 密钥环以存储凭据。只有在你明确选择启用 ETag 轮询回退机制时才会产生出站流量(该机制会为你列出的仓库调用 GitHub API)。完整的网络接口说明请参见 SECURITY.md。


贡献

有关各部分的开发指南、在本地加载插件、版本同步和发布流程,请参见 .github/CONTRIBUTING.md。


许可证

waitbus 基于 MIT License 发布。完整许可文本请参见 LICENSE。

更多信息

评论

支持 Markdown

第一次接触 MCP?

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

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