Grandream

Grandream

公開: 8 min read

CodexとClaudeを同じSandboxに同居させる|launcherへ集約

CodexとClaudeを同じSandboxに同居させる|launcherへ集約
AIエージェントの開発をご検討中ですか? AIエージェント開発サービスを見る →

連載「Docker Sandboxesでコーディングエージェントを隔離する」第2回/検証日: 2026年8月23日/sbx 0.39.0

第1回では、共通Kit(.sbx/sampleapp/spec.yaml)を書き、Claude agent typeの永続Sandboxsampleappを1つ作ってnpm ciまで通しました。今回はそこへCodex CLIを足し、CodexとClaude Codeが同じprivate cloneで作業できる状態にします。

やることは2つです。KitでCodex CLIを入れて認証を通すこと、そして増えたコマンドをひとつのランチャーへ集約すること。エージェントごとにSandboxを分ける案も試しましたが、この構成を選びました(比較は第6回)。

agent typeはClaudeを選び、CodexはKitから足す

サンドボックス作成時に決まるagent typeは1つだけ。claudeで作れば公式templateのClaude Codeが入り、codexで作ればCodexが入る。両方入った状態は選べません。

それでも1つのSandboxに同居させられるのは、足りない側をKitのinstall処理で入れられるからです。土台はClaude agent type、そこへCodex CLIをnpmで追加しました。Claudeを土台にした理由は1つで、Claude DesktopのSSH連携がこのagent typeを要求します(第5回)。

第1回のKitへ、install処理を1つ足します。

setup:
  install:
    # (第1回の Node.js install はそのまま)
    - description: Install a pinned Codex CLI alongside Claude Code
      user: "0"
      command: |
        set -eu
        npm install --global @openai/codex@0.146.0
        test "$(codex --version)" = "codex-cli 0.146.0"

バージョンを固定し、末尾でcodex --versionを検証します。第1回のNode.js installと同じ形です。忘れると後で困るのは、Codex Appからremote接続するときにremote側のcodexコマンドが必要になる点で、Kitに入れていないと接続はできても何も動きません。

ネイティブモジュールをビルドするなら、同じinstall処理へbuild-essentialvimも入れます。サンドボックス内で完結する作業が増え、ホストへ出る回数が減ります。

        apt-get update
        env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
          build-essential ca-certificates curl vim xz-utils

Kitを変えても既存Sandboxへは自動反映されません。作り直しです。未pushの変更が無いことを確認してからsbx rmします(手順は第5回)。

後付けしたCodex CLIは、Sandbox内でdevice loginする

同居構成の代償は認証に出ます。

codex agent typeで作ったSandboxなら、Codexの認証はホスト側で管理され、サンドボックス内にトークン実体を置きません。ところがClaude agent typeへ後付けしたCodex CLIには、そのhost-side OAuthが適用されません。CLIとして使うなら、永続Sandbox内で一度device loginします。

sbx exec -it sampleapp codex login --device-auth

このとき、Codexのサブスクリプション認証情報はSandbox内へ保存されます。第3回・第4回で作るproxy-managed credential(実キーはホストに残り、サンドボックスにはsentinelだけ渡る形)とは分離の強度が違います。エージェント交代のしやすさと引き換えに受け入れているtrade-offで、サンドボックス内にagent credentialを置けない要件が出たらagent別Sandboxへ戻す、という整理にしました。

Claude Code側の状態確認はこれだけ。

sbx exec sampleapp claude auth status

なお、Codex AppからSSH接続するremote sessionはApp側の認証を使うので、このCLI loginとは別経路です。CLIを使わずDesktop Appからしか触らないなら、device loginは要りません。

開発サーバーは内側で0.0.0.0、外側でlocalhostに出す

--publishを効かせるには、サンドボックス内のbind先を合わせる必要があります。Next.jsの開発サーバーなら、microVM内部で0.0.0.0へbindします。

npm run dev -- --hostname 0.0.0.0

127.0.0.1へbindすると、それはサンドボックス自身のloopbackになり、ホストのport forwardingからは届きません。エージェントに「開発サーバーを起動して」と頼んで、ブラウザから見えないときはまずここを疑います。

ホスト側はhttp://localhost:3000の1本です。Sandboxが1つなので開発サーバーも1つで、どちらのエージェントが起動しても同じURLを見ます。ポートを2つ管理しなくて済むのは、同居構成の地味な利点でした。

第1回でpublishを127.0.0.1:3000:3000/tcp4と書いたのはこの節と対応しています。内はワイルドカードbind、外はloopbackだけtcp4の明示は、localhost::1へ解決されてIPv4でlistenしているアプリに届かない、という食い違いを避けるためです。

起動口をpackage.jsonに置くと、ホストのNode.jsが要る

Sandboxが1つでも、打つコマンドは増えます。作成・再開・shell・npm ci・lint・build・開発サーバー・両エージェントの認証確認。素のsbxだと1コマンドがこの長さです。

sbx exec sampleapp -- npm run dev -- --hostname 0.0.0.0

最初に考えたのはpackage.jsondev:sandboxのようなscriptを足す案でした。却下しました。そのscriptを叩くためにホストのNode.jsとnpmが必要になり、Node.jsをホストから追い出すという当初の目的と正面から衝突するからです。

