Перейти к содержимому

Развёртывание 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, поэтому инструкции по развёртыванию, стоявшие на этом месте, описывали сайт, которого не существовало. Конфигурация, которую никто не запускает, хуже её отсутствия, потому что следующий человек правит её навигацию и не может понять, почему ничего не меняется.

Изменить эту страницу на GitHub