Hermes Agent 入門 — 初心者のためのハンズオンガイド|2026-09 版
第 10 章

実行環境 — ローカルから Docker・クラウドまで 7 バックエンド

local / Docker / SSH / Modal / Daytona / Vercel Sandbox / Singularity の使い分けとコンテナ強化

この章で分かること

これは何か

「エージェントに shell を渡す」と聞いて身構えるのは自然な反応だ。Hermes のエージェントはターミナルツール経由で実際にコマンドを実行できるが、そのコマンドが物理的にどこで動くかを選べるのが、この章で扱う「ターミナルバックエンド」という仕組みである。

設定の入口は 1 つで、~/.hermes/config.yaml の terminal.backend キーである。

terminal:
  backend: local    # local | docker | ssh | modal | daytona | vercel_sandbox | singularity

変更は第 3 章で紹介した hermes config サブコマンドで行う。

hermes config get terminal.backend
hermes config set terminal.backend docker

2026-09 時点(v0.21.0)で選べるバックエンドは 7 種類。数え方には公式ドキュメント内でも表記のゆれがあり、アーキテクチャ図の注記は今も古い「6 backends」のままだが、設定リファレンス本文は「seven terminal backends」と明記している。本章では後者の 7 を正としたうえで、内訳を次節以降で見ていく。

バックエンドが決めるのは「コマンドがどこで動くか」だけではない。terminal ツール、ファイル操作ツール、execute_code の RPC(第 9 章)もすべて同じバックエンド上で実行される。バックエンドを Docker に切り替えると、これらのやり取りはすべて同じコンテナの中に閉じ込められる。

いつ使うのか

典型的な選び方は次のような問いに集約できる。

Docker デーモンが使えない環境(HPC クラスタや、root 権限のない共有マシンなど)では singularity(Apptainer)が代わりになる。迷ったときの目安を表にまとめる。

状況選ぶバックエンド
個人のマシンで素早く試したい・隔離のコストを払いたくないlocal
危険なコマンドの結果をホストから隔離したい・安全なサンドボックスで CI/CD を回したいdocker
手元にない強力なリモートマシンで作業したいssh
評価・ベンチマークを使い捨てのクラウド計算資源で並列に回したいmodal
チームで管理されたクラウド開発環境を使い回したいdaytona
Vercel へのデプロイを前提に、スナップショットでファイルシステムを保存したいvercel_sandbox
HPC クラスタや共有マシンで Docker デーモンが使えないsingularity

7 つに共通する軸は「隔離の強さ」と「使い捨てかどうか」である。次節ではこの 2 軸に沿って、リスクの段階として整理する。

リスクの段階で見る 7 バックエンド

「エージェントに shell を渡すのが怖い」という感覚に対する Hermes の答えは、隔離をオール・オア・ナッシングにしないことだ。ホスト直実行からクラウドの使い捨て環境まで、隔離の強さを段階的に選べる。

別マシンへ コンテナへ クラウドへ ローカル ホスト権限そのまま local SSH(別マシン) ネットワーク境界 ssh コンテナへ隔離 namespace+権限剥奪 docker・singularity クラウド使い捨て 破棄 or 復元 modal / daytona vercel_sandbox
図 10-1 左から右へ進むほど隔離が強くなり、最終的には使い捨てのクラウド環境になる。docker と singularity はどちらも namespace でコンテナに閉じ込めるが、剥奪する権限の細かさは異なる(次節で解説)。

各段階で何が変わるかを言葉でも確認しておく。local はエージェントに自分のユーザー権限をそのまま渡す。ssh は物理的に別のマシンへ実行を移すが、隔離の仕組み自体はそのマシンの通常のシェルと変わらない——ネットワークの向こう側にあるという境界だけが頼りになる。docker と singularity は OS のネームスペース機能でプロセスを閉じ込める箱を作り、docker はさらに Linux capability を丸ごと剥奪したうえで一部だけ復元する分だけ隔離が強い。modal / daytona / vercel_sandbox はクラウド上の VM・コンテナ・マイクロ VM そのものを使い捨て、セッションが終わればファイルシステムをスナップショットしてから消す。

次節では、初心者が最初に手を出しやすい docker の中身を詳しく見ていく。

Docker バックエンドの中身 — ハードニングと永続コンテナ

