[English](docker.md) | 日本語

# Docker で MAESTRO を動かす

MAESTRO を最短で動かす方法が Docker Compose です。`git clone` から稼働まで一通りと、つまずきやすい点（Linux のネットワーク、LLM エンドポイント、データ永続化、サンドボックス）をまとめます。

## まずはこれだけ

Linux でも、Windows の WSL2（Docker Desktop の WSL 統合）でも動きます。

```bash
docker compose up -d     # 初回はイメージをビルドしてから起動
# http://localhost:9876 を開く
```

`.env` や `config.yaml` は用意しなくても起動します。未設定のまま起動すると UI にセットアップウィザードが開き、そこから LLM の接続先を設定できます。先に指定しておきたい場合は `cp .env.example .env` して `OLLAMA_BASE_URL` / `OLLAMA_MODEL` を設定します（[LLM エンドポイント](#llm-エンドポイント)参照）。

Compose は `9876` を**全インターフェース**に公開するので、Docker ホストは初期状態から LAN で到達できます。裏を返すと初期状態は**認証なしで露出**しており、ポートに届く相手はエージェントの Bash ツール経由でホスト上のコードを実行できます。共有ネットワークに置いたままにする前に[ローカル以外へ出す](#ローカル以外へ出す)を必ず読んでください。

### Windows（WSL2）

Docker Desktop の WSL 統合を有効にした WSL2 ディストロ内で、同じコマンドを実行するだけです。ヘッド付きブラウザ一式はコンテナ内で完結するため、Windows 側に X サーバーや WSLg は不要です。またリポジトリの `.gitattributes` がビルド用のシェルスクリプトを LF に固定するので、Windows でチェックアウトしても（本来 CRLF になってしまう場合でも）ビルドが壊れません。

## コンテナがやること・やらないこと

- MAESTRO 本体（Web UI・ワーカー・ツール・任意の Gateway）を動かす。
- LLM は動かさない。既存の OpenAI 互換エンドポイント（Ollama / vLLM / ホスト型ゲートウェイ等）に向ける。
- ヘッド付きブラウザ一式（Xvfb / x11vnc / Chromium）と Bash サンドボックス（bubblewrap ＋ 事前同梱の Python ツール群）を内蔵。Browser タブとサンドボックス Bash は最初から動く。

## LLM エンドポイント

`.env.example` の既定は、ホスト上の Ollama を指します。

```ini
OLLAMA_BASE_URL=http://host.docker.internal:11434/v1
OLLAMA_MODEL=qwen3:32b
```

`host.docker.internal` は Docker Desktop（macOS/Windows）に加え、`docker-compose.yml` が `extra_hosts: host.docker.internal:host-gateway` を設定しているため Linux でも解決できます。通常は変更不要です。

LLM が Docker ホスト以外にある場合は向き先を変えます。

```ini
# 別マシンの Ollama / vLLM（そのマシンの IP を指定）
OLLAMA_BASE_URL=http://192.0.2.10:11434/v1
# ホスト型の OpenAI 互換ゲートウェイ
OLLAMA_BASE_URL=https://your-gateway.example.com/v1
```

`OLLAMA_MODEL` のモデルがそのエンドポイントで実際に pull / 配信されていることを確認してください。なければ最初の LLM 呼び出しで失敗します。

## 起動の確認

```bash
docker compose ps                 # STATUS が healthy になる
docker compose logs -f maestro    # 起動ログを追う。Ctrl-C で停止
```

コンテナは `/health` を叩くヘルスチェックを持ちます。`healthy` になったら <http://localhost:9876> を開き、タスクを1つ作って進行することを確認します。LLM エンドポイントが誤っていれば、タスク開始直後にログへ接続エラーが出ます。

## データ永続化

`docker compose down` やイメージ再ビルドをまたいで残る名前付きボリュームが2つあります。

| ボリューム | マウント先 | 保持する内容 |
|--------|-------|-------|
| `maestro-data` | `/app/data` | SQLite DB・ユーザー・スキル・シークレット |
| `maestro-workspaces` | `/workspaces` | タスクごとのエージェント作業領域 |

`docker compose down -v` はこれらのボリューム（＝全データ）を削除します。残したいときは `-v` を付けないこと。

## 設定

イメージには動作する `config.yaml`（`config.yaml.example` のコピー）が入っており、`.env` でよく使う項目（LLM エンドポイント・`PORT`）を上書きします。多くの構成では `.env` の編集だけで十分です。

`config.yaml` 全体をホスト側で管理し、**Settings UI** の編集をコンテナ再作成後も残したい場合は bind-mount します（`docker-compose.yml` のコメントを外す）。

```yaml
    volumes:
      - ./config.yaml:/app/config.yaml   # 先に ./config.yaml を作っておく
```

起動前にホスト側のファイルを作成してください（`cp config.yaml.example config.yaml`）。無いと Docker がそのパスに*ディレクトリ*を作ってしまいます。bind-mount しない場合、Settings UI の変更はコンテナ内だけに残り、再作成で失われます。

## Docker 内の Bash サンドボックス

`safety.bash_sandbox` の既定は `auto`（bubblewrap があれば使い、無ければ堅牢な許可リストへフォールバック）。イメージは bubblewrap を同梱するので、サンドボックスは既定で有効です。複数ユーザーや信頼できないワークロードでは `safety.bash_sandbox: always`（bubblewrap が無ければ fail-closed）にします。

bubblewrap は非特権ユーザー名前空間を必要とします。多くの新しめのホストでは有効ですが、ホストやコンテナランタイムが無効化している場合、`auto` は堅牢な許可リストへ縮退し、起動時に警告を出します。詳細は [operations/bash-sandbox-provisioning.md](operations/bash-sandbox-provisioning.md)。

## ビルド方式（プレビルトではない）

`docker compose up` は `Dockerfile` から**ローカルでイメージをビルド**します（公開イメージはありません）。初回ビルドは Chromium と Python ツール群を取得するため数分かかります。以降はキャッシュを再利用します。新しいコードを取得したら `docker compose up -d --build` で再ビルドします。

## ローカル以外へ出す

既定で Compose は `9876` を全インターフェースに公開するため、インスタンスは初期状態から LAN で到達でき、**かつ認証なし**です。共有ネットワーク・信頼できないネットワークで常用する前に必ず堅牢化してください:

1. 認証を有効化（OAuth、またはローカルアカウント）。
2. `safety.bash_sandbox: always` を設定。
3. TLS を終端（MAESTRO のネイティブ HTTPS か、前段のリバースプロキシ）。

逆に**ローカル限定**に戻したい場合（SSH トンネル経由で使う等）は、compose のポートマッピングを `127.0.0.1:9876:9876` に固定します。

完全な堅牢化チェックリストは [SECURITY.md](../SECURITY.md) と [getting-started.md](getting-started.md) を参照。

## トラブルシューティング

| 症状 | 考えられる原因 |
|---------|--------------|
| タスクが接続エラーで即失敗 | `OLLAMA_BASE_URL` にコンテナから到達できない、またはモデルが配信されていない |
| `host.docker.internal` が見つからない（Linux） | compose から `extra_hosts` が消えている、または host-gateway 非対応の古い Docker。ホストの LAN IP を使う |
| `down`/`up` 後に設定が消える | `config.yaml` を bind-mount していない（[設定](#設定)参照） |
| Browser タブ・CAPTCHA プールが不安定 | `/dev/shm` が小さい。compose は `shm_size: 1gb` を設定済み。維持すること |
| 再起動後にデータが全部消えた | `docker compose down -v` を使った（`-v` がボリュームを削除する） |