Makefileも候補でしたが、今回のランチャーに必要だった処理を並べると、素直に書けるのはPOSIX shellでした。

  • 同じSandbox内でのCodex CLI/Claude Codeの起動
  • サンドボックスの作成・再利用・停止
  • 両エージェントの認証状態の確認
  • 対話的なcredential登録(第3回・第4回)
  • npm ci、lint、build、dev serverの振り分け
  • SSHセットアップ(第5回)

リポジトリへ足すのはscripts/sandboxの1本だけで、責務は3つにきれいに割れました。

置き場所

担当

package.json

アプリケーション内部のコマンド

scripts/sandbox

ホストとサンドボックスの境界・認証・ライフサイクル

.sbx/.../spec.yaml

microVMの環境・ネットワーク・credential注入

ホスト側の必須ツールは、shellとsbxだけになります。

ランチャーのインターフェースを先に決める

実装より先に、打ちたいコマンドの形を決めました。

./scripts/sandbox setup    # secret・Sandbox・npm ci・SSH・agent 認証
./scripts/sandbox codex    # 同じSandbox内のCodex CLI
./scripts/sandbox claude   # 同じSandbox内のClaude Code
./scripts/sandbox auth     # 両エージェントの認証状態を確認
./scripts/sandbox dev      # env をフィルタして 0.0.0.0:3000 で起動
./scripts/sandbox install  # npm ci
./scripts/sandbox lint
./scripts/sandbox build
./scripts/sandbox shell
./scripts/sandbox ports
./scripts/sandbox status
./scripts/sandbox stop     # microVM を停止。データは保持

エージェント名が要るのはcodexclaudeの2つだけ。Sandboxが1つなのでlintbuilddevも対象は自明です。Sandboxを分けていたときは全コマンドにcodexclaudeを付けて回していたので、ここは同居構成で素直に軽くなりました。

setupの中身は次の順です。

  1. sbxの存在と最低バージョンを確認する
  2. Dockerアカウントのloginとdaemonを確認する
  3. GitHub用fine-grained PATを登録する(第3回)
  4. アプリ用のAPIキーを登録する(第4回)
  5. ローカル設定ファイルの存在を確認する
  6. Kitをkit validateにかける
  7. Sandboxが無ければ作成し、あれば再利用する
  8. 初回または必要時だけnpm ciする
  9. Codex/Claudeの認証状態を確認し、sbx setup sshを実行する(第5回)

各処理を冪等にしておくのが肝です。この9段のうち3〜4は対話が入り、7〜8は数分かかります。途中で失敗したときに最初からやり直すのではなく、同じsetupを打ち直せば続きから進む状態にしておくと、環境を作り直すコストが下がります。作り直しのコストが下がると、Kitを気軽に直せるようになります。

ランチャー自体はホスト側で動くコードです。サンドボックスの外にある数少ない自作コードなので、ここだけは隔離の恩恵がありません。書くのはエージェントに任せても、静的検査は必ず通します。

sh -n scripts/sandbox
shellcheck -s sh scripts/sandbox

まとめ

第2回の要点です。

  • agent typeは作成時に1つだけ決まる。Claudeを土台にし、足りないCodex CLIはKitのinstall処理でバージョン固定して入れる
  • 後付けCodex CLIにはhost-side OAuthが効かない。codex login --device-authのトークンはSandbox内に残る(交代の容易さとのtrade-off)
  • 開発サーバーは内側で0.0.0.0へbindし、ホストはlocalhost:3000の1本。publishは127.0.0.1:...:tcp4で絞る
  • 起動口をpackage.jsonに置くとホストのNode.jsが必要になり、隔離の目的と衝突する
  • 責務はpackage.json(アプリ内部)/launcher(境界・認証・ライフサイクル)/spec.yaml(環境)に三分割する。setupは9段すべて冪等にし、打ち直せる状態を保つ
  • ランチャーはサンドボックスの外で動く唯一の自作コード。隔離の恩恵がないので静的検査を通す

次回は、このランチャーのsetupが扱うcredentialのうち、GitHubのトークンを扱います。サンドボックスでエージェントを自由に走らせる前提だからこそ、gh loginをそのまま持ち込まない、という話です。

連載の構成

  1. Docker Sandboxes入門とプロジェクト初期設定
  2. CodexとClaudeを同じSandboxに同居させる(本記事)
  3. GH_TOKENでリポジトリ権限を絞る
  4. OpenAI APIアクセスのはまりどころ(header注入)
  5. Codex AppとClaude DesktopからSSHで入る
  6. Sandboxを1つにした構成判断
  7. Orca ADEとSSH接続の並列開発(別記事)

参考資料


本記事はsbx 0.39.0で2026年8月23日に検証した内容です。CLIのオプション名は更新される可能性があるため、sbx create --helpなど手元のバージョンの出力を併せて確認してください。

関連記事

Grandream

Grandream

株式会社グランドリーム

AI・システム開発のプロフェッショナルチームです。AIエージェント・業務自動化・Webシステム開発などを手がけています。

AIエージェント開発のご相談はお気軽に

PoC段階から本番運用まで一貫対応します。

AI開発について相談する

AIエージェント開発サービスの詳細を見る →

どのサービスが合うか30秒で診断する →