ntfy
ntfy 是一个简单的基于 HTTP 的发布-订阅通知服务。它可与 ntfy.sh 上的免费公共服务器或任何自托管实例配合使用,支持任何能发起 HTTP 请求的客户端——手机、浏览器、脚本、手表。
ntfy 是 Hermes 的轻量级推送渠道的理想选择:通过 ntfy 移动应用 订阅一个 topic(主题),向该 topic 发送消息与 agent 对话,然后在手机上收到回复。
前提条件
- 一个 topic 名称(任意唯一字符串——
hermes-myname-2026即可) - 已安装 ntfy 移动应用 并订阅该 topic
- 可选:自托管的 ntfy 服务器,或用于私有/保留 topic 的
ntfy.sh账户 token
仅此而已。无需 SDK、无需守护进程、无需 Node.js。适配器使用 httpx,该库已是 Hermes 的依赖项。
配置 Hermes
通过设置向导
hermes setup gateway
选择 ntfy 并按提示操作。
通过环境变量
将以下内容添加到 ~/.hermes/.env:
NTFY_TOPIC=hermes-myname-2026
NTFY_ALLOWED_USERS=hermes-myname-2026
NTFY_HOME_CHANNEL=hermes-myname-2026
| 变量 | 是否必填 | 说明 |
|---|---|---|
NTFY_TOPIC | 是 | 要订阅的 topic(接收消息) |
NTFY_SERVER_URL | 可选 | 服务器 URL(默认:https://ntfy.sh)——指向自托管 ntfy 以保护隐私 |
NTFY_TOKEN | 可选 | Bearer token(如 tk_xyz)或用于 Basic 认证的 user:pass |
NTFY_PUBLISH_TOPIC | 可选 | 用于发送回复的不同 topic(默认与 NTFY_TOPIC 相同) |
NTFY_MARKDOWN | 可选 | 设为 true 以使用 X-Markdown: true 请求头发送回复 |
NTFY_ALLOWED_USERS | 推荐 | 允许的 topic 名称(逗号分隔,视为用户 ID;见下文) |
NTFY_ALLOW_ALL_USERS | 可选 | 设为 true 以允许所有发布者——仅在具有读取 token 的私有 topic 下安全 |
NTFY_HOME_CHANNEL | 可选 | cron 任务/通知投递的默认 topic |
NTFY_HOME_CHANNEL_NAME | 可选 | 主渠道的可读标签 |
身份模型——部署前请阅读
ntfy 没有原生的已认证用户身份。已发布消息中的 title 字段由发布者控制,可以是发布者想要的任何内容。Hermes 适配器不使用 title 进行授权——否则任何知道 topic 的发布者都可以伪造允许的用户。
相反,topic 名称本身即为身份。发布到该 topic 的每条消息都被视为来自同一个逻辑用户(即该 topic)。因此 NTFY_ALLOWED_USERS 通常就是 topic 名称本身——一个控制整个渠道访问的单条目白名单。
这意味着任何知道 topic 的人都可以与 agent 对话。要将其变为真正的信任边界:
- 自托管 ntfy 并通过访问控制锁定 topic。只有持有读/写 token 的授权客户端才能发布。
- 或在 ntfy.sh 上使用私有 topic(保留 topic 需要账户),并通过
NTFY_TOKEN保护。 - 或选择一个长且难以猜测的 topic 名称(
hermes-7d4f9c8b-2026),将其视为共享密钥。这是最轻量的方案,但 topic 名称可能通过日志或截图泄露。
在任何情况下,除非底层 topic 已启用访问控制,否则不要通过 ntfy 传输敏感数据。
快速开始——从手机与 agent 对话
- 选择一个 topic 名称:
hermes-myname-2026 - 在手机上:安装 ntfy 应用,点击 +,输入
hermes-myname-2026 - 在主机上:
echo 'NTFY_TOPIC=hermes-myname-2026' >> ~/.hermes/.env
echo 'NTFY_ALLOWED_USERS=hermes-myname-2026' >> ~/.hermes/.env
hermes gateway restart - 从 ntfy 应用向该 topic 发送一条消息。agent 的回复将以推送通知的形式送达。
在 cron 任务中使用 ntfy
设置 NTFY_HOME_CHANNEL 后,cron 任务即可投递到 ntfy:
cronjob(
action="create",
schedule="every 1h",
deliver="ntfy", # uses NTFY_HOME_CHANNEL
prompt="Check for alerts and summarise."
)
或显式指定目标 topic:
send_message(target="ntfy:alerts-channel", message="Done!")
即使 cron 在 gateway 进程外运行,此功能也有效——插件注册了一个 standalone_sender_fn,会自行建立 HTTP 连接。
自托管 ntfy
如需完全掌控:
# Docker
docker run -p 80:80 -it binwiederhier/ntfy serve
# Native
go install heckel.io/ntfy/v2@latest
ntfy serve
然后将 Hermes 指向该实例:
NTFY_SERVER_URL=https://ntfy.mydomain.com
NTFY_TOPIC=hermes
NTFY_TOKEN=tk_abc123 # if you've set up access control
自托管可提供 topic 访问控制、消息持久化策略、附件和 emoji 标签。参见 ntfy 服务器文档。
Markdown 格式化
当发布者设置 X-Markdown: true 请求头时,ntfy 客户端会渲染 Markdown。要为 Hermes 的出站回复启用此功能:
NTFY_MARKDOWN=true
或在 config.yaml 中配置:
platforms:
ntfy:
extra:
markdown: true
移动应用支持 CommonMark 的子集——粗体、斜体、列表、链接、围栏代码块。确切支持范围参见 ntfy 的 Markdown 文档。
仅出站设置(只推送通知,不接收消息)
如果只希望 Hermes 推送通知到 ntfy(cron 摘要、告警),而不接受任何回复消息,可将 NTFY_TOPIC 和 NTFY_PUBLISH_TOPIC 设为相同值,并完全省略 NTFY_ALLOWED_USERS。没有白名单时,agent 不会响应任何入站消息——手机可收到推送,但对话是单向的。
限制
- 消息大小:ntfy 将消息体上限设为 4096 个字符。超出时 Hermes 会截断并发出警告。
- 无输入状态指示:协议不支持此功能;
send_typing为空操作。 - 无线程或附件:ntfy 是纯推送通知。长回复保留在消息体中,不会分线程展开。
- 无原生用户身份:参见上文的身份模型章节。
故障排查
认证失败 / 401 — NTFY_TOKEN 有误,或该 token 对此 topic 没有发布/订阅权限。适配器在收到 401 时会停止重连循环,gateway 运行时状态将显示 fatal: ntfy_unauthorized。修正 token 后重启 gateway。
Topic 未找到 / 404 — NTFY_TOPIC 在所配置的服务器上不存在。对于 ntfy.sh,topic 在首次发布时自动创建,因此 404 意味着你指向的自托管服务器尚未创建该 topic。适配器会停止重连循环并显示 fatal: ntfy_topic_not_found。
已连接但收不到消息 — 检查 NTFY_ALLOWED_USERS 是否包含 topic 名称本身。在 ntfy 的身份模型中,topic 即用户;白名单为空时所有消息都会被拒绝。
每 60 秒重连一次 — 流式 keepalive 默认为 55 秒;ntfy 可能存在间歇性网络问题。适配器采用指数退避(2 → 5 → 10 → 30 → 60 秒),一旦流保持存活 ≥60 秒则重置为 0。