Docker バックエンドは、Hermes プロセスが動くホストの上で1 つの永続 Docker コンテナを起動し、以降のすべてのターミナル呼び出しをそのコンテナへの docker exec として実行する。「1 つ」という点が重要で、同じセッションだけでなく、/new による新規会話、/reset、delegate_task によるサブエージェント(第 9 章)まで、すべて同じコンテナを共有する。

セッション /new(新規会話) delegate_task サブエージェント docker exec docker exec docker exec 永続 Docker コンテナ(1つだけ) --cap-drop ALL(DAC_OVERRIDE / CHOWN / FOWNER のみ復元) --security-opt no-new-privileges --pids-limit 256 tmpfs 制限:/tmp 512MB・/var/tmp 256MB・/run 64MB /workspace・パッケージ・バックグラウンドプロセスが持続 次のプロセスが再接続 Hermes プロセス終了(/quit・SIGKILL) → 既定では no-op、コンテナは動き続ける
図 10-2 セッション・新規会話(/new)・delegate_task のサブエージェントはすべて同じ永続コンテナに docker exec で接続する。Hermes プロセスが終了しても既定では何もしない no-op で、コンテナは動き続ける。

この共有ぶりは徹底している。作業ディレクトリの変更、apt install や pip install で入れたパッケージ、/workspace 以下のファイル、そしてバックグラウンドで動かしたプロセスまで、次のツール呼び出しへ、さらには次の Hermes プロセスへとそのまま持ち越される。次に起動した Hermes プロセスは、コンテナに付いたラベル(hermes-agent / hermes-task-id / hermes-profile)による検索でこの同じコンテナを見つけ、数ミリ秒で再接続する。

コンテナが実際に止まる(docker rm -f される)のは次の場合に限られる。

もう 1 つ、container_persistent: false に設定すると挙動が変わる点にも触れておく。これは「セッションごとに使い捨てのコンテナを作る」モードで、チャットやセッションが終わるたびにコンテナが破棄され、ファイルシステム・マウント・バックグラウンドプロセスは何も引き継がれない。会話を跨いだ隔離をセキュリティ境界として扱いたいときはこちらを使う。ただし delegate_task のサブエージェントは、このモードでも親セッションのコンテナをそのまま共有する。

セキュリティハードニングの中身は次のとおりである。

項目設定内容
Linux capability--cap-drop ALL で全剥奪したうえで、DAC_OVERRIDE / CHOWN / FOWNER のみ復元
権限昇格--security-opt no-new-privileges
プロセス数--pids-limit 256
一時領域サイズ制限つき tmpfs(/tmp 512MB、/var/tmp 256MB、/run 64MB)

公式イメージは nikolaik/python-nodejs:python3.11-nodejs20(Python 3.11 系・Node.js 20 系)が既定で、docker_image キーで差し替えられる。トークンやクレデンシャルは docker_env(config.yaml に直接書く固定値)と docker_forward_env(ホストのシェルや ~/.hermes/.env から値だけを引き継ぎ、値自体は config.yaml に残さない)で使い分ける。ネットワークを完全に遮断したいときは docker_network: false にすると --network=none の air-gap コンテナになる。

SSH・クラウド系バックエンドの使い分け

Docker の外側にある 5 つのバックエンドは、それぞれ必要な認証情報と永続化の仕組みが異なる。

バックエンド必要な認証情報永続化の方式注意点
sshTERMINAL_SSH_HOST / TERMINAL_SSH_USERControlMaster で接続再利用+常駐シェルで cwd・環境変数を保持5 分アイドルで keepalive。sudo や標準入力が要る操作は都度接続にフォールバック
modalMODAL_TOKEN_ID + MODAL_TOKEN_SECRET(または ~/.modal.toml)ファイルシステムのみスナップショットプロセスや PID 空間は保持されない
daytonaDAYTONA_API_KEYstop / resume 方式ディスク上限 10 GiB(Daytona 側の制約)
vercel_sandboxVERCEL_TOKEN + VERCEL_PROJECT_ID + VERCEL_TEAM_ID(開発中は短命な VERCEL_OIDC_TOKEN でも可)ファイルシステムをスナップショット / 復元pip install 'hermes-agent[vercel]' が別途必要
singularityなし(apptainer / singularity バイナリが必要)書き込み可能なオーバーレイが永続docker://... の URL は自動で SIF ファイルに変換・キャッシュされる。--containall --no-home で完全な namespace 隔離

