内容
<p align="center">
<img src="assets/banner.png" alt="Sentou: 来自我,致你" width="820" />
</p>
[](https://github.com/TrueLineCollective/sentou/actions/workflows/ci.yml)
[](./LICENSE)
<p align="center">
<img src="assets/dashboard-routes.png" alt="Sentou 路线图,显示实时跟踪的链接作为交通线路网络" width="820" />
</p>
Sentou 将 Claude 工件或任意 HTML 发布到您控制的私有链接。您可以决定谁可以打开它。您可以在原处继续编辑,链接永远不会断裂。所有内容都在您自己的基础设施上运行,因此文档和已打开文档的人员列表都存储在您拥有的服务器上。
通常,分享您在 Claude 中创建的内容意味着以下两种妥协之一:将其放在公共网址上并希望它保持不变,或者将其交给一个平台,该平台的业务是跟踪谁阅读了您的作品。Sentou 是两者的替代方案。
<p align="center">
<img src="assets/loop.gif" alt="同一个 Sentou 链接从一个版本更新到下一个版本" width="820" />
<br /><sub>一次性发布,然后在 Claude 中继续编辑。链接保持不变。</sub>
</p>
这还是早期阶段。核心功能已经实现并得到了测试,但下面的路线图中仍有一部分尚未完成。我在公开环境中构建它,并保持此页面的更新,诚实地反映已发布和仍在计划中的内容。
## 使用方法
Sentou 适用于与特定人员分享您创建的内容,而不是整个互联网。如果您只需要一个公共网址,任何静态主机都可以免费提供。Sentou 适用于其他情况:决定谁可以打开它,查看谁实际打开了它以及打开了多长时间,并将您的内容和查看者列表存储在您拥有的服务器上。
1. **在具有公共地址的服务器上部署它** (小型 VPS、容器主机、公司服务器)。这使得链接可以被其他人访问。在 `localhost` 上运行只是为了试用,因为 `localhost` 链接仅在您自己的机器上有效。
2. **创建工件。** 在仪表板中,粘贴 HTML 或上传 `.html` 文件,然后设置门控:要求电子邮件,限制为公司域名,添加过期时间,开启跟踪。
3. **发布。** 您将获得一个类似 `https://sentou.yourdomain.com/v/abc123` 的链接。
4. **以您通常的方式发送链接** ,通过电子邮件、Teams、Slack 或短信。Sentou 创建链接;您分享它。
5. **他们在浏览器中打开并与之交互。** 如果您设置了门控,他们需要先输入电子邮件(或一次性代码)。您可以在仪表板中查看打开和停留时间。
6. **随时更改。** 编辑并重新发布,相同的链接将更新,因此您已经发送给其他人的版本将看到最新版本。无需重新发送。
由于它是自我托管的,您可以在自己的基础设施上运行它。这是隐私的保障,您的内容和查看者列表永远不会离开您的服务器,这也是一个真正的设置步骤。带有部署跳过功能的托管版本正在路线图中。
## 目前的功能
- 一个用于管理链接的真实网络仪表板:路线图(所有链接,带有打开次数和实时/过期/撤销状态),一个带有实时沙盒预览的撰写屏幕,每个链接的分析带有旅行者地图,显示谁打开了什么以及打开了多长时间,以及收藏、团队、设置和帐户屏幕。
- 具有角色和邀请的多用户帐户。第一个注册帐户成为所有者。其他人只能通过邀请加入。成员共享同一个工作区,可以根据其角色查看和管理链接。
- 通过 HTTP API 或从 Claude 通过 MCP 服务器发布工件或原始 HTML。
- 在 Claude 中编辑并重新发布到相同的链接。网址保持不变,已经拥有它的任何人都将继续访问最新版本。链接在其原始版本被冻结后会跟随您的最新版本。
- 控制谁可以访问:要求电子邮件,限制为公司域名,设置过期时间,或在您完成后撤销链接。
- 如果您开启,可以查看谁打开了链接,何时打开以及停留了多长时间。跟踪默认关闭,按链接开启(发布时 `track: true`)。当开启时,查看者会告知收件人链接记录了他们的访问,您可以从仪表板或 `/api/stats` 读取总数。每次打开、查看者电子邮件和停留时间都存储在您的服务器上。
- 将相关链接捆绑到一个收藏中,其自己的可分享页面位于 `/c/<slug>`。每个链接都保留其自己的门控。
- 当链接首次打开时,通过电子邮件或 webhook 通知,设置每个工作区在设置中。
## 沙盒的工作原理
Sentou 工件是任意的 HTML 和 JavaScript,其他人在自己的浏览器中加载。这是真正的攻击面,也是需要最多关注的部分。工件以 `sandbox allow-scripts` 指令在内容安全策略中提供,并在不允许同源访问的 `allow-scripts` iframe 中渲染。内容安全策略还允许内联和 `eval` 脚本以及 HTTPS 资源,因为工件需要运行自己的代码;负载隔离是 `sandbox` 指令,而不是源列表。脚本仍然运行,因此工件保持交互,但浏览器为它们提供了一个不透明的源,没有返回父页面或其数据的方法。
## 隐私和数据
Sentou 在您控制的基础设施上存储尽可能少的数据。以下是存储在磁盘上的内容:
- **工件** 和您重新发布的每个版本。
- **对于带有 `verifyEmail` 的门控链接** ,每个查看者的已验证电子邮件。没有验证的门控要求电子邮件作为访问摩擦,但不存储它。Sentou 永远不会保留未验证的地址。
- **对于跟踪链接** ,每次访问的打开事件(时间戳和停留时间),归因于已验证的查看者的电子邮件或 `anon` 否则。
所有内容都存储在一个位于 `SENTOU_DB` 的 SQLite 数据库中。没有遥测数据,也不会将任何数据发送到您的服务器外部。像保护个人数据文件一样保护此文件:限制权限,并在暴露的主机上使用磁盘加密。设置 `SENTOU_RETENTION_DAYS` 以修剪旧于指定窗口的查看者和事件数据,并使用 `/api/forget` 按需擦除链接的数据或一个查看者的。
## 快速入门(自我托管)
需要 Node 20.9 或更新版本和 Git。
```bash
git clone https://github.com/TrueLineCollective/sentou.git
cd sentou
npm install
echo "SENTOU_SECRET=$(openssl rand -hex 32)" > .env.local # 生产环境中需要;本地开发环境中省略,使用随机进程间密钥(开发环境会话在重启后重置)
npm run dev
```
如果端口 3000 已被占用,请在另一个端口上运行,并告诉 Sentou 从哪个公共 URL 构建链接,否则它返回的链接将指向端口 3000:
```bash
echo "SENTOU_BASE_URL=http://localhost:3001" >> .env.local
PORT=3001 npm run dev
```
(参见 `.env.example` 获取所有可用的设置。)
打开 `http://localhost:3000/setup` 以创建所有者帐户。第一个帐户是所有者;进一步注册仅限邀请。设置完成后,仪表板位于 `http://localhost:3000/`。
从仪表板中,您可以撰写新路线,复制查看者 URL,并查看谁打开了它们以及打开的频率(每次加载时,都会反映最新信息)。在撰写部分,您可以粘贴 HTML 或放入 `.html` 文件(拖放或上传按钮),并在发布前进行实时沙盒预览。对于自动化或 MCP 使用,通过帐户屏幕或 `POST /api/keys` 生成 API 密钥(密钥值仅返回一次)。将其作为 `Authorization: Bearer <key>` 发送到任何写入端点。
## 部署
`npm run dev` 适用于本地工作,而不是互联网。Sentou 将其数据存储在本地磁盘上的单个 SQLite 数据库中,因此请将其作为 **具有持久卷的单个实例运行**。不要将其部署到无服务器或自动扩展平台(Vercel Functions,多副本容器):每个实例都将获得自己的存储库,您的链接将分散在这些实例上。一个容器,一个磁盘。
存储是一个 SQLite 文件,因此它可以处理个人和团队规模(数十个链接,数千个事件),而无需外部服务,但它不适用于高流量。重度、多租户流量是托管层面的用途。在前面放置一个您控制的反向代理(Caddy、nginx、平台入口)以获取 TLS,并使每个 IP 的速率限制信任其客户端地址。
### 使用 Docker
```bash
export SENTOU_SECRET=$(openssl rand -hex 32)
export SENTOU_BASE_URL=https://sentou.yourdomain.com
docker compose up -d --build
```
存储位于 `sentou-data` 卷中,并在重启后存活。在其前面放置一个 TLS 终止反向代理(Caddy、nginx、平台入口)。
然后打开 `https://your-domain/setup` 并立即创建所有者帐户。在您这样做之前,访问 `/setup` 的第一个访客将获得所有权,因此请在分享 URL 之前声明它。
### 不使用 Docker
```bash
npm ci && npm run build
SENTOU_SECRET=... SENTOU_BASE_URL=https://... npm run start
```
`npm run start` 是此处支持的命令。由于构建会发出一个独立的捆绑包,Next.js 会打印一个警告,建议使用 `node .next/standalone/server.js` 代替。对于此部署样式,请忽略它:独立服务器期望资产和数据库迁移已复制到其旁边(Docker 镜像会执行此操作),因此请使用 `npm run start`,它从项目根目录运行,所需的一切都已就位。
### 暴露实例的环境
| 变量 | 原因 |
| --- | --- |
| `SENTOU_SECRET` | 签名和加密会话及访问 cookie。生产环境中需要。生成方法:`openssl rand -hex 32`。在非生产环境中,使用随机进程间密钥(开发环境会话在重启后重置)。 |
| `SENTOU_BASE_URL` | 链接所基于的公共 URL。未设置时,生成的链接指向 `http://localhost:3000`。Better Auth 先读取 `BETTER_AUTH_URL`,然后回退到 `SENTOU_BASE_URL`;设置其中一个。 |
| `BETTER_AUTH_URL` | 可选。覆盖 `SENTOU_BASE_URL` 仅用于身份验证。将其设置为非 localhost 主机也会激活暴露实例的姿态:写入端点随后需要身份验证会话或 API 密钥。 |
| (所有者身份验证) | 首次注册的帐户成为所有者。所有其他注册需要通过仪表板或 API 发送的邀请。 为自动化或 MCP 使用生成 API 密钥,从帐户屏幕或 `POST /api/keys`。明文密钥仅返回一次,不存储。将其作为 `Authorization: Bearer <key>` 发送到写入端点。 |
可选:`SENTOU_RESEND_KEY` + `SENTOU_EMAIL_FROM` 使 `verifyEmail` 链接成为真正的边界,并且在生产环境中需要发送工作区邀请。`SENTOU_RETENTION_DAYS` 清除存储的查看者和跟踪数据,这些数据已超过 N 天。
## API
每个端点都接受和返回 JSON。写入端点(发布、重新发布、撤销、统计、忘记、密钥)在任何公开或生产实例上都需要身份验证。发送登录的 Better Auth 会话 cookie 或 `Authorization: Bearer <key>` 以及通过 `POST /api/keys` 铸造的每个用户 API 密钥。
```bash
# 发布,带有完整的门控。每个门控字段都是可选的;省略它们以获取公共链接。
curl -X POST $URL/api/publish -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{
"html": "<h1>Q3 board deck</h1>",
"title": "Q3 board deck",
"requireEmail": true,
"verifyEmail": true,
"allowedDomains": ["acme.com"],
"expiresAt": "2030-01-01T00:00:00.000Z",
"track": true
}'
# -> { "id", "slug", "url", "version" }
CT='content-type: application/json'
curl -X POST $URL/api/republish -H "authorization: Bearer $TOKEN" -H "$CT" -d '{"id":"...","html":"<h1>v2</h1>"}'
curl -X POST $URL/api/revoke -H "authorization: Bearer $TOKEN" -H "$CT" -d '{"id":"..."}'
curl "$URL/api/stats?id=..." -H "authorization: Bearer $TOKEN" # -> { linkId, totalOpens, viewers[] }
curl -X POST $URL/api/forget -H "authorization: Bearer $TOKEN" -H "$CT" -d '{"id":"..."}' # 擦除链接的所有查看者数据
curl -X POST $URL/api/forget -H "authorization: Bearer $TOKEN" -H "$CT" -d '{"id":"...","email":"a@acme.com"}' # 擦除一个查看者
```
`/api/access` (向门控链接提交电子邮件),`/api/access/verify` (提交电子邮件的代码),以及 `/api/track` (查看者的打开和关闭信标)由于不是所有者,因此按设计无需身份验证。它们受到速率限制。
API 密钥在 `POST /api/keys` 处铸造,在 `POST /api/keys/revoke` 处撤销(所有者范围)。开放通知通道(电子邮件和 webhook)在设置屏幕中按工作区配置。
## 下一步
已发布:网络仪表板(路线图,带有实时预览的撰写,每个链接的分析带有旅行者地图,收藏,团队,设置,帐户),多用户帐户,带有所有者和仅限邀请的成员角色,发布和重新发布循环,沙盒查看器,MCP 服务器,门控层(电子邮件,域名允许列表,过期,撤销,电子邮件验证),每个收件人的跟踪,以及通过电子邮件或 webhook 的开放通知。
接下来,大致按顺序:托管版本供任何不想运行服务器的人使用,然后是企业功能(SSO,审计日志,数据驻留)。托管服务首先出现,因为它将为其他功能提供资金。
## 为什么是开源
这里的开源是一种赌博,而不是一种礼物。一个处理您的文档的工具通过每一行都是可读的并且您可以自己运行它来获得更多信任。而且,一些试用自我托管版本的人将为托管版本付费,因为建立一个服务器是大多数人更愿意跳过的。因此核心是免费的,而便捷的部分是要花钱的。
具体来说:此存储库中 `/ee` 文件夹之外的所有内容都是 AGPL-3.0 许可的。自我托管,修改,保持它,只要你喜欢。许可证要求一件事:如果你为其他人运行修改后的服务,请与他人分享你的修改。托管的 Sentou Cloud 和企业功能将是商业化的,它们将为开源核心提供资金。 [LICENSING.md](./LICENSING.md) 包含完整的条款。
## 贡献
欢迎提出问题和拉取请求。如果你有功能想法,请先开一个问题,以明确其位置,然后再编写代码。无需 CLA,无需仪式。
## 谁在背后
Sentou 是一个 [True Line Collective](https://github.com/TrueLineCollective) 项目。