プラグインサンドボックスの設定

このページ

サンドボックス化されたプラグインは、プラグイン宣言に加えてプラットフォームランナーが必要です。マーケットプレイスとレジストリのインストールは常にそのランナーを使用し、sandboxed: [] に記載されたプラグインも同様です。plugins: [] のネイティブプラグインは EmDash サーバープロセスで実行され、サンドボックスの分離は得られません。

ランナーはデプロイプラットフォームに依存します。Cloudflare Workers では、各プラグインは Worker Loader バインディングを通じて作成された Dynamic Worker として実行されます。Node.js では、サーバーはオープンソースの Workers ランタイムである workerd を子プロセスとして起動し、各プラグインをその中のサービスとして実行します。emdash() の sandboxRunner オプションがランナーを選択し、ホスト済みレジストリカタログを有効にします。これがなければ、sandboxed: [] のプラグインは読み込まれません。明示的に設定されたレジストリは引き続き閲覧できますが、サンドボックス化されたプラグインのインストールや更新は SANDBOX_NOT_AVAILABLE で失敗します。

次の表は、各ランナーが必要とするものと適用するものをまとめています。

Cloudflare WorkersNode.js
sandboxRunner@emdash-cms/cloudflare の sandbox()"@emdash-cms/sandbox-workerd/sandbox"
要件Workers 有料プラン、worker_loaders バインディング、Worker エントリポイントからの PluginBridge エクスポートworkerd パッケージ
データベースアクセスDB D1 バインディング(設定されたアダプターとは独立)設定されたデータベース
適用される制限CPU 時間、サブリクエスト、ウォール時間ウォール時間

Cloudflare Workers

Dynamic Workers は Workers 有料プランで利用可能です。*-cloudflare テンプレートには以下のエントリポイントエクスポートが含まれていますが、バインディングはコメントアウトされているため、スキャフォールディング中にサンドボックス化プラグインを有効にしない限り、新規プロジェクトは Workers 無料プランでデプロイされます。

  1. wrangler.jsonc で Worker Loader バインディングを有効にします。ランナーはこれを LOADER という名前で読み取り、このバインディングが存在する場合にのみ Cloudflare サンドボックスを選択します:

    {
    	"worker_loaders": [
    		{
    			"binding": "LOADER",
    		},
    	],
    }

    Wrangler 設定が名前付き環境を使用する場合、Astro ビルド中に CLOUDFLARE_ENV を設定してください。Cloudflare Vite プラグインと sandbox() が同じ環境を読み取ります。バインディングは継承されないため、サンドボックス化プラグインを実行する各名前付き環境に LOADER を追加してください。

  2. Worker エントリポイントから PluginBridge をエクスポートし、main をそのファイルに向けます。PluginBridge は、サンドボックス化されたプラグインがコンテンツ、メディア、ストレージ、メールにアクセスするためのエントリポイントです。ランナーはエントリモジュールのエクスポートでこれを検索します:

    import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
    
    export { PluginBridge };
    
    export default {
    	...handler,
    	scheduled: createScheduledHandler(),
    } satisfies ExportedHandler;
    {
    	"main": "./src/worker.ts",
    }
  3. emdash() インテグレーションでランナーを選択します:

    import { d1, r2, sandbox } from "@emdash-cms/cloudflare";
    
    emdash({
    	database: d1({ binding: "DB" }),
    	storage: r2({ binding: "MEDIA" }),
    	sandboxRunner: sandbox(),
    });

Node.js

  1. ピア依存関係である workerd と一緒にランナーをインストールします:

    npm install @emdash-cms/sandbox-workerd workerd

    workerd パッケージは、オプション依存関係を通じて現在のプラットフォーム用のバイナリ(x64 での Linux、macOS、Windows、arm64 での Linux と macOS)をインストールします。サーバーが実行されるプラットフォームで、オプション依存関係を有効にしてインストールしてください。マルチステージ Docker ビルドでは、ランタイムステージと同じプラットフォームのステージでインストールを実行してください。

  2. emdash() インテグレーションでランナーを選択します:

    import { sqlite } from "emdash/db";
    
    emdash({
    	database: sqlite({ url: "file:./data/emdash.db" }),
    	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
    });

ランナーは Miniflare をオプション依存関係として宣言します。パッケージマネージャーはデフォルトでこれをインストールします。NODE_ENV が development(astro dev が設定)の場合、ランナーはプラグインを Miniflare に渡し、Miniflare が独自の workerd プロセスを管理します。以下のクラッシュポリシーは適用されません。オプション依存関係が省略された場合、ランナーは代わりに workerd を使用します。astro preview は NODE_ENV を production に設定し、node ./dist/server/entry.mjs は未設定のままにします。両方とも workerd を使用します。

workerd プロセスの実行方法

EmDash は、サンドボックス化されたプラグインが読み込まれた後、サイトへの最初のリクエスト時の初期化中に workerd を起動し、プラグインサービスが応答するまで最大 10 秒待機します。管理画面からのプラグインのインストールや更新で再起動されます。workerd が stdout や stderr に書き込むすべての内容は、[emdash:workerd] プレフィックス付きでサーバーの出力に表示されます。

プラグインサービスは 127.0.0.1 でリッスンし、サーバーへのチャネルは Unix ドメインソケット(Windows では 127.0.0.1 TCP ポート)です。受信ポートを開く必要はありません。

子プロセスはサーバー環境から PATH、HOME、TMPDIR、TMP、TEMP、LANG、LC_ALL のみを受け取るため、サーバー環境のシークレットはサンドボックスに入りません。より多くの変数を渡すには、EMDASH_WORKERD_PASSTHROUGH_ENV をカンマ区切りの変数名リストに設定してください。

workerd が予期せず終了した場合、ランナーは [emdash:workerd] workerd exited with <reason> をログに記録し、次回の呼び出しで再起動します。遅延は 1 秒から始まり、30 秒まで倍増します。workerd が 60 秒以内に 5 回以上クラッシュすると、ランナーは再起動を停止し、[emdash:workerd] workerd crashed 5 times in 60 seconds, giving up をログに記録します。それ以降、すべてのサンドボックス化プラグインのフックとルートは Plugin sandbox unavailable for <plugin>: workerd crashed 5 times in 60 seconds and the runner stopped retrying; restart the server で失敗します。サーバーの再起動で workerd が再び起動し、管理画面からのプラグインのインストールや更新でも同様です。サーバーへの SIGTERM は workerd も一緒に終了します。

リソース制限

各ランナーはプラグイン呼び出しごとに同じ制限セットを適用します。制限は固定されており、emdash() インテグレーションにはそのオプションがありません。

制限値Cloudflare WorkersNode.js
CPU 時間50 msWorker Loader によって適用。プラグインは制限に達するとスローする適用されない
サブリクエスト10Worker Loader によって適用。プラグインは制限に達するとスローする適用されない
メモリ128 MBプラグインごとには適用されない。プラットフォームの isolate メモリ上限が適用される適用されない
ウォール時間30 秒ランナーによって適用ランナーによって適用

フックやルートがウォール時間制限を超えた場合、呼び出しは Plugin <id> exceeded wall-time limit of 30000ms during hook:<name>(または route:<name>)で失敗します。フックの場合、EmDash は EmDash: Sandboxed plugin <id> プレフィックスで失敗をログに記録し、そのプラグインの結果なしでリクエストを続行します。制限を超えたプラグインルートは、その呼び出し元に対して失敗します。

ランナーが利用できない場合

Cloudflare Workers では、sandbox() がビルド時に wrangler.jsonc をチェックします。LOADER という名前の worker_loaders バインディングがない場合、ランナーを未設定のままにし、次の警告をログに記録します:

[emdash] Sandboxed plugins are disabled because wrangler.jsonc has no LOADER Worker Loader binding. Worker Loader requires a Workers paid plan.

選択されたランナーはランタイムでも利用できない場合があります:Cloudflare Workers ではデプロイされた LOADER バインディングまたは PluginBridge エクスポートが欠落している場合、Node.js では workerd がインストールされていないかバイナリが実行されない場合です。EmDash はランナーがコロンの後に報告する原因とともに警告をログに記録します。以下の警告はバインディングが欠落している場合に Cloudflare Workers でログに記録されます:

EmDash: Plugin sandbox is configured but not available on this platform: the worker has no worker_loaders binding named LOADER. Sandboxed plugins will not be loaded.

sandboxed: [] のプラグインは読み込まれず、インストール済みのマーケットプレイスとレジストリプラグインは実行されず、管理画面からの新規インストールはエラーコード SANDBOX_NOT_AVAILABLE で失敗します。サイトの残りの部分には影響しません。

サンドボックス化プラグインをインプロセスで実行する

emdash() で sandbox: false を設定すると、sandboxed: [] のプラグインとインストール済みマーケットプレイスプラグインを、分離や制限なしにサーバープロセスで実行します。これはプラグインのバグとサンドボックスのバグを区別するデバッグオプションです。次の設定は Node.js サイトでサンドボックスを無効にします:

emdash({
	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
	sandbox: false,
});

Cloudflare Workers では、ランタイムは sandbox: false is not supported in Cloudflare Workers で起動を拒否します。

トラブルシューティング

各項目は、サーバーがログに記録するメッセージ、または管理画面が返すエラーコードを見出しとしています。

「[emdash] Sandboxed plugins are disabled because wrangler.jsonc has no LOADER Worker Loader binding」

Cloudflare アダプターは、ビルド時の Wrangler 設定に LOADER という名前の worker_loaders バインディングがないため、サンドボックスランナーを選択しませんでした。これは Workers 無料プランでの期待される設定です。Workers 有料プランでは、wrangler.jsonc でバインディングを有効にしてサイトを再ビルドしてください。

「Plugin sandbox is configured but not available on this platform」

コロンの後のテキストが原因を示します。Cloudflare Workers では、the worker has no worker_loaders binding named LOADER は wrangler.jsonc に LOADER という名前の worker_loaders バインディングが必要であることを意味し、the worker entrypoint does not export PluginBridge は main が指すファイルが PluginBridge をエクスポートする必要があることを意味します。バインディングのデプロイには Workers 有料プランが必要です。

Node.js では、workerd is missing or its binary does not run on this platform はランナーが workerd を実行できなかったことを意味します。チェックが欠落パッケージをダウンロードできないように、インストール済みバイナリを直接実行してください:

./node_modules/.bin/workerd --version

Windows では node_modules\\.bin\\workerd.cmd --version を実行してください。コマンドが失敗した場合、workerd が node_modules にないか、インストール済みバイナリがこのプラットフォームで実行されません。ターゲットプラットフォームでオプション依存関係を有効にして再インストールしてください。

「workerd failed to start within 10 seconds」

子プロセスは起動しましたが、プラグインサービスが 10 秒以内に応答しませんでした。このメッセージの前の [emdash:workerd] プレフィックス付きの行には、設定エラーや起動エラーを含む workerd 自体の出力が含まれています。ランナーは次回の呼び出しで再試行します。

「workerd crashed 5 times in 60 seconds, giving up」

ランナーは workerd の再起動を停止しました。このメッセージの前の [emdash:workerd] workerd exited with <reason> 行が各クラッシュの終了コードまたはシグナルを示します。原因を修正してからサーバーを再起動してください。

プラグインインストール時の SANDBOX_NOT_AVAILABLE

管理画面のインストールリクエストは、ランナーが欠落しているか利用できないため拒否されました。ランナーが設定されている場合、エラーメッセージは上記の起動警告と同じ原因で終わります。プラットフォーム用のランナーを設定するか、その原因を修正して再デプロイしてください。