いずれのバックエンドも container_persistent: true(既定)で「ファイルシステムを保存する」ことは約束するが、「同じライブなサンドボックス・PID 空間・バックグラウンドプロセスがそのまま動き続ける」ことまでは約束しない。ここが Docker バックエンドとの決定的な差になる。Docker は本物の永続コンテナをそのまま生かし続けるのに対し、クラウド系はいったん破棄してからファイルシステムだけを復元する方式である。

iron-proxy — サンドボックスに本物の鍵を渡さない

Docker サンドボックスの中でエージェントがプロンプトインジェクションに引っかかったとき、最悪のシナリオは「printenv | grep -i key で本物の API キーがそのまま盗まれる」ことだ。これを防ぐのが、Hermes がホスト側で動かすエッジプロキシ iron-proxy(egress credential-injection proxy)である。2026-09 時点では Docker バックエンドのみに配線されており、Modal / Daytona / SSH / Singularity にはまだプロキシ用の環境変数も CA 証明書も渡らない。

プロキシ トークン 本物の鍵に 転送 サンドボックス内 OPENROUTER_API_KEY= プレースホルダ(token) iron-proxy(ホスト) TLS 終端 本物の鍵に置換 外部 API openrouter.ai など 許可リストのみ ✕ サンドボックスから外部への直接アクセスはできない
図 10-3 サンドボックスはプレースホルダのプロキシトークンしか持たず、ホスト側の iron-proxy が TLS を終端して本物の API キーに置き換えてから外部へ転送する。直接アクセスの経路は存在しない。

導入は hermes egress のサブコマンドで行う。

hermes egress install   # 署名済みバイナリをダウンロード
hermes egress setup     # ホスト側のCA生成 + プロバイダごとのプロキシトークンを発行
hermes egress start     # プロキシデーモンを起動
hermes egress status    # 状態確認

デフォルトで許可されているアップストリームは openrouter.ai / api.openai.com / api.anthropic.com / generativelanguage.googleapis.com / api.x.ai / api.mistral.ai / api.groq.com / api.together.xyz / api.deepseek.com / inference.nousresearch.com の 10 件で、ループバック・リンクローカル(クラウドのメタデータ API である 169.254.169.254 を含む)・RFC1918 プライベートアドレス・CGNAT といった SSRF 対策の拒否 CIDR も既定で効いている。

ただし万能ではない。リクエスト署名や SDK が発行する OAuth を使う認証方式はこの仕組みではカバーできない。AWS Bedrock / SageMaker の SigV4 署名、GCP Vertex AI のサービスアカウント OAuth がこれにあたり、該当する環境変数がサンドボックスに存在する場合は、その分だけ本物のクレデンシャルがサンドボックスの中に置かれることになる。

ハンズオン 10-1 Docker バックエンドに切り替えて、コンテナの中で動いていることを確認する

Docker Desktop(または Docker Engine)が起動している必要がある。初回はハンズオン中に既定イメージ nikolaik/python-nodejs:python3.11-nodejs20 のダウンロードが走るため、回線状況によっては数分かかる。切り替えると、以降のシェル実行はホストのユーザー権限では動かなくなることも意識しておく。

  1. 現在のバックエンドを確認する。
hermes config get terminal.backend

何も変更していなければ既定値の local が返るはずだ。

  1. Docker バックエンドに切り替える。
hermes config set terminal.backend docker
  1. hermes を起動し、実行環境を確認するコマンドを投げる。
python3 --version
node --version

ホスト側と違うバージョンが返ってくれば、コンテナの中で実行されている証拠になる。既定イメージの名前がそのまま Python 3.11 系・Node.js 20 系であることを示しているので、ホストにこれと違うバージョンが入っていれば差は一目で分かる。

ハンズオン 10-2 単一コンテナがセッションをまたいで共有されることを確認し、後片付けする

  1. ハンズオン 10-1 のセッションの中で、/workspace にファイルを 1 つ作る。
echo "hermes-container-test" > /workspace/marker.txt
  1. exit でいったん Hermes プロセスを終了し、hermes で新しいプロセスを起動し直す。そのうえで同じファイルを読む。
cat /workspace/marker.txt

