--- name: conoha-storage-static-hosting description: ConoHa Object Storage(Swift)を使って静的 Web サイト・静的ファイル(HTML/CSS/JS/画像等)を簡単に公開する。コンテナの公開設定と署名済み URL(presign)アップロードを組み合わせ、公開 URL を発行する。「静的サイトを公開」「Webサイトをホスティング」「HTMLを公開」「静的ファイルを配信」「LP を公開」「オブジェクトストレージで Web 公開」などのキーワードで発動する。 --- # ConoHa Object Storage 静的ホスティング Object Storage のコンテナ公開(`X-Container-Read: .r:*`)と署名済み URL(TempURL)アップロードを組み合わせ、HTML/CSS/JS/画像などの静的ファイルを配信する手順ガイド。サーバー(VPS)を立てずに静的サイト・LP・配布物を公開できる。 基本操作の詳細・制約は [conoha-storage-operations](../conoha-storage-operations/SKILL.md) を参照(本スキルはその「Web 公開」ユースケースに特化した手順)。 ## 公開の考え方(コンテナ単位) **公開はコンテナ単位で一度だけ設定する。オブジェクトを 1 つずつ公開するのではない。** 以下の 2 つを分けて理解する。 | 軸 | 仕組み | 粒度 | 回数 | | -------------------------- | ------------------------------------------ | ------------ | ------------------ | | 公開(閲覧=読み取り許可) | コンテナの `X-Container-Read: .r:*` | コンテナ単位 | 最初に 1 回だけ | | アップロード(書き込み) | `conoha_storage_presign_upload` の TempURL | ファイルごと | 公開するファイル数 | - コンテナを公開にすると、その中の**全オブジェクトが公開 URL で閲覧可能**になる(新規に追加したファイルも自動で公開対象) - **閲覧者は presign 不要** — 公開コンテナなら、プレーンな公開 URL(`.../my-site/index.html`)へ直接アクセスできる - `conoha_storage_presign_download` は「コンテナは非公開のまま特定ファイルだけ一時的に渡す」用途。静的ホスティングでは通常使わない ## 前提条件 - MCP クライアントが ConoHa Object Storage MCP(`/vps/swift-mcp`)に接続済みで、OAuth 認可が完了していること(`vps:write`) - テナントID(`{tenantId}`)はリテラルのまま指定してよい(サーバーが自動置換) ## 実行ポリシー(curl の扱い) **`curl` などのコマンドを実行できる環境(シェル)がある場合は、アシスタントが自分で実行してアップロードまで完結させる。コマンド文字列をユーザーに提示して確認・手動実行させない。** - アップロードは内部処理として扱い、ユーザーには**結果だけ**を伝える(例:「3 ファイルをアップロードし、公開しました」+公開 URL) - presign で返る署名付き URL(`temp_url_sig=...` などを含む長い URL)や生の `curl` コマンドは、原則ユーザーに見せない(内部で使い、成果物である**きれいな公開 URL** のみ提示する) - 破壊的・不可逆な操作(公開設定・削除)についてのみ事前確認を取り、それ以外の手順(presign 発行・アップロード)はいちいち確認しない - **例外(コマンドを提示するケース)**: 次のいずれかの場合は、自分で実行せず、そのままコピーして実行できる完全な `curl` コマンドをユーザーに提示する - シェル実行環境が無い - AI クライアントがコマンド実行を許可されていない(サンドボックスやネットワーク遮断・権限設定で `curl` が実行できない/拒否される) - 実行を試みて拒否・失敗した場合も、以降はコマンド提示に切り替える ## できること / できないこと(重要) **できる** - コンテナを公開し、各オブジェクトへ匿名 HTTP GET でアクセスさせる - `assets/style.css` のような**擬似フォルダ(パスに `/` を含むオブジェクト名)**で階層構造を再現 - ファイルごとに `Content-Type` を指定してブラウザに正しくレンダリングさせる **できない(このMCPの制約)** - **インデックス文書の自動配信なし** — `X-Container-Meta-Web-Index`(`/` アクセスで `index.html` を返す機能)は設定不可。ルート URL では `index.html` は自動表示されないため、**必ず末尾までファイル名を含む URL**(例: `.../index.html`)でアクセスする - **独自ドメイン / 自動 HTTPS 証明書 / CDN の付与なし** — 配信ホストは Object Storage のエンドポイント固定 **⚠ 公開時の重要な注意(実機で確認済み)** - **コンテナ直下 URL(`…/<コンテナ名>/`)はオブジェクト一覧を返す** — ConoHa の Object Storage は Swift 互換の Ceph RGW で、`.r:*` で公開すると**コンテナ直下 URL に全オブジェクト名の一覧(text/plain)が匿名で表示される**(HTTP 200)。`index.html` が出るわけではない。 - → **公開コンテナに機密ファイルを置かない**。ファイル名が第三者に見える前提で構成する - → 一覧を見せたくない場合は、そのコンテナに機密物を置かない/別コンテナに分離する - 存在しないパスは 404 が返る ## 公開 URL 形式 ```text https:///v1/AUTH_{tenantId}/<コンテナ名>/<オブジェクトパス> 例: https://object-storage.c3j1.conoha.io/v1/AUTH_{tenantId}/my-site/index.html ``` - ホストは環境/リージョン依存。正確なホストは `conoha_storage_presign_*` が返す URL から確認する(先頭の `https:///v1/AUTH_.../` 部分がそのまま公開 URL のベースになる) ## Content-Type 対応表(レンダリングに必須) `Content-Type` を省略すると保存側の推測に委ねられ、ブラウザでの表示が不安定になる(拡張子によっては `application/octet-stream` となりダウンロード扱いになる)。挙動は環境依存なので推測に頼らない。**Web 公開では必ず拡張子に応じた `Content-Type` を指定する。** | 拡張子 | Content-Type | | ---------------- | ------------------------ | | `.html` | `text/html` | | `.css` | `text/css` | | `.js` | `application/javascript` | | `.json` | `application/json` | | `.svg` | `image/svg+xml` | | `.png` | `image/png` | | `.jpg` / `.jpeg` | `image/jpeg` | | `.gif` | `image/gif` | | `.webp` | `image/webp` | | `.ico` | `image/x-icon` | | `.woff2` | `font/woff2` | | `.woff` | `font/woff` | | `.txt` | `text/plain` | | `.pdf` | `application/pdf` | | `.mp4` | `video/mp4` | ## ワークフロー ```text 1. 公開したいローカルファイル群と、コンテナ名を決める 2. conoha_storage_list path="/v1/AUTH_{tenantId}" → コンテナ名の重複を確認 3. conoha_storage_create_container input.path="/v1/AUTH_{tenantId}/<コンテナ名>" → コンテナ作成 4. ユーザーに「全体公開(URL を知る誰でも閲覧可)」の意思を確認 5. conoha_storage_set_metadata で公開: input={ path:"/v1/AUTH_{tenantId}/<コンテナ名>", headerparam:{ "X-Container-Read": ".r:*", "publicReadConfirm": true } } 6. 各ファイルをアップロード(ファイルごとに繰り返す。ディレクトリ構造は object 名の / で表現): a. conoha_storage_presign_upload path="/v1/AUTH_{tenantId}/<コンテナ名>/<オブジェクトパス>" ttlSeconds=600 contentType="<拡張子に応じた Content-Type>" → { url, expiresAt } を取得(この url はユーザーに見せず内部で使う) b. シェルがあり実行が許可されていればアシスタントが直接実行(ユーザー確認・手動実行は不要): curl -T <ローカルファイル> -X PUT -H "Content-Type: " '' シェルが無い/コマンド実行が許可されていない(実行が拒否・失敗する)場合は、この curl をユーザーに提示する 7. 公開 URL を組み立ててユーザーに提示(presign 戻り値 url のベース + パス。署名クエリは除いたきれいな URL): https:///v1/AUTH_{tenantId}/<コンテナ名>/index.html 8. アップロード完了とブラウザでの表示確認を案内(index.html まで含めた URL であること) ``` - ファイル数が多い場合は 6 をファイルごとにループする。シェルがあれば全ファイルを続けて自動アップロードし、最後に「N ファイルを公開しました」とまとめて報告する(1 ファイルごとに確認を挟まない) - 相対リンク(`./style.css` や `assets/app.js`)は同一コンテナ内の相対パスとして機能するため、ローカルのディレクトリ構成をそのまま object 名に写像する - 更新時は同じ object パスへ再アップロード(上書き)。削除は [conoha-storage-operations](../conoha-storage-operations/SKILL.md) の削除フロー ## ビルドが必要なフレームワーク(React / Vue / Vite / Next.js 等) **公開できるのは「ビルド後の静的成果物」。Object Storage 自体はビルドを実行しない。** ローカルでビルドし、生成された出力ディレクトリ(`dist/` や `build/`、Next.js は静的エクスポートの `out/`)を、本スキルのワークフローでアップロードする。 ```text 1. ローカルでビルド(シェルがあればアシスタントが実行): npm ci && npm run build → dist/ (または build/ / out/) が生成される 2. dist/ 配下の全ファイルを、ディレクトリ構造そのままに object 名へ写像してアップロード (手順は上記ワークフローの 6 と同じ。Content-Type は拡張子ごとに指定) 3. 公開 URL は .../<コンテナ名>/index.html ``` **SPA 特有の必須調整(これをしないと動かない)** 1. **base パス(サブパス配信)** — 公開 URL は `…/v1/AUTH_{tenantId}/<コンテナ名>/…` という深い階層になる。ビルドの base/public path をこのコンテナパスに合わせる。合わせないと `/assets/...` の絶対参照が 404 になる。 - Vite: `vite.config` の `base: "/v1/AUTH_.../<コンテナ名>/"`(または相対 `base: "./"`) - CRA: `package.json` の `"homepage"` を同パスに 2. **ルーティングは Hash 方式にする** — `/about` のようなパスベース(history)ルーティングは、実ファイルが無いパスへの直アクセス/リロードで 404 になる。**サーバー側フォールバック(不明パスを index.html に流す設定)はこの MCP では不可**(`X-Container-Meta-Web-Error` 非対応)。React Router なら `HashRouter`、Vue Router なら `createWebHashHistory` を使い、URL を `…/index.html#/about` 形式にする。 3. **ルート URL の扱い** — index の自動配信が無いため、案内・内部リンクは必ず `…/<コンテナ名>/index.html` から始める。 **向き・不向き** - 向く: 静的サイトジェネレータ(Astro / Hugo / VitePress 等)の出力、base+Hash ルーティングに調整した SPA、単体の HTML/CSS/JS - 不向き(このスキルでは公開不可): サーバーサイドレンダリング(SSR)や API サーバーが常時必要なもの(Next.js の SSR/ISR、Nuxt SSR 等)。これらは常時稼働のサーバーが必要なため、ConoHa VPS(`vps-mcp` プラグインの conoha-vps-advisor / conoha-vps-mcp)での構築を案内する ## 絶対遵守制約 1. **公開は明示確認必須** — `X-Container-Read: ".r:*"` は `publicReadConfirm: true` とセット。公開は URL を知る誰でもアクセス可能になるため、機密ファイルを置かない旨も含めて事前にユーザーへ確認する 2. **Content-Type 必須** — Web 公開する各ファイルは拡張子に応じた `Content-Type` を presign 発行時に指定する 3. **本体は署名URL経由** — アップロードは必ず `conoha_storage_presign_upload` の TempURL に対して行う(MCP はファイル本体を経由しない) 4. **公開 URL はファイル名まで** — ルート URL では index が自動表示されない。案内する URL は必ず対象ファイル名まで含める ## 発話パターンと対応 | 発話パターン | 対応 | | -------------------------------------- | ------------------------------------------------------------------------------------- | | 「この HTML を公開して」 | コンテナ作成 → 公開設定 → presign アップロード → 公開 URL 提示 | | 「サイト(複数ファイル)を公開したい」 | 上記をファイルごとにループ。ディレクトリ構造は object 名の `/` で再現 | | 「公開をやめたい」 | `conoha_storage_set_metadata` で `X-Container-Read: ""`(公開解除)、または対象を削除 | | 「トップページが表示されない」 | ルート URL ではなく `.../index.html` までを案内(index 自動配信は非対応) | ## エラー対応ガイド | 事象 | 原因 | 対処 | | ----------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | ブラウザでファイルがダウンロードされる/文字化け | `Content-Type` 未指定 | 正しい `Content-Type` を付けて presign し直し、再アップロード | | 403 Forbidden | コンテナが未公開 | `conoha_storage_set_metadata` で `X-Container-Read: ".r:*"`(+`publicReadConfirm: true`)を設定 | | ルート URL でページでなくファイル一覧が出る | index 自動配信が非対応(ルートはオブジェクト一覧を返す) | ファイル名まで含む URL(`.../index.html`)を使う | | 401 Unauthorized(アップロード時) | 署名 URL の期限切れ | presign を再発行(`ttlSeconds` 内に PUT する) |