关于 waitbus
waitbus 是面向单台工作站的事件总线,让 Agent、脚本和本地服务广播并等待 CI、测试、容器及文件系统事件。事件写入 SQLite 并支持离线回放,等待无需轮询,也不依赖云端。
主要功能
- 01汇集五类本地事件源
- 02阻塞等待并组合事件条件
- 03事件广播与 SQLite 回放
- 04插件式事件源接入
- 05Linux 与 macOS 守护进程支持
适用场景
- 01等待测试、构建或 CI 完成
- 02在不同 Agent 间传播失败事件
- 03文件或容器事件触发脚本执行
MCP 相关
waitbus 使用 MCP SDK,但输入没有说明它向 MCP 客户端公开的具体接口。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
5收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| repo | string|null | 否 | 用于筛选的 owner/repo;null 返回所有已配置的筛选条件(pr_aggregate 视图必填)。 |
| view | enum | 是 | 要返回的 CI 投影。'status':每个仓库最新的 workflow_run。'failed_jobs':近期失败的 workflow_job 行。'pr_aggregate':某个拉取请求的所有运行和作业(需要 repo 和 pr_number)。status | failed_jobs | pr_aggregate |
| limit | integer | 否 | failed_jobs 视图最多返回的失败作业数(按最新优先)。 |
| pr_number | integer|null | 否 | 拉取请求编号;仅在 pr_aggregate 视图中必填。 |
详细介绍
waitbus:工作站本地、跨框架的状态总线。面向 Agent 的 D-Bus,带有重放日志和等待原语
一个 wait 动词即可同时阻塞等待并查询所有本地来源。当你机器上的任何 Agent、CI 任务、测试或容器完成或失败时,这台机器上的其他工具都会收到通知:你的 Claude Code、Cursor、脚本和 CI。支持服务通知的客户端会收到推送;其他客户端则会执行一次阻塞式 waitbus wait。没有 waitbus 云服务、没有账户、没有遥测;所有处理都在你的机器上进行。
下面展示了一个 Pydantic AI Agent 和一个 LangGraph Agent:两个使用不同框架的 Agent 作为独立 OS 进程运行,同时等待同一个本地 waitbus 总线。一个 Agent 失败后,使用另一种框架的对等 Agent,以及实时的 waitbus top 视图,都会响应这次失败广播。

使用真实框架和真实的 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 事件会广播给实时订阅者。不会触及临时目录以外的任何本机内容。

录制好的 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 的事件,并通过退出代码传回判定结果。底层总线还会将每个事件广播给这台机器上的所有其他工具。
架构
根据 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-systemd | Linux:复制并启用 systemd-user 单元 |
waitbus install-launchd | macOS:复制并引导 LaunchAgent plist |
waitbus install-credentials | 将 HMAC 密钥存入 0600 权限的密钥文件(读取 --file 或标准输入) |
waitbus doctor | 验证安装(路径、二进制文件、凭据存储、单元、/metrics) |
waitbus status | 运维仪表板:数据库行数、守护进程存活状态 |
waitbus verify-plugin | 验证 .claude-plugin/plugin.json |
waitbus listener serve | HTTP webhook 接收器(回环地址 :9000) |
waitbus broadcast serve | AF_UNIX 广播中枢守护进程 |
waitbus etag-poll run | GitHub API ETag 轮询工作进程 |
waitbus mcp serve | MCP 服务(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 waitCLI 使用 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,并遵循与其余配置链相同的先匹配优先规则:
WAITBUS_STATE_DIR/WAITBUS_RUNTIME_DIR环境变量(由运维人员控制)。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 的 macOSuser_runtime_dir属于可被 Apple 清理的缓存, 不适用于 AF_UNIX 套接字)。
- **Linux:**状态目录为
随附的 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 用户实例在没有打开交互式会话时持续运行:
-
让用户管理器在注销后继续运行。 运行一次:
loginctl enable-linger <username>否则,最后一个交互式会话结束时,systemd 用户管理器及其所有单元都会停止。
-
无需解锁密钥环。 密钥存放在用户状态目录下权限为
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 + systemd | macOS + launchd | 备注 |
|---|---|---|---|
| Python 监听器 | 是 | 是 | 绑定到回环地址,仅使用标准库 |
| SQLite 事件存储 | 是 | 是 | 单文件数据库,无需服务器 |
| 广播守护进程 | 是 | 是 | AF_UNIX SOCK_STREAM + 长度前缀帧格式 |
| 门铃原语 | os.eventfd | SOCK_STREAM ping | 在绑定位置内联分派 |
| 对端凭据 UID 检查 | SO_PEERCRED | 通过 ctypes 调用 getpeereid() | 特意不使用 LOCAL_PEERPID(参见 SECURITY.md) |
| 安装入口 | waitbus install-systemd | waitbus install-launchd | 两者均会拒绝在另一平台上运行 |
| 进程管理器 | systemd-user 单元 | launchd LaunchAgents | 8 个 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。
更多信息
- CHANGELOG.md:版本历史和迁移说明
- SECURITY.md:HMAC 威胁模型、平台假设和问题报告流程
- .github/CONTRIBUTING.md:各部分的开发指南、在本地加载插件
- docs/emitters/recipes.md:shell 和 docker 的事件发送示例,以及面向外部生产者的 CloudEvents 信封映射
- docs/emitters/claude-code-hook.md:在 Claude Code 会话生命周期钩子上发送总线事件
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。
