uzuraDev/html-vault
GitHub: uzuraDev/html-vault
一个可自托管的密码保护保险库,用于存储和沙盒预览 LLM 生成的 HTML 代码片段,并提供 MCP 集成实现 AI 对话中的自动保存。
Stars: 0 | Forks: 0
# HTML 保管库
**English** | [日本語](README.ja.md)
可自行托管、受密码保护的保险库,用于存储和安全预览(沙盒 iframe)你的 HTML 代码片段。单个 Docker 镜像即可运行在 VPS、Fly.io、Render、家庭服务器或 Raspberry Pi 上。
它的目标受众是那些使用 LLM(Claude / ChatGPT 生成的工件、AI 解释器、仪表盘)生成 HTML,并希望将这些代码片段存储在自己的基础设施上并进行安全预览,而不是将其粘贴到第三方在线工具中的人。这是一个早期的个人 OSS 项目——欢迎提供反馈和提问。

## 60 秒快速体验
```
docker run -p 3000:3000 -e AUTH_PASSWORD=change-me ghcr.io/uzuradev/html-vault:latest
```
打开 **http://localhost:3000** 并使用你设置的 `AUTH_PASSWORD` 登录。在此次临时运行中,数据存储在内存中;若要持久化存储,请添加卷:`-v "$PWD/data:/data"`。
[](https://render.com/deploy?repo=https://github.com/uzuraDev/html-vault)
该按钮使用了仓库的 `render.yaml` Blueprint。有关 Fly.io、Render 和自托管的详细信息,请参阅下方的[部署](#deploy)。
## 快速开始
```
cp .env.example .env # set AUTH_PASSWORD (and SESSION_SECRET)
docker compose up -d
```
打开 **http://localhost:3000** 并使用 `AUTH_PASSWORD` 登录。系统不会自动生成密码或将其写入日志——如果你没有设置 `AUTH_PASSWORD`,请使用 `docker compose exec html-vault node setpass.js` 创建一个(此命令也可用于稍后更改密码)。
## 语言(构建时)
UI 和服务器消息在构建时即已内置——没有运行时切换选项。请设置 `APP_LANG`(`en`/`ja`,默认为 `en`):
- **Docker**:在 `.env` 中进行设置,然后运行 `docker compose up -d --build`
- **Node**:`APP_LANG=ja npm start`
字符串文件位于 [`locales/`](locales) 中。你可以通过复制一个语言文件并使用该 `APP_LANG` 进行构建来添加新语言。
## 环境变量
| 变量 | 默认值 | 描述 |
|------|------|------|
| `PORT` | `3000` | 主机端口(容器固定为 3000);原生 Node 的监听端口 |
| `HOST` | `0.0.0.0` / `127.0.0.1` (Node) | 绑定地址 |
| `SESSION_SECRET` | 每次启动时随机生成 | Session 签名密钥。在生产环境中请设置固定值(`openssl rand -hex 32`) |
| `BEHIND_HTTPS` | `0` | 在 TLS 终止代理后设置为 `1`(启用 Secure cookies) |
| `DATA_DIR` | `/data` / `./data` (Node) | 数据目录 |
| `MAX_UPLOAD_MB` | `10` | HTML 最大大小(MB) |
| `AUTH_PASSWORD` | 未设置 | 首次登录密码(或运行 `setpass.js`)。仅在 `auth.json` 存在之前使用 |
| `APP_LANG` | `en` | UI/消息语言(`en`/`ja`),在构建时应用 |
| `API_TOKEN` | 未设置(禁用) | 用于无头 API 访问(`POST`/`GET /api/snippets`)的 Bearer token。为 [stdio MCP server](#mcp-integration-headless-upload) 提供支持。 |
| `MCP_SECRET_PATH` | 未设置(禁用) | 为 claude.ai 风格的自定义连接器启用远程 MCP 端点 `/mcp/`(未设置时返回 404)。使用 `openssl rand -hex 24` 生成。 |
## 部署
- **VPS / 家庭服务器 / Raspberry Pi**:`docker compose up -d`。公开访问时请使用 HTTPS(请参阅安全性)。详细信息:[deploy/DEPLOY.md](deploy/DEPLOY.md)。Cloudflare Tunnel:[deploy/CLOUDFLARE.md](deploy/CLOUDFLARE.md)。
- **预构建镜像**:`ghcr.io/uzuradev/html-vault:latest`(在 `docker-compose.yml` 中将 `build:` 替换为 `image:`)。
- **Fly.io**:包含 `fly.toml` —— `fly launch --no-deploy`,创建一个卷,设置 `SESSION_SECRET`,然后 `fly deploy`。
- **Render**:包含 `render.yaml` Blueprint(持久化磁盘需要付费实例)。
## 安全性
| 威胁 | 缓解措施 |
|------|------|
| 未经授权的访问 | 需要登录,bcrypt,速率限制(15 分钟 10 次) |
| 存储的 HTML 带来的 XSS | 在 `sandbox` iframe 中预览(无 `allow-same-origin`);源码以 `text/plain` 格式提供 |
| Session 劫持 | HttpOnly / SameSite=Strict / Secure (HTTPS) cookie |
| CSRF | 在修改数据的 API 上使用双重提交 token |
| 路径遍历 | 服务器生成的 ID,仅限 32 位十六进制 |
| Headers | 通过 helmet 设置 CSP / X-Frame-Options |
公开访问时:请使用 HTTPS 并设置固定的 `SESSION_SECRET`。可选择添加前置验证(Basic auth / Cloudflare Access)。
注意:系统不会自动生成密码或将其写入日志——请设置 `AUTH_PASSWORD` 或运行 `setpass.js` 来创建首次登录。预览的 HTML 仍然可以发出外部请求(外部图片/脚本/表单);如果你打开了不受信任的 HTML,请通过 CSP 进行限制。
## MCP 集成(无头上传)
在对话期间,将模型生成的 HTML 直接保存到保险库中。根据客户端的不同,有两种路径。
### A. 本地 MCP 客户端(例如 Claude Code)— stdio MCP
1. 在保险库上设置一个 `API_TOKEN`(`.env`,例如 `openssl rand -hex 32`)并重启。设置 token 后,`POST /api/snippets` 和 `GET /api/snippets` 也会接受 `Authorization: Bearer ` —— 这些操作无需登录/CSRF。将 `API_TOKEN` 保持未设置状态可禁用 token 验证(默认)。
2. 运行 [`mcp/`](mcp) 中捆绑的 MCP 服务器,并将其注册到你的客户端。有关 `.mcp.json` 示例以及 `upload_html` / `list_snippets` 工具,请参阅 [mcp/README.md](mcp/README.md)。
该 token 是一个写入凭证——请妥善保密,建议使用 HTTPS,并通过更改 `API_TOKEN` 来轮换它。Token 请求会跳过 CSRF(浏览器不会自动附加 `Bearer` header,因此它不是 CSRF 攻击媒介);cookie/session 流程仍会强制执行 CSRF。
### B. claude.ai / 移动应用 — 内置远程 MCP
在 claude.ai 中将保险库注册为**自定义连接器**,Claude(Web 应用或**移动端**)就可以通过 `upload_html` 工具保存其生成的 HTML。无需单独的 MCP 进程——服务器本身提供 `/mcp/` 服务。
- **传输方式**:Streamable HTTP / 无状态(JSON 响应,无额外依赖)
- **工具**:`upload_html`(写入)/ `list_snippets`(读取)
- **验证**:无验证 + 密钥路径。只要未设置 `MCP_SECRET_PATH`,`/mcp` 就会返回 404。
设置:
1. **使服务器可通过 HTTPS 公开访问**(必需)。claude.ai 从 Anthropic 的云进行连接,因此 `localhost` / LAN / 仅限 VPN 的服务器将无法工作。请将其置于反向代理 + 域名 + TLS 之后,或使用 Cloudflare Tunnel(请参阅 [deploy/](deploy))。
2. 生成一个密钥字符串,在 `.env` 中进行设置,然后重启:
openssl rand -hex 24 # 将输出设置为 MCP_SECRET_PATH
# .env: MCP_SECRET_PATH=
3. 在 claude.ai → Customize > Connectors → **Add custom connector** 中,粘贴 URL:
https:///mcp/
无需填写 OAuth 字段(无需验证)。在 Web/桌面端注册会同步到移动应用。
4. 让 Claude “将此 HTML 保存到保险库”。将 `upload_html` 设置为“始终允许”,使其近乎自动化。
快速检查(本地):
```
curl -s -X POST http://localhost:3000/mcp/ \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```
## 备份
所有数据都在 `data/` 下。将其归档:
```
tar czf html-vault-backup-$(date +%F).tar.gz data/
```
## 贡献 / 许可证
[CONTRIBUTING.md](CONTRIBUTING.md) ([日本語](CONTRIBUTING.ja.md)) · [MIT](LICENSE)
标签:Docker, HTML预览, MITM代理, 代码片段管理, 安全防御评估, 自定义脚本, 自托管, 请求拦截