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 项目——欢迎提供反馈和提问。 ![screenshot](https://static.pigsec.cn/wp-content/uploads/repos/cas/3d/3dee3e99ec75563099bead9a65cae8c9ea82daf32954cd189f2cf31d58e8cfc9.png) ## 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"`。 [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](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代理, 代码片段管理, 安全防御评估, 自定义脚本, 自托管, 请求拦截