プロセスを再起動しても中身がそのまま読めれば、それは同じコンテナに再接続できている証拠である。ホストを再起動したわけでもコンテナを作り直したわけでもなく、ラベルによる検索で同じコンテナに docker exec し直しているだけだからだ。

  1. 確認が終わったら、バックエンドを元の状態へ戻す。
hermes config set terminal.backend local

local に戻しても、起動したコンテナ自体はホスト上に残り続ける(既定では終了時に何もしない no-op のため)。動かしっぱなしにしたくない場合は、Docker のラベルで検索して明示的に削除する。

docker ps --filter label=hermes-agent=1
docker rm -f <コンテナ名またはID>

つまずきポイント

「バックエンド数」の表記はドキュメント間で割れている — アーキテクチャ図の注記は今も「6 backends」と書かれたままだが、設定リファレンス本文は明確に「seven terminal backends」としている。Vercel Sandbox を含めた 7 が正しい数字であり、古い図の注記に引きずられないよう注意する。

Docker バックエンドの「持ち越し」はバグではなく仕様 — セッションを終えても、新しい会話を始めても、Hermes プロセスを再起動しても、同じコンテナがそのまま使われ続ける。「前回インストールしたパッケージが残っている」「前回立ち上げたバックグラウンドプロセスがまだ動いている」という状態は想定内の挙動であり、まっさらな環境が欲しいときは container_persistent: false にするか、コンテナを手動で削除する必要がある。

iron-proxy は Docker 限定で、しかも全認証方式をカバーしない — Modal・Daytona・SSH・Singularity はまだプロキシの対象外で、これらのバックエンドでは本物のクレデンシャルがそのままサンドボックス(あるいはリモートマシン)に渡る。Docker バックエンドで使っていても、AWS SigV4 や GCP のサービスアカウント OAuth のような署名・OAuth ベースの認証は素通りしてしまう点も忘れてはならない。

クラウド系バックエンドの「永続化」はプロセスの生存を意味しない — container_persistent: true はファイルシステムのスナップショット / 復元を約束するだけで、同じライブなサンドボックスや PID 空間、動かしっぱなしのバックグラウンドプロセスが次回も生きている保証ではない。「さっき立てたサーバーがまだ動いているはず」という期待は、Modal / Daytona / Vercel Sandbox では外れる可能性が高い。

理解度チェック

Q1. 2026-09 時点(v0.21.0)でのターミナルバックエンドは何種類ですか。表記が古いドキュメント箇所はどこですか。

7 種類(local / docker / ssh / modal / daytona / vercel_sandbox / singularity)。設定リファレンス本文が正しい数字を示しているのに対し、architecture.md の図の注記は古い「6 backends」のままになっている。

Q2. Docker バックエンドで、セッションを終了しても新しい Hermes プロセスを起動しても同じコンテナが使われ続けるのはなぜですか。

コンテナには hermes-agent / hermes-task-id / hermes-profile のラベルが付与されており、Hermes プロセスは起動時にこのラベルで既存コンテナを検索して再接続するため。プロセス終了時のクリーンアップは既定では「何もしない」no-op で、コンテナ自体は動き続ける。

Q3. Docker バックエンドのセキュリティハードニングのうち、Linux capability に関する設定を説明してください。

--cap-drop ALL で全 capability をいったん剥奪したうえで、DAC_OVERRIDE・CHOWN・FOWNER の 3 つだけを復元する。加えて --security-opt no-new-privileges で権限昇格を防ぎ、--pids-limit 256 でプロセス数を制限する。

Q4. iron-proxy はサンドボックスにどんな値を持たせ、実際の API キーはどこで置き換えられますか。

サンドボックスには opaque なプロキシトークンだけを持たせる。ホスト側で動く iron-proxy デーモンが TLS を終端し、そのプロキシトークンを本物の API キーに置き換えてから外部のアップストリームへ転送する。

Q5. iron-proxy がカバーできない認証方式を 2 つ挙げてください。

AWS Bedrock / SageMaker が使う SigV4 署名リクエストと、GCP Vertex AI のサービスアカウントから発行される OAuth。どちらも静的なヘッダー置換では扱えないため、該当する環境変数があればサンドボックスに本物のクレデンシャルがそのまま渡ってしまう。

まとめ

参考リンク