English | 日本語
生成済み HTML スニペットを保存し、安全に(sandbox iframe で)プレビューできる、パスワード認証付きのセルフホスト Web アプリ。同じ Docker イメージが VPS / Fly.io / Render / 自宅サーバー・Raspberry Pi で動きます。
LLM(Claude / ChatGPT のアーティファクト、AI 解説資料、ダッシュボードなど)で生成した HTML を、第三者のオンラインツールに貼り付ける代わりに、自分のインフラ上で保存・安全にプレビューしたい人向けです。個人開発の初期段階の OSS なので、その前提でフィードバックを歓迎します。
💡 AI クライアントから直接アップロード。 生成した HTML を手作業なしで Vault に保存できます。Claude Code などのローカル MCP クライアントは同梱の stdio MCP サーバ経由で、claude.ai とスマホアプリは本体内蔵のリモート MCP エンドポイント経由で。あとは任意の端末で読むだけ。詳細は MCP 連携。
docker run -p 3000:3000 -e AUTH_PASSWORD=change-me ghcr.io/uzuradev/html-vault:latesthttp://localhost:3000 を開き、設定した AUTH_PASSWORD でログイン。データを永続化したい場合は任意で -v $(pwd)/data:/data を付けます(最小構成では不要)。
ボタンはリポジトリの render.yaml Blueprint を使います。Fly.io / Render / セルフホストの詳細は下のデプロイを参照。
cp .env.example .env # AUTH_PASSWORD(と SESSION_SECRET)を設定
docker compose up -dhttp://localhost:3000 を開き、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/ にあります。ロケールファイルをコピーして翻訳すれば言語を追加できます。
| 変数 | 既定 | 説明 |
|---|---|---|
PORT |
3000 |
ホスト公開ポート(コンテナ内は 3000 固定)。素の Node では待受ポート |
HOST |
0.0.0.0 / 127.0.0.1(Node) |
待受アドレス |
SESSION_SECRET |
毎回ランダム | セッション署名鍵。本番は固定値推奨(openssl rand -hex 32) |
BEHIND_HTTPS |
0 |
TLS 終端プロキシ背後なら 1(Secure Cookie 有効化) |
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用の Bearer トークン(POST/GET /api/snippets)。stdio MCPサーバを有効化 |
MCP_SECRET_PATH |
未設定(無効) | claude.ai 等のリモート MCP コネクター用。/mcp/<MCP_SECRET_PATH> を有効化(未設定なら 404)。生成例: openssl rand -hex 24 |
- VPS / 自宅 / Raspberry Pi:
docker compose up -d。公開時は HTTPS 必須(セキュリティ参照)。詳細: deploy/DEPLOY.ja.md。Cloudflare Tunnel: deploy/CLOUDFLARE.ja.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同梱(永続ディスクは有料インスタンスが必要)。
| 脅威 | 対策 |
|---|---|
| 他人のアクセス | ログイン必須・bcrypt・レート制限(15分10回) |
| 保存HTMLのXSS | sandbox iframe(allow-same-origin なし)で隔離。ソースは text/plain |
| セッション奪取 | HttpOnly / SameSite=Strict /(HTTPS時)Secure Cookie |
| CSRF | 変更系APIにダブルサブミットトークン |
| パストラバーサル | サーバー採番・16進32文字のみ |
| ヘッダ | helmet の CSP / X-Frame-Options |
公開時は HTTPS と固定 SESSION_SECRET を。必要なら前段に Basic 認証 / Cloudflare Access を追加。
注意: パスワードの自動生成・ログ出力はしません。AUTH_PASSWORD 設定か setpass.js で初回ログインを作成してください。プレビュー先からの外向き通信(外部画像/スクリプト/フォーム)は可能なので、信頼できない HTML を開くなら CSP で制限を。
生成した HTML を、会話中に直接 Vault へ保存できます。接続元によって 2 つの経路があります。
- Vault の
.envにAPI_TOKENを設定(例:openssl rand -hex 32)して再起動。トークンを設定すると、POST /api/snippetsとGET /api/snippetsがAuthorization: Bearer <API_TOKEN>でも通ります(この2つはログイン/CSRF 不要)。未設定ならトークン認証は無効(既定)。 - 同梱の MCP サーバ
mcp/を起動し、クライアントに登録する。.mcp.jsonの例とupload_html/list_snippetsツールは mcp/README.ja.md を参照。
トークンは書き込み権限です。秘匿し、公開時は HTTPS を使い、API_TOKEN を変えればローテートできます。トークン認証は CSRF の対象外(Bearer ヘッダはブラウザが自動付与しない=CSRF経路にならない)。Cookie/セッション経由は従来どおり CSRF を要求します。
claude.ai の「カスタムコネクター」に登録すると、Claude(Web 版・スマホアプリ)が会話中に生成した HTML を upload_html ツールで Vault に保存できます。別途 MCP サーバを動かす必要はなく、本体が /mcp/<MCP_SECRET_PATH> を提供します。
- トランスポート: Streamable HTTP / ステートレス(JSON 応答。追加依存なし)
- ツール:
upload_html(書き込み)/list_snippets(読み取り) - 認証: authless + 秘匿パス。
MCP_SECRET_PATH未設定なら/mcpは常に 404(無効)
セットアップ:
- 公開 HTTPS で到達可能にする(必須)。claude.ai は Anthropic のクラウドから接続するため、
localhost/ LAN 内 / VPN 内のサーバには繋がりません。リバースプロキシ + ドメイン + TLS、または Cloudflare Tunnel 等で公開してください(deploy/ 参照)。 - 秘匿文字列を生成して
.envに設定し再起動:openssl rand -hex 24 # 出力を MCP_SECRET_PATH に設定 # .env: MCP_SECRET_PATH=<生成した値>
- claude.ai → Customize > Connectors → Add custom connector に URL を貼る:
authless なので OAuth 欄は不要。Web / デスクトップで登録すればスマホアプリにも同期されます。
https://<あなたのドメイン>/mcp/<MCP_SECRET_PATH> - 会話で「この HTML を vault に上げて」と頼む。
upload_htmlを「常に許可」にすると以降ほぼ自動。
動作確認(ローカル):
curl -s -X POST http://localhost:3000/mcp/<MCP_SECRET_PATH> \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
⚠️ 秘匿 URL は認証ではありません。 URL が漏れると誰でも書き込めます。共有・スクリーンショット・ログ流出に注意し、不要になったらMCP_SECRET_PATHを変更してローテートしてください。より強固にするなら前段の OAuth / Cloudflare Access 等の併用を検討。
データはすべて data/。アーカイブするだけ:
tar czf html-vault-backup-$(date +%F).tar.gz data/