shipmail-to/agent-inbox-starter
GitHub: shipmail-to/agent-inbox-starter
基于 Bun 和 TypeScript 的 Shipmail AI 邮件收件箱分类代理入门套件,通过多层安全控制让 Claude 安全地对入站邮件进行分拣并生成回复草稿。
Stars: 0 | Forks: 0
# Shipmail AI agent inbox 入门套件
一个独立的 Bun 和 TypeScript starter,用于通过 Claude 对 Shipmail 收件箱进行分类。它会验证 Shipmail webhooks,拦截未经批准的发件人,移除常见的 prompt injection 内容,对邮件进行分类,并创建防过期的回复草稿。默认情况下不开启发送功能。
## 入站消息的处理流程
1. Shipmail 发送一个已签名的 `message.received` webhook。
2. 在验证签名之前,服务器会拒绝大于 1 MB 的请求体。这同时适用于声明的 `Content-Length` 值和流式传输的请求体。
3. 服务器验证签名并校验当前的 webhook 合约。
4. 在获取收件箱正文之前,会先检查可见的 `From` 地址。
5. Webhook 的 `email_id` 用于选择会话中对应的 provider 消息。如果缺失或未找到,接下来会尝试追踪到的消息 ID,最后尝试发件人的最新消息。
6. 当需要发件人身份验证时,消息必须具有 DMARC `pass` 验证结果。未通过验证的邮件将被升级处理,不会调用 Claude 或创建草稿。
7. 消息将被转换为纯文本。控制字符、类似指令的行以及未经批准的 URL 将被移除。
8. Claude 将消息分类为 `needs_reply`、`ignore` 或 `escalate`。
9. 常规回复会通过 `createInboxReplyDraft` 连同当前的 `reply_version` 一起保存。
10. 仅当 `AUTO_SEND=true` 时才会发送草稿。草稿的创建和发送使用从 webhook 事件 ID 派生的相互独立的稳定幂等键。
被忽略的会话将被标记为 `no_reply_expected`。被升级处理的会话将保留在回复队列中,等待人工审核。
## 五分钟快速开始
前提条件:[Bun](https://bun.sh)、一个 [Shipmail 账户](https://shipmail.to) 以及一个 Anthropic API key。
1. 在 Shipmail 中,为 agent 创建一个 mailbox。
2. 创建一个具有权限范围限制的 API key。初始权限应包含收件箱读取和草稿写入权限。只有在计划启用 `AUTO_SEND` 时才添加发送权限。
3. 复制并填写环境变量文件:
cp .env.example .env.local
4. 安装并运行服务器:
bun install
bun --env-file .env.local run dev
5. 使用以下任一隧道暴露本地服务器:
cloudflared tunnel --url http://localhost:3000
ngrok http 3000
6. 为 `message.received` 创建一个 Shipmail webhook,地址指向 `https://your-tunnel.example/webhook`。将 webhook 的签名密钥放入 `SHIPMAIL_WEBHOOK_SECRET` 中。
健康检查 endpoint 为 `GET /health`。决策日志不会包含消息正文。
## 配置
| 变量 | 用途 |
| --- | --- |
| `SHIPMAIL_API_KEY` | 具有权限范围限制的 Shipmail API key |
| `SHIPMAIL_MAILBOX_ID` | 此进程接受的唯一 mailbox |
| `SHIPMAIL_WEBHOOK_SECRET` | 创建 webhook 时返回的签名密钥 |
| `ANTHROPIC_API_KEY` | Anthropic API key |
| `PORT` | 本地 HTTP 端口,默认为 `3000` |
| `SHIPMAIL_ALLOWED_SENDERS` | 必需的逗号分隔地址,或类似 `*@example.com` 的条目 |
| `SHIPMAIL_ALLOWED_URL_HOSTS` | 可选的确切 host,或类似 `*.example.com` 的条目 |
| `AUTO_SEND` | 为 `true` 时发送生成的草稿。默认为 `false` |
| `REQUIRE_AUTHENTICATED_SENDER` | 在调用 Claude 之前要求 DMARC `pass`。默认为 `AUTO_SEND` 的值,因此当开启自动发送时,该选项默认开启 |
模型在 `src/agent.ts` 中被固定为 `claude-sonnet-5`。
## 沙盒测试
Shipmail 沙盒入站注入功能可以测试真实的 mailbox 和 webhook 路径,而无需通过 SMTP 发送邮件:
```
bun --env-file .env.local run sandbox:e2e
```
该脚本调用 `mailboxes.injectSandboxInbound`。其默认发件人是 `sandbox-sender@example.com`,因此需将该确切地址添加到 `SHIPMAIL_ALLOWED_SENDERS` 中以便进行测试。如有需要,可使用 `SANDBOX_FROM` 覆盖它。此脚本不属于 `bun test` 的一部分,并且需要真实的 Shipmail API key。
自动化流水线测试使用了模拟的 Shipmail 和 Anthropic 客户端。它不会发起任何网络调用:
```
bun test
```
## 安全模型
电子邮件是受攻击者控制的输入。在此 starter 中,在模型能够影响任何操作之前,会应用多层独立的安全控制:
- 在验证签名之前强制执行 1 MB 的 webhook body 限制
- 可见 `From` 地址的 allowlisting,支持可选的显式 `*@domain` 条目
- 当 `REQUIRE_AUTHENTICATED_SENDER=true` 时,在创建模型或草稿之前强制执行 DMARC 验证
- 对 `message.received` 进行签名验证和严格的 Zod 校验
- Mailbox ID 绑定
- 移除 HTML、脚本、注释、控制字符以及 Unicode 双向控制字符
- 在输入到模型之前,替换常见的 prompt injection 和角色操纵语句
- 公共 HTTPS URL 检查,拒绝包含凭证、localhost、私有地址、link-local、保留地址以及非常规数字 IP 格式的请求
- URL host allowlisting,所有其他 URL 将被脱敏
- 系统策略,以及 JSON 和 XML 样式的非可信数据分隔符
- 对模型允许的三种决策进行 Zod 校验
- 对生成的草稿进行二次安全检查
- 默认采取仅生成草稿的行为
- 进行 Shipmail `reply_version` 检查,并为草稿创建和发送使用基于事件的幂等键
Shipmail 会为每个收件箱消息返回 SPF、DKIM 和 DMARC 验证结果。当开启发件人身份验证强制检查时,此 starter 要求必须通过 DMARC。DMARC 将可见的 `From` 域名与对齐的 SPF 或 DKIM 结果绑定在一起。仅靠 SPF 是不够的,因为它检查的是 envelope sender,这可能与可见的 `From` 地址不同。
如果身份验证结果缺失、格式错误、为空,或者 DMARC 的验证结果不是 `pass`,该消息将保留在回复队列中等待人工审核。验证结果和原因将被记录。不会调用 Claude,也不会创建草稿。在 `AUTO_SEND` 模式下强制验证默认开启,在仅草稿模式下默认关闭。显式设置 `REQUIRE_AUTHENTICATED_SENDER` 可覆盖该默认值。
Allowlisting 虽然降低了风险,但在关闭身份验证强制检查时,并不能使发件人变得完全可信。模型的防御措施并不能防范未来所有的 prompt injection 技术。请针对您的使用场景审查策略、发件人列表、模型输出和操作日志。敏感、安全、法律、账户访问和财务相关的消息均被设计为需升级处理。
## 部署到 Railway
从此仓库创建一个服务,添加 `.env.example` 中的环境变量,并使用:
```
bun install --ignore-scripts
bun run start
```
Railway 会提供 `PORT`,starter 会读取该变量。将健康检查路径设置为 `/health`,然后创建 Shipmail webhook,地址指向 `https://your-service.example/webhook`。
## 部署到 Vercel
`api/webhook.ts` 适配器将相同的 Web Request handler 作为 Vercel Function 导出。导入该仓库,添加所有必需的环境变量并部署。在以下地址创建 Shipmail webhook:
```
https://your-project.vercel.app/api/webhook
```
Vercel 路由不会启动 `Bun.serve`。本地和 Railway 的入口点仍然使用 `src/index.ts`。
## 示例
- `examples/vercel-ai-sdk`:通过 `ai` 包和 `@ai-sdk/anthropic` 进行最小化分类
- `examples/langgraph`:一个轻量级的 LangGraph 分类节点
- `examples/mcp-client`:托管的 HTTP 和本地 stdio MCP 配置,以及一个工具列表脚本
每个示例都有自己的 `package.json`,因此根目录的测试不会安装示例的依赖项。
## Shipmail 链接
- [文档](https://shipmail.to/docs)
- [面向 AI agents 的电子邮件指南](https://shipmail.to/docs/guides/email-for-ai-agents)
- [TypeScript SDK](https://shipmail.to/docs/sdks/typescript)
- [MCP 指南](https://shipmail.to/docs/mcp)
- 托管的 MCP endpoint:`https://shipmail.to/api/mcp`
## 许可证
MIT。详见 `LICENSE`。
标签:Bun, TypeScript, Webhook, 力导向图, 启动模板, 安全插件, 自动化攻击, 邮件处理