Развёртывание Chimera на сервере (VPS)
Chimera работает как долгоживущий процесс — шлюз. Добавьте --cron, и она вдобавок будет
запускать задания по расписанию, то есть действовать вовремя, а не только когда ей напишут. Это
руководство описывает развёртывание на VPS за $5 двумя способами: Docker Compose (рекомендуется)
или systemd.
Состояние — долговременная память, задания по расписанию, траектории, журнал аудита — живёт в
CHIMERA_HOME (это каталог). Сохраните его (том Docker или настоящий путь), и агент переживёт
перезапуски.
0. Что нужно заранее
- VPS на Linux (одного ядра и 1 ГБ памяти вполне достаточно для одного агента).
- Хотя бы один ключ поставщика. Самое дешёвое начало — ключ OpenRouter.
- Для публичных входящих вебхуков (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) плюс демон расписания. Состояние сохраняется в томе 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)
Установите в виртуальное окружение прямо на хосте:
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 только выполняет задания, которые вы завели. Добавляйте их из командной строки (они
сохраняются в 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
Расписание может замолчать двумя способами, и пока вы не спросите, оба выглядят одинаково — как расписание, у которого ничего не назначено:
- Ничего не выполнялось. Демон умер, контейнер так и не перезапустили, хост уснул. Ни исключения, ни строки в журнале, ни вердикта. Все остальные механизмы честности здесь стоят ниже по течению от состоявшегося запуска, поэтому ни одному из них не достаётся хода.
- Всё выполнялось и всё провалилось. Демон жив,
last_runбыл минуту назад, расписание движется — и при этом каждая отправка проваливается уже месяц. Это выглядит здоровее первого случая, потому что поле, похожее на признак здоровья, записывает попытку, а не исход.
cron doctor задаёт оба вопроса и даёт разные советы, потому что у этих неисправностей нет ничего
общего: просрочка — про демон, а провалы — про задание. chimera cron list печатает строку, когда
хоть что-то проваливается, чтобы вам не нужно было знать о существовании команды, чтобы это
обнаружить.
Чем это не является. Это вопрос, а не наблюдатель: пока процесс лежит, здесь никто ничего не замечает — по той же причине, по которой упавший процесс не может записать собственное падение. Он честно отвечает в тот миг, когда кто-нибудь спросит: оболочка, приложение, следующий запуск. Настоящему сторожу нужны собственные часы и собственная живучесть, а это отдельное решение и оно отслеживается как 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, потому что ответить — не провал.
Сердцебиение демона закрывает окно, которое задания закрыть не могут. При каждом тике демон записывает признак жизни в <CHIMERA_HOME>/scheduler/heartbeat.json (атомарно, рядом с jobs.json). Доктор читает его первым и отвечает на вопрос, который одни задания задать не могут: мёртвый демон с ежедневным заданием выглядит здоровым со стороны заданий ~23 часа — задание ещё не просрочено — пока сердцебиение останавливается в момент смерти демона. Вердикт выносится против трёх тиков интервала, записанного самим сердцебиением, и печатается вместе с использованным потолком — чтобы можно было спорить с числом, а не гадать, откуда оно. Три честных прочтения: alive/stale — вынесены; unknown — сердцебиение есть, но без записанного интервала тика (писал более старый код), так что свежесть не оценить, не выдумав число — и читатель выдумывать отказывается; 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(он в gitignore); никогда не запекайте их в образ. - Доступ снаружи: привязывайте шлюз к
0.0.0.0только за брандмауэром или обратным прокси. ЗадайтеCHIMERA_SERVER_TOKEN, чтобы требоватьAuthorization: Bearer <token>на HTTP-шлюзе и в API приложения (интерфейсу токен выдаётся автоматически только для клиентов с loopback, поэтому выставленный наружу экземпляр остаётся за вашей собственной аутентификацией). Аутентификация включается по желанию и по умолчанию пуста, так что без этой переменной её нет — ограничьте порт или откройте только путь вебхука. Как достучаться до этого экземпляра из приложения, смотрите в §5. - Песочница: задайте
CHIMERA_SANDBOX=docker, чтобы инструменты оболочки и выполнения кода работали в одноразовом контейнере, а не на хосте. - Выполнение на хосте без присмотра: с 2026-07-20 запуск без человека отказывает в командах на
хосте при значении по умолчанию
CHIMERA_HOST_EXEC=ask(подтверждать не на чем — терминала нет). Развёртывание, которому действительно нужно, чтобы агент выполнял оболочку на хосте, осознанно ставитCHIMERA_HOST_EXEC=allow; более безопасный вариант —CHIMERA_SANDBOX=docker, где эта проверка пропускается, потому что контейнер действительно изолирует. Точно так же сервер API взводит сужение по заражению (CHIMERA_TAINT_NARROW=1): после того как агент прочитал недоверенное содержимое, инструменты выполнения, записи и отправки наружу отказывают. Поставьте0, чтобы он продолжал действовать самостоятельно.
5. Как достучаться до этого экземпляра из приложения
Приложение по умолчанию говорит с той Chimera, которую само запускает на вашей машине. Начиная с v0.44 оно может указывать и на ту, которую вы держите сами, — на этот VPS, — и тогда приложение становится окном в агента, который и так всю ночь выполняет ваши задания.
Прочитайте эту часть, прежде чем открывать порт. То, что вы выставляете наружу, — не панель наблюдения. Каждый экран этого приложения — поверхность управления: он выполняет команды оболочки, правит файлы, раздаёт доску самостоятельных задач и меняет настройки. Экземпляр, доступный из интернета без токена, — это не «Chimera, на которую кто-то может посмотреть», а машина, на которой любой нашедший адрес может выполнять команды за счёт ваших ключей.
Должны выполняться три условия, и без первых двух приложение отказывается подключаться:
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, перезапустите и вставьте то же значение в приложение. Приложение отказывает
удалённому адресу без токена по причине выше: экземпляр без него открыт любому, кто найдёт адрес.
Обратите внимание, чего сервер намеренно не делает: когда удалённый клиент запрашивает интерфейс, страница отдаётся без токена. Токен никогда не выдаётся по сети — вы копируете его в свой клиент один раз, отдельным путём. Именно поэтому в приложении есть поле для него.
3 — Источник вашего приложения. Приложение обслуживается собственным местным помощником, поэтому его запросы к этому экземпляру идут с другого источника, и браузер отбрасывает ответы, пока этот экземпляр не назовёт тот источник:
CHIMERA_ALLOWED_ORIGINS=http://127.0.0.1:45813
Приложение показывает точное значение, когда подключение не удаётся, — оно прямо в сообщении об ошибке, готовое к копированию. Порт постоянен для установки (он запоминается между запусками с версии v0.43), так что это задаётся один раз на каждую машину, с которой вы подключаетесь. Несколько источников перечисляются через запятую.
Эта настройка не является границей безопасности, и читать её так нельзя. CORS решает, какая
страница может прочитать ответ, и ничего не решает о том, кто может вызывать. Воротами служит
токен. Назвать источник, не задав токен, ничего не защищает — это лишь делает незащищённый экземпляр
доступным из браузера в дополнение к curl.
По умолчанию пусто, поэтому экземпляр, который никто не настраивал, ведёт себя ровно как прежде.
Что приложение говорит при неудаче
- «Токен отклонён» — адрес и источник верны, а значение неправильное.
- «Не удалось достучаться» — либо адрес неверный, либо источник не разрешён. Браузер намеренно не даёт их различить, поэтому приложение называет оба и выдаёт вам источник, который нужно разрешить.
- Предупреждение о версии — приложение сравнивает версию своего сервера с этой и называет оба числа. Оно не отказывает: сервер на выпуск позади обычно работает, а отказ запер бы вас на том самом экране, который нужен, чтобы это исправить. Некоторые конечные точки на старой стороне могут отсутствовать.
Ещё безопаснее
Обойдитесь вовсе без публичного порта: дотянитесь до VPS через WireGuard или сеть Tailscale и направьте приложение на частный адрес. Токен всё равно важен — частная сеть это комната поменьше, а не пустая.
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.
Для shell или окружения 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 делает один настоящий вызов провайдера через настроенный маршрут и цепочку
сертификатов. Прошедшая проба подтверждает этого провайдера и этот вызов; она не удостоверяет каждую
интеграцию. По возможности не кладите учётные данные в URL прокси: значения окружения видны любому,
кто может посмотреть на процесс.
chimera api и chimera logs
Две команды терминала для работающего экземпляра, ни одна из которых не запускает сервер:
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 app по URL,
который приложение объявляет в CHIMERA_HOME; иначе использует http://127.0.0.1:8765, порт
chimera serve по умолчанию, чей шлюз отвечает на GET /health и POST /chat (/api там нет).
serve --discord/--telegram/--slack/--signal вообще не открывает HTTP-порт, так что в таком
развёртывании chimera api достучаться не до чего; chimera logs при этом работает. --url
переопределяет адрес. Вызов через loopback игнорирует переменные прокси выше, так что токен никогда
не попадает в прокси, а команда отказывается отправлять токен на удалённый URL http://. Каждый
запрос по-прежнему решают собственная авторизация и подтверждения сервера.
chimera logs читает CHIMERA_HOME/logs/backend.log: предупреждения и ошибки, записанные chimera serve и chimera app, с ротацией на 1 МБ и замаскированными секретами. Traceback остаётся при
записи, к которой относится, под --level и --run, а --follow продолжает чтение через ротацию.
7. Честное положение дел
Chimera находится в стадии альфа. Это разворачивается и работает, а демон расписания делает её
действующей по собственному почину. Её опыт промышленной эксплуатации — одно развёртывание:
собственный VPS автора, где с июля 2026 года круглосуточно работают задания по расписанию и бот
Discord. Это реальное использование, а не парк машин, и с вашей нагрузкой оно не встречалось.
Начинайте с заданий, где ставки невелики, следите за logs и держите в уме меры управления
(--guard у solve, CHIMERA_SANDBOX=docker) для всего, что касается реальных систем.
Где публикуются эти страницы
Эти файлы — исходник документации на chimeraagent.space, которая отрисовывает их прямо из этого каталога во время сборки. Правьте markdown здесь, и сайт последует за вами; второй копии, которую нужно держать в согласии, нет.
Конфигурация MkDocs, которая раньше лежала в mkdocs.yml, удалена. Она была полной — тема,
навигация, десять страниц — и её никогда не публиковали: не было ни рабочего процесса, ни ветки
gh-pages, поэтому инструкции по развёртыванию, стоявшие на этом месте, описывали сайт, которого не
существовало. Конфигурация, которую никто не запускает, хуже её отсутствия, потому что следующий
человек правит её навигацию и не может понять, почему ничего не меняется.