サーバー(VPS)へのChimeraのデプロイ
Chimeraは長時間稼働するゲートウェイプロセスとして動作します。--cron を追加すると、実際の時計に基づいてスケジュールされたジョブも発火するようになり、(メッセージを受け取った時だけでなく)時間通りに行動します。このガイドでは、5ドルのVPSデプロイを2通りの方法で扱います: Docker Compose(推奨)または systemd。
状態 — 長期記憶、cronジョブ、トラジェクトリ、監査ログ — は CHIMERA_HOME(ディレクトリ)に存在します。これを永続化すれば(Dockerボリュームまたは実際のパス)、エージェントは再起動を生き延びます。
0. 前提条件
- Linux VPS(単一エージェントには1 vCPU / 1 GB RAMで十分です)。
- 少なくとも1つのプロバイダーキー。最も安く始めるならOpenRouterキーです。
- パブリックな受信webhook(WhatsApp Cloud API、
POST /webhook/<hook>)には、ドメイン + TLS付きのリバースプロキシ(CaddyまたはNginx)が必要です。Discord/Telegram/Slack/Signalは送信接続なので不要です。
テンプレートから環境ファイルを作成し、キーを入力してください。
cp .env.example .env
# edit .env — e.g. set CHIMERA_OPENROUTER_KEYS=sk-or-...
1. Docker Compose(推奨)
docker compose up -d # build + run; restarts on crash and on reboot
docker compose logs -f # watch it
docker compose ps # status + health
これは chimera serve --host 0.0.0.0 --cron を実行します: HTTPゲートウェイ(/chat、/webhook/<hook>、/health)に加えて cronデーモンです。状態は chimera-data ボリュームに永続化されます。
チャットプラットフォームを配信する(Discordの例) — .env にトークンを設定し、docker-compose.yml のコマンドを上書きします。
command: ["serve", "--host", "0.0.0.0", "--cron", "--discord"]
そして再度 docker compose up -d します。(Telegram/Slack/Signalも各フラグで同様に動作します。それぞれに対応する CHIMERA_* トークンが必要です — .env.example を参照してください。)
新しいバージョンへの更新:
git pull && docker compose up -d --build
2. systemd(Dockerなし)
ホスト上のvirtualenvにインストールします。
git clone https://github.com/brcampidelli/chimera-agent.git /opt/chimera
cd /opt/chimera
python3 -m venv .venv && . .venv/bin/activate
pip install '.[messaging,mcp]'
cp .env.example .env # then edit it
/etc/systemd/system/chimera.service を作成します。
[Unit]
Description=Chimera Agent gateway + cron daemon
After=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/chimera
EnvironmentFile=/opt/chimera/.env
Environment=CHIMERA_HOME=/opt/chimera/state
ExecStart=/opt/chimera/.venv/bin/chimera serve --host 0.0.0.0 --cron
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now chimera
sudo systemctl status chimera
journalctl -u chimera -f
3. プロアクティブな作業をスケジュールする(--cron デーモン)
--cron はスケジュールしたジョブを実行するだけです。CLIで追加してください(CHIMERA_HOME に永続化されます)。
chimera cron add "morning-brief" "0 8 * * *" "Summarize overnight news and post it."
chimera cron add "nightly-backup" "0 3 * * *" "Back up the important files."
chimera cron list
Docker内では:
docker compose exec chimera chimera cron add "morning-brief" "0 8 * * *" "..."
デーモンは --cron-tick 秒(デフォルト30)ごとにティックし、期限が来た各ジョブのアクションをエージェント経由でディスパッチします。失敗したジョブはログに記録され、デーモンを止めることは決してありません。
スケジュールが黙っていることを尋ねる
chimera cron doctor
スケジュールは2通りの黙り方をしますが、尋ねるまではどちらも同じに見えます — 何も期限が来ていないスケジュールのように:
- 何も実行されなかった。 デーモンが死んだ、コンテナが一度も再起動されなかった、ホストが眠った。例外もログ行も判定もありません。ここにある他のあらゆる誠実さの仕組みは実行が起きたことより下流に位置するので、どれにも出番が回ってきません。
- すべて実行されてすべて失敗した。 デーモンは生きていて、
last_runは1分前、スケジュールは前に進んでいます — それでいて1か月のあいだディスパッチが毎回失敗しています。こちらのほうが1つ目より健康そうに読めます。健康に見えるフィールドが記録しているのは試行であって、結果ではないからです。
cron doctor は両方の問いを立て、異なる助言をします。直し方に共通点がまったくないからです: 遅延はデーモンの話、失敗はジョブの話です。何かが失敗しているとき chimera cron list が1行を出力するので、このコマンドの存在を知らなくても気づけます。
これが何ではないか。 これは問いであって、見張りではありません: プロセスが落ちているあいだ、ここでは何も気づきません — クラッシュしたプロセスが自分のクラッシュをログに残せないのと同じ理由です。何かが尋ねたその瞬間に正直に答えます — シェル、アプリ、次回の起動。本物の監視役には自分の時計と自分の生存性が要り、それは別個の決定であって、issue #26として追跡されています。答えではなく警報がほしい場合は、ホスト自身のcronから実行してください:
*/30 * * * * cd /opt/chimera && out=$(.venv/bin/chimera cron doctor --check) || echo "$out" | mail -s "chimera" you@example.com
これが機能するのは、Chimera以外の何かに監督されているからです — それこそが要点です。
--check は、ジョブが遅れているか失敗しているときだけではなく、デーモンのハートビートが古くなったときにも 1 で終了します。つまり上の host-cron の行は、ジョブの予定が何であれ、次回の実行で死んだデーモンを検出します。メールはそのときだけ届きます。オプションなしの cron doctor は常に 0 で終了します — 答えることは失敗ではないからです。
デーモンのハートビートが、ジョブでは閉じられない窓を閉じます。 各 tick ごとに、デーモンはその生存の証を <CHIMERA_HOME>/scheduler/heartbeat.json(jobs.json の隣、アトミックに)に書き込みます。doctor はそれを最初に読み、ジョブだけでは答えられない問いに答えます:死んだデーモンが日次ジョブを持っていると、ジョブから見る限り約 23 時間は健康に見えます — ジョブはまだ遅れていないからです — 一方ハートビートはデーモンが死んだ瞬間に止まります。判定は、ハートビート自身が記録した間隔の 3 tick を上限として下され、使われた上限つきで表示されるので、数字の出所を推測する代わりに、その数字に異議を唱えられます。三つの正直な読み方:alive/stale は判定済み。unknown はハートビートが存在するが tick 間隔を記録していない(古いライター)ことを意味し、新鮮さは数を捏造しない限り判定できません — そして読み手は捏造を拒みます。none はハートビートが全くないことで、これは“信号なし”であって“死”ではありません — 一度も走ったことのないデーモンは、どちらの証拠も残しません。これは liveness であって taint ではありません:スケジューラ表面はtaint を追跡しません(SECURITY.md 参照)。
4. ヘルス、バックアップ、セキュリティ
- ヘルス:
GET /healthは{"ok": true}を返します。Composeにはヘルスチェックが組み込まれています。 - バックアップ:
chimera-dataボリューム(Docker)またはCHIMERA_HOMEディレクトリ(systemd)をバックアップしてください — それがすべての永続的な状態です。例:docker run --rm -v chimera-data:/d -v $PWD:/b busybox tar czf /b/chimera-state.tgz -C /d . - シークレット: キーは
.env(git管理外)に保管してください。決してイメージに焼き込まないでください。 - 公開範囲: ゲートウェイを
0.0.0.0にバインドするのはファイアウォール/リバースプロキシの背後のみにしてください。CHIMERA_SERVER_TOKENを設定して、HTTPゲートウェイとデスクトップAPIでAuthorization: Bearer <token>を必須にしてください(デスクトップUIには、ループバッククライアントに対してのみ自動的にトークンが渡されるため、リモートに公開されたインスタンスは独自の認証の背後にとどまります)。認証はオプトインでデフォルトは空なので、この変数がなければ認証はありません — ポートを制限するか、webhookパスのみを公開してください。デスクトップアプリからこのインスタンスに到達する方法は §5 を参照してください。 - サンドボックス化:
CHIMERA_SANDBOX=dockerを設定すると、シェル/コードツールをホストではなく使い捨てのコンテナ内で実行します。 - 無人でのホスト実行: 2026-07-20以降、ヘッドレス実行はデフォルトの
CHIMERA_HOST_EXEC=askの下でホストコマンドを拒否します(確認できるTTYがないため)。エージェントが本当にホスト上でシェルを実行する必要があるデプロイでは、意図的にCHIMERA_HOST_EXEC=allowを設定します。より安全な選択肢はCHIMERA_SANDBOX=dockerで、コンテナが実際に隔離するためこのゲートはスキップされます。同様に、APIサーバーは汚染絞り込み(CHIMERA_TAINT_NARROW=1)を武装します: エージェントが信頼できないコンテンツを読んだ後、実行/書き込み/送信ツールはフェイルクローズ(失敗時に閉じる)します。自律的に動作し続けるには0に設定してください。
5. デスクトップアプリからこのインスタンスに到達する
デスクトップアプリはデフォルトでは、自分のマシン上で自ら起動したChimeraと話します。v0.44からは、自分で運用しているもの — このVPS — を指すこともできるようになり、アプリは夜通しあなたのcronジョブをこなしているエージェントを覗く窓になります。
ポートを開ける前にこの部分を読んでください。 公開されるのはダッシュボードではありません。このアプリのどの画面も操作面です: シェルを実行し、ファイルを編集し、自律タスクのボードをディスパッチし、設定を変更します。トークンなしでインターネットから到達できるインスタンスは「誰かが眺めるかもしれないChimera」ではありません — アドレスを見つけた誰もがコマンドを実行できるマシンであり、その支払いはあなたのプロバイダーキーです。
3つのことが成り立っている必要があり、最初の2つがなければアプリは接続を拒否します:
1 — TLS. 本物の証明書を持つリバースプロキシの背後に置いてください(Caddyなら取得してくれます):
chimera.seudominio.com {
reverse_proxy 127.0.0.1:8765
}
自分のマシンの外側では、アプリは https でないアドレスを拒否します。トークンはすべてのリクエストで Authorization ヘッダーに載って旅をするからです — 素のhttpでは、それはあなたとサーバーのあいだのすべてのホップに手渡される資格情報であり、その最中に画面上でおかしく見えるものは何もありません。
2 — トークン。 認証はオプトインでデフォルトは空です:
CHIMERA_SERVER_TOKEN=$(openssl rand -hex 32)
それを .env に入れて再起動し、同じ値をアプリに貼り付けてください。アプリが上記の理由でトークンなしのリモートアドレスを拒否するのは、トークンのないインスタンスが見つけた者に対して開いているからです。
サーバーが意図的にしないことに注目してください: リモートクライアントがUIを求めたとき、サーバーはトークンなしでページを配信します。トークンがネットワーク越しに渡されることは決してありません — 一度だけ、帯域外で、自分のクライアントにコピーします。アプリにそのための入力欄があるのはそのためです。
3 — 自分のアプリのオリジン。 アプリは自身のローカルなsidecarから配信されるため、このインスタンスへのリクエストはクロスオリジンになり、このインスタンスがそのオリジンを名指ししない限りブラウザは応答を捨てます:
CHIMERA_ALLOWED_ORIGINS=http://127.0.0.1:45813
接続に失敗すると、アプリが正確な値を表示します — エラーメッセージに載っていて、そのままコピーできます。ポートはインストールごとに安定していて(v0.43から起動間で記憶されます)、接続元のマシンごとに一度設定すれば済みます。複数はカンマ区切りです。
この設定はセキュリティ境界ではなく、そう読んではいけません。 CORSが決めるのはどのページが応答を読めるかであって、誰が呼べるかについては何も決めません。ゲートはトークンです。トークンを設定せずにオリジンを名指ししても何も守られません — 保護されていないインスタンスが curl に加えてブラウザからも到達できるようになるだけです。
デフォルトは空なので、誰も設定していないインスタンスは以前とまったく同じように振る舞います。
失敗したときアプリが伝えること
- 「トークンが拒否されました」 — アドレスもオリジンも正しく、値が間違っています。
- 「到達できませんでした」 — アドレスが間違っているか、オリジンが許可されていないかのどちらかです。ブラウザはどちらなのかを意図的に言わないため、アプリは推測せず両方を挙げ、許可すべきオリジンを渡します。
- バージョン警告 — アプリは自分のバックエンドのバージョンをこちらと比べ、両方の数字を伝えます。拒否はしません: 1リリース遅れのサーバーはたいてい動きますし、拒否すればそれを直すのに必要な画面から締め出すことになるからです。古い側には存在しないエンドポイントがあるかもしれません。
さらに安全に
公開ポートを完全に飛ばす手もあります: WireGuardかTailscaleのtailnet経由でVPSに到達し、アプリをプライベートアドレスに向けてください。それでもトークンは重要です — tailnetは小さい部屋であって、空の部屋ではありません。
6. プロキシ、独自CA、実行中のインスタンスの中を見る
プロキシと独自CA
外向きの通信 — プロバイダー呼び出しと連携 — は、Chimeraプロセスの環境から標準的なプロキシと証明書の変数を読みます。現時点で確認できること(httpx 0.28、LiteLLM 1.99): httpxは HTTP_PROXY、HTTPS_PROXY、NO_PROXY を尊重し、CAバンドルを SSL_CERT_FILE / SSL_CERT_DIR から取得します。LiteLLMは SSL_CERT_FILE を読みます。どちらも REQUESTS_CA_BUNDLE は読みません。これは requests ライブラリの上に作られたコードにしか影響しないので、あなたの連携のどれかが requests を使っている場合にだけ追加で設定してください。LiteLLM内部のプロバイダーアダプターはまちまちなので、依存する経路は想定せず、下のプローブで証明してください。これらはいずれもChimeraの受信リスナーやAPIの認可を変えません。
シェルまたはsystemdの環境では:
export HTTP_PROXY=http://proxy.example:8080
export HTTPS_PROXY=http://proxy.example:8080
export NO_PROXY=localhost,127.0.0.1,.internal.example
export SSL_CERT_FILE=/etc/ssl/certs/company-ca.pem
# Optional, only for requests-based code:
# export REQUESTS_CA_BUNDLE=/etc/ssl/certs/company-ca.pem
chimera doctor --probe
docker-compose.yml では、サービスの environment: の下に書きます(CAファイルはコンテナ内に存在している必要があります):
environment:
HTTP_PROXY: http://proxy.example:8080
HTTPS_PROXY: http://proxy.example:8080
NO_PROXY: localhost,127.0.0.1,.internal.example
SSL_CERT_FILE: /etc/ssl/certs/company-ca.pem
# REQUESTS_CA_BUNDLE: /etc/ssl/certs/company-ca.pem # optional, requests-based code only
volumes:
- ./company-ca.pem:/etc/ssl/certs/company-ca.pem:ro
chimera doctor --probe は、設定した経路と証明書チェーンを通じて実際のプロバイダー呼び出しを1回行います。プローブが通れば、そのプロバイダーとその呼び出しが検証されたことになります。すべての連携を保証するものではありません。可能な限り認証情報をプロキシURLに入れないでください: 環境変数の値は、プロセスを調べられる人なら誰にでも見えます。
chimera api と chimera logs
実行中のインスタンス向けのターミナルコマンドが2つあり、どちらもサーバーは起動しません:
chimera api GET /api/health # the app's API: status and body, token never printed
chimera api POST /api/... -d '{}' --yes # changing state needs --yes
docker exec chimera chimera api GET /health # the HTTP gateway of plain `chimera serve`
docker exec chimera chimera logs --level ERROR --lines 50
docker exec chimera chimera logs --follow --run <run-id>
chimera api は環境の CHIMERA_SERVER_TOKEN を送ります。アプリが CHIMERA_HOME に公開するURLから chimera app を見つけ、見つからなければ http://127.0.0.1:8765 を使います。これは chimera serve のデフォルトポートで、そのゲートウェイは GET /health と POST /chat に応答します(そこに /api はありません)。serve --discord/--telegram/--slack/--signal はHTTPポートをまったく開かないので、その構成では chimera api が到達できる先はありません。chimera logs は引き続き使えます。--url でアドレスを上書きできます。ループバックへの呼び出しは上のプロキシ変数を無視するので、トークンがプロキシに届くことはなく、コマンドはリモートの http:// URLへのトークン送信を拒否します。個々のリクエストを決めるのは、引き続きサーバー自身の認可と承認です。
chimera logs は CHIMERA_HOME/logs/backend.log を読みます: chimera serve と chimera app が書き出す警告とエラーで、1 MBでローテーションされ、シークレットはマスクされています。--level と --run の下でもトレースバックは所属するレコードと一緒に残り、--follow はローテーションをまたいで読み続けます。
7. 正直な状況
Chimeraはアルファ版です。これはデプロイされ動作し、cronデーモンによってプロアクティブになります。本番実績は1つのデプロイだけです。作者自身のVPSで、2026年7月からcronとDiscordボットを24時間動かしています。これは実際の利用ですがフリートではなく、あなたのワークロードは経験していません。低リスクなcronから始め、logs を監視し、実システムに触れる何かに対してはガバナンスのガードレール(solve の --guard、CHIMERA_SANDBOX=docker)を心に留めておいてください。
これらのページが公開されている場所
これらのファイルはchimeraagent.space上のドキュメントのソースであり、ビルド時にこのディレクトリから直接レンダリングされます。ここのMarkdownを編集すればサイトに反映されます。同期を保つべき2つ目のコピーは存在しません。
かつて mkdocs.yml にあったMkDocsの設定は削除されました。それは完成していました — テーマ、ナビゲーション、10ページ — しかし一度も公開されたことはありませんでした: ワークフローも gh-pages ブランチも存在しなかったため、かつてこの場所にあったデプロイ手順は、存在しないサイトについて説明していたことになります。誰も実行しない設定は、設定が無いよりも悪いものです。なぜなら次の人がそのナビゲーションを編集しても、なぜ何も変わらないのか分からないからです。