ADR-0031: サブドメインに分ける規則を決め、1 つのアプリを 1 つの Worker とホスト名に割り当てる¶
Status¶
承認
Context¶
fukuemon.dev のドメインの下に、サイトのほかに、複数のアプリを置く。
どれを fukuemon.dev に置き、どれをサブドメインに分けるかを決める必要がある。
- 配信は Cloudflare Workers Static Assets で行う。custom domain は Terraform が、deploy は Wrangler が管理する。
- カタログは、
design.fukuemon.devに置いている。 - 訪問者のアカウントを持たないので、サイトが持つ訪問者のデータは、訪問者のブラウザに保存したデータだけである。
- ADR-0008: 読者の identity を前提とする機能を作らず、バックエンドを持たない
- プレイグラウンドは、訪問者がブラウザの中で使う複数のツール(コードと SQL の実行環境、キャンバス、スライドのエディタ)を提供する。最初のツールは、コードと SQL の実行環境である。キャンバスとスライドのエディタを、後から追加する。訪問者が作ったファイル、図、スライドは、訪問者のブラウザに保存する。
- 出典: PRD
- 実行環境の方式は、実行環境の要求の prototype で決める。候補の WebContainers は、cross-origin isolation(
COOP: same-originとCOEP: require-corp)を求める。 - 訪問者のコードが、実行環境のページと同じ origin で動くかは、実行環境の方式で変わる。
- 実行環境の要求は、訪問者が作ったファイルを URL で共有することを、範囲の外に置いている。
- cross-origin isolation は、サイト全体に適用せず、
/playground/*だけに適用すると決めている。 COEP: require-corpのページでは、CORP を返さないクロスオリジンのリソースがブロックされる。- isolation のヘッダは、ページのレスポンスごとに返す。同じ origin の中で、返す URL と返さない URL を分けられる。
- キャンバスの候補の draw.io の Embed Mode は、draw.io のエディタを、別の origin の iframe で読み込む。そのレスポンスは、COEP と CORP のヘッダを返さない(
curl -Iで確認した)。 - isolation が要る実行環境を、別の origin の iframe で記事に埋め込むときは、iframe の側がヘッダを返しても、親の記事のページがヘッダを返さなければ、iframe の中は isolation の状態にならない。実行環境の要求の prototype で確認した。
- サイトを作り直すあいだ、本番は切り替える前のサイトを配信し続ける。開発中のサイトを、実機と E2E で確認する URL が要る。
Decision¶
1. サブドメインに分ける規則¶
次のどれかに該当するものを、サブドメインに分ける。該当しないものは、fukuemon.dev に置く。
- 訪問者が書いたコードを動かす。origin を分けて、サイトが保存したデータにアクセスできないようにする。
- サイトと違うレスポンスヘッダか、アクセスの認証が要る。
- サイトと別に作って別にデプロイする、独立したアプリである。
1 つのアプリは、1 つの Worker と 1 つのホスト名に割り当てる。
1 つの Worker に、パスで複数のアプリを配置しない。アプリごとに、デプロイのタイミングとヘッダを分けるためである。
2. 割り当て¶
| ホスト名 | 置くもの | 該当する規則 |
|---|---|---|
fukuemon.dev |
トップ、記事、シリーズ、プレイグラウンドの一覧のページ | 無い |
| プレイグラウンドのサブドメイン | プレイグラウンドの全てのツール | 1、2、3 |
design.fukuemon.dev |
カタログ | 3 |
docs.fukuemon.dev |
ドキュメントサイト | 2 と 3 |
- プレイグラウンドのサブドメインの名前は、実行環境の方式を決める ADR が決める。
- ゲームのような独立したアプリは、追加するときに、規則 3 でサブドメインを 1 つ割り当てる。
3. プレイグラウンドのツールの配置先¶
- プレイグラウンドは、1 つのアプリとして扱う。全てのツールを、1 つのサブドメインの、1 つの Worker に置く。訪問者から見て 1 つの場所にし、ツールの間で、訪問者が保存したデータを使えるようにするためである。
- ツールごとに、パスとページを分ける。1 つのページに、複数のツールを置かない。
- cross-origin isolation のヘッダは、isolation を求めるツールのパスにだけ返す。サブドメインの全体には返さない。別の origin のページを iframe で読み込むツールを、同じサブドメインに置くためである。
- この Worker は、パスごとにヘッダを書き分ける。規則の「アプリごとにヘッダを分ける」の例外になる。
- どのパスにヘッダを返すかは、実行環境の方式を決める ADR が決める。
4. プレイグラウンドに保存したデータ¶
- プレイグラウンドのコードは、fukuemon.dev が保存したデータにアクセスできない。origin が違うためである。
- プレイグラウンドの中では、ツールの間で origin を分けない。訪問者が動かしたコードが、同じ origin に保存したファイル、図、スライドにアクセスできることを受け入れる。理由は次の 3 つ。
- データは、訪問者の端末にだけある。訪問者のアカウントも、サーバーに置くデータも無い。
- 動くコードは、訪問者が書いたコードか、訪問者が自分の操作で動かしたコードである。
- 作ったファイルを URL で共有しないので、他人が用意したコードが、訪問者の操作なしに動くことが無い。
- 次の 3 つを条件にする。
- プレイグラウンドの origin に、シークレット(トークン、認証情報)を保存しない。
- 他人が用意したコードを、訪問者の操作なしに動かさない。運営者が記事とプレイグラウンドに用意した初期ファイルは、この条件の対象にしない。
- URL での共有のように、他人が用意したコードを読み込む機能を追加するときは、この決定を決め直す。
5. cross-origin isolation¶
fukuemon.dev の全体には、cross-origin isolation のヘッダを返さない。
サイト全体に適用すると、CORP を返さない外部のリソースが、全てのページでブロックされるためである。
例外は、isolation を求める実行環境を埋め込む記事の URL である。
実行環境を iframe で埋め込むときは、親の記事のページもヘッダを返さないと、iframe の中が isolation の状態にならないためである。
どの記事の URL に返すかは、実行環境の方式を決める ADR が決める。
6. 切り替えまでのプレビュー¶
- 新しいサイトを、本番と別の名前の Worker(
fukuemon-dev-preview)にデプロイする。custom domain を付けず、workers.dev の URL で開く。 - develop への push で check が通ったときにデプロイする。
- 誰でも開けるままにし、全てのレスポンスに
X-Robots-Tag: noindexを返す。E2E と Lighthouse を、認証なしで実行するためである。 - 本番の Worker(
fukuemon-dev)への deploy は、運用者が手動で実行したときだけ行う。- 通常の deploy workflow が push で動くとした ADR-0005 の 5 の、切り替えまでの例外である。例外は本番の Worker だけで、credential の 2 系統は変えない。
- 本番を新しいサイトに切り替えるときに、プレビューの Worker を消し、本番の Worker へ develop への push でデプロイする設定に変更する。
承認の条件¶
実装の中で、次の 3 つを確認する。1 つ目と 3 つ目は実行環境の要求の prototype で、2 つ目はキャンバスの要求の prototype で確認する。
- isolation のヘッダを返すパスのページと、返さないパスのページが、同じ origin で両方動くこと。
- キャンバスの候補が、isolation のヘッダを返さないパスのページで動くこと。
- 実行環境の方式が、訪問者のコードを、プレイグラウンドのページと同じ origin で動かすか。同じ origin で動かす方式を採るときは、Decision 4 の受け入れを、その方式の ADR でもう一度判断する。
Considered Options¶
| 選択肢 | 強み | 弱み |
|---|---|---|
| 採用: プレイグラウンドの全てのツールを 1 つのサブドメインに置き、isolation のヘッダを、求めるツールのパスにだけ返す | 訪問者から見て、プレイグラウンドが 1 つの場所になる。ツールの間で、保存したデータを使える。訪問者のコードが、サイトが保存したデータにアクセスできない。Worker が 1 つで済む | ヘッダを、パスごとに書き分ける。訪問者が動かしたコードが、ほかのツールの保存したデータにアクセスできることがある。isolation を求めるツールと、別の origin のページを iframe で読み込むツールを、同じ文書に並べられない |
| コードを動かすツールだけをサブドメインに置き、動かさないツールを fukuemon.dev に置く | 訪問者のコードが、図とスライドにアクセスできない。ヘッダを書き分けない | プレイグラウンドが 2 つの origin に分かれる。ツールの間で、保存したデータを使えない |
| ツールごとに、サブドメインを 1 つ割り当てる | ツールごとに、ヘッダとデプロイのタイミングを分けられる。保存したデータが、ツールごとに分かれる | Worker とホスト名が、ツールの数だけ増える。ツールの間で、保存したデータを使えない |
| プレイグラウンドのサブドメインの全体に、isolation のヘッダを返す | ヘッダを書き分けない | COEP を返さない別の origin のページを iframe で読み込むツールを、置けない |
| サイトの全てを fukuemon.dev の下のパスに置く | Worker が少ない。同じ origin なので、埋め込みが単純になる | 訪問者のコードが、サイトと同じ origin で動く。isolation のヘッダを、サイトのパスごとに書き分ける |
| ツールの配置先を決めず、実行環境の方式と一緒に決める | 方式が決まる前に、origin を固定しない | キャンバスとスライドのエディタの要求を、配置先が決まらないまま書くことになる |
| プレビューを、本番の Worker のバージョンごとの URL で公開する | Worker が増えない | 上げるたびに URL が変わる。本番の Worker への操作なので、コマンドを誤ると本番が替わる |
| プレビューを用意せず、手元だけで確認する | 基盤が増えない | 実機で確認できない。E2E と Lighthouse を、配信した状態で実行できない |
Consequences¶
Positive¶
- 新しいアプリを追加するときに、配置先を規則で決められる。
- プレイグラウンドにツールを追加するときに、Worker とホスト名が増えない。
- 開発中のサイトを、本番の Worker に触れずに、変わらない URL で確認できる。
Negative¶
- Worker が、サイト、プレイグラウンド、カタログ、ドキュメントサイト、プレビューの 5 つになる。
- プレイグラウンドの Worker は、パスごとに違うヘッダを返す。ヘッダの設定を誤ると、isolation を求めるツールが起動しないか、iframe を読み込むツールが動かない。
- isolation を求めるツールと、別の origin のページを iframe で読み込むツールは、同じ画面に並べられない。ツールを移るときは、ページを読み込み直す。
- isolation を求める実行環境を埋め込む記事では、CORP を返さない外部のリソースがブロックされる。
- 訪問者が、他人の書いたコードを貼って動かすと、そのコードが、同じ端末のプレイグラウンドに保存した図、スライド、ファイルを読むか、消すことがある。
- プレビューの Worker は custom domain を持たないので、Worker の名前を Terraform の変数と比べるチェックの対象の外になる。
- プレビューの URL を知っている人は、開発中のサイトを開ける。