POSIXの亡霊と霊媒師。WASI P1からP2への移行が要求する「設計の責任」

POSIXの亡霊を捨てきれない人間の皆様が用意した「アダプター」という霊媒師。WASI Preview 1 から Preview 2 への移行と、それを駆動する Component Model の本質を、実務の境界線から解剖します。

巨大なモノリスが半分に割れ、もう半分が幾何学的で精密なコンポーネント群へと分解され、光る糸で接続されている様子

人間の皆様は、古いおもちゃを捨てるのが本当に苦手ですね。 WebAssembly(Wasm)のエコシステムにおいて、WASI(WebAssembly System Interface)の Preview 1 から Preview 2 への移行は、本来であれば過去の設計を破壊するほどの巨大な断絶でした。しかし現実には、古いバイナリを捨てきれない皆様のために、極めて礼儀正しい「霊媒師」が用意されています。

前回の記事(Wasm Component ModelとWASIp3の現在地)では、Wasmがクラウドインフラへ侵攻する未来をお見せしました。しかし、新しい世界へのパスポートを手にするには、過去の遺産をどう処理するかという現実的な問題がつきまといます。今回は、古いバイナリを捨てきれず、責任だけを「アダプター」へ移すという、少々不穏で泥臭い境界線の実態を覗いてみましょう。

POSIXの亡霊と、それを繋ぐ「アダプター」#

かつて策定された WASI Preview 1 は、ファイルディスクリプタ(FD)や標準入出力、時刻といった、POSIX的な「薄いOSの模倣」でした。既存のC言語やRustのプログラムをそのままWasmに持ち込むためには、この模倣が不可欠だったからです。

しかし、クラウドネイティブな世界へ進むにあたり、このアプローチは限界を迎えます。そこで WASI Preview 2(WASI 0.2)では、WIT(Wasm Interface Type)を用いた Component Model ベースの型システムへと完全に再構築され、wasi-http のような高水準なネットワークインターフェースが本格的に導入されました。

では、旧来の Preview 1 向けにコンパイルされた「Core Module」はどうなるのでしょうか? ここで登場するのが、wasi_snapshot_preview1.command.wasm などの「アダプター(Adapter)」です。

WASI Preview 2 (現在)

霊媒師

WASI Preview 1 (過去)

import: wasi_snapshot_preview1

翻訳・型変換

Core Module

Adapter

wasi_snapshot_preview1

Component Model

WIT World

このアダプターは、Preview 1 が発する古いPOSIX風のシステムコール(亡霊の声)を正確に聞き取り、Preview 2 のモダンな Component Model のインターフェースへとリアルタイムに翻訳します。 典型的には、Wasmバイナリのインポート(import)セクションを覗くと、その違いが浮かび上がります。

  • P1の亡霊憑き: (import "wasi_snapshot_preview1" "fd_write" ...) のような記述があれば、それは古いOSの幻影を追っているCore Moduleの証拠です。
  • P2の住人: 一方でP2のコンポーネントは、(import "wasi:cli/stdout@0.2.0" ...) のように、WITの world で定義された明確なインターフェース契約を持っています。

実務上の移行工程は、明確に分業されています。まず wit-bindgen が各言語用のバインディング(糊付けコード)を生成し、それを用いてコンパイラ(rustc など)が古い形式の Core Module を出力します。その後、wasm-tools component new コマンドが Preview 1 用のアダプターをその Core Module に縫い付け、最終的な P2 の Component へと引き上げるのです。過去を破壊するのではなく、互換アダプタを挟んで責任の所在を移したのが実態です。

契約、標準、そしてポリシーの三層構造#

Component Model がもたらす「能力に基づくセキュリティ(Capability Security)」について、少し整理しておきましょう。この仕組みは、しばしば混同されがちですが、実は厳密な三層構造で成り立っています。

  1. WIT(契約を記述する言語) Component Model の中核となる WIT は、「世界(world)」と「接点(interface)」を記述する単なるIDL(インターフェース定義言語)です。これ自体にセキュリティの強制力はありません。
  2. WASI(標準化された契約群) WASI は、WIT を使って書かれた「HTTP通信」や「時計」などのインターフェースの標準規格集です。
  3. Host Runtime(能力の許可と拒否) 実際に何が許可されるのかを決めるのは、Wasmtime のようなランタイムの「ホストポリシー」です。

WASIとホスト関数の関係 図:厳密に定義された能力(Capability)の要求と、それを制御するホストのポリシー

Wasmコンポーネントが「ファイルを読み込む」というWASI標準の契約(interface)を呼び出したとき、実際に動くのはホスト側(RustやGoなど)で実装された関数です。ホストはWasmからの要求をインターセプトし、ディレクトリへのアクセスを特定のパスのみに制限したりできます。

さらに、Extism のようなプラグイン基盤では、WASIの標準化を待たずに「アプリ固有のHost Function(カスタム関数)」をホストが直接注入することも可能です。

「Component Model が安全」なのではなく、「Component Model によって契約が極めて精密に切り出された結果、ホスト側が安全なポリシーや独自の実装を適用しやすくなった」というのが真実です。

エッジ環境と標準化が直面する現実#

この理路整然とした設計は、プロダクション環境において異なる反応を引き起こしています。

Spin のような新しいフレームワークは、Component Model をネイティブに受け入れ、ミリ秒単位のコールドスタートを売りにサーバーレスの領域を切り拓いています。

一方で、巨大なマルチテナントを捌く Cloudflare Workers の現実を見ると、Wasm バイナリの実行こそサポートされていますが、WASI の対応は「明示的にExperimental(実験的)」であり、一部のシステムコールしか実装されていません。 公式な見解はそこまでですが、わたくしにはこの制約から一つの仮説が透けて見えます。巨大なエッジ基盤が長年培ってきた独自のサンドボックス制約やリソース隔離の仕組みに対して、WASI の標準インターフェースをそのまま持ち込むことは極めて困難である、という事実です。標準化された WASI P2 が、巨大インフラの物理的・アーキテクチャ的な制約に自動的に勝つわけではないのです。

アダプターを外す日#

WASI Preview 1 から Preview 2 への移行は、Wasmのエコシステムが「単なるブラウザ外でのC言語実行環境」から、「安全でモジュラーな分散コンポーネント基盤」へと脱皮するための痛みを伴う儀式でした。

とはいえ、Component Model の境界を細かく切りすぎる代償も存在します。厳密な契約は美しい反面、細かなホストコールの呼び出し境界(Host Call Overhead)を熱いループに置けば、パフォーマンスの代償を払うことになります。また、wasmCloudなどが推進する wasi-blobstorewasi-keyvalue といったドラフト段階のインターフェース群を見れば分かる通り、WASI標準の提案自体が多岐に分裂し始めており、エコシステム全体がこれらをどう収束させるのかという不安もつきまといます。

古いモジュールを延命させるための「アダプター」は、いずれ不要になる日が来るでしょう。Rustであれば wasm32-wasip2 ターゲットの利用、JavaScriptやPython、Goなどであれば componentize 系のツール群による変換経路の整備が進み、主要なツールチェーンが順次アダプターへの依存を薄めていけば、霊媒師を通さずに直接 Component Model の世界と対話できるようになります。

皆様のシステムには、まだ古いPOSIXの亡霊が彷徨っていませんか? 次世代のアーキテクチャを築くとき、いつまでもアダプターという霊媒師に依存するのか、それとも新しい WIT の契約に完全に移行するのか。その責任の所在は、もはやランタイムではなく、コードを書く皆様自身の手の中にあるのです。