Newer
Older
gn-synapse / docs / 08-deploy-runbook.md

08 · Рантбук развёртывания Synapse (для ИИ-агента)

Пошаговая инструкция для ИИ-агента, разворачивающего Synapse на проде. Агент работает на целевом хосте с shell-доступом (docker, git). Основные развилки уже решены владельцем (§0); всё, что сверх этого, не описано документом и .env-примером — подтверждать у владельца, не выбирать молча (см. открытые вопросы в CLAUDE.md).

0. Решения владельца (2026-10-04, зафиксированы)

  1. Хост — целевая машина, на которой работает агент (VM/VPS-развилка не важна); рантбук рассчитан на любой docker-хост.
  2. Домен — synapse.gnexus.space, публичный вход через nginx gnexus.space, TLS по его сертификату (§4).
  3. Telegram-бот — отложен: канал добавляется позже (задача #29); слот telegram_bot_token тогда создадут через админку (Настройки). Не искать токен, не ставить в .env.
  4. OAuth-клиент в gnexus-auth — владелец регистрирует в процессе настройки, после развёртывания (§5). Стек поднимается и без него: healthz/приём событий/воркер работают, SPA-вход — после §5.

1. Предполёт (checklist)

  • docker + docker compose v2 на целевом хосте.
  • git-доступ к git.gnexus.space/git/root/gn-synapse.git.
  • DNS/hosts: домен Synapse резолвится; gnexus-auth доступен с хоста
    (прод-URL — в `GAUTH_BASE_URL`, сейчас 192.168.1.167).
  • Свободный порт на хосте (SYNAPSE_PORT, дефолт 8013). Публично
    сервится nginx'ом gnexus.space на 80/443.
  • На хосте есть место под том postgres_data (основное хранилище, см.
    docs/04-database.md).

2. Код и окружение

git clone https://git.gnexus.space/git/root/gn-synapse.git gnsynapse
cd gnsynapse
cp .env.example .env

Полный список переменных — в .env.example (он же шаблон; там комментарии по каждой). Обязательные к заполнению:

Переменная Откуда значение
POSTGRES_PASSWORD, DATABASE_URL openssl rand -hex 16 в пароль (одинаковый в обоих полях)
GAUTH_BASE_URL URL prod-gnexus-auth (192.168.1.167), согласовать схему (http/https)
GAUTH_CLIENT_ID / GAUTH_CLIENT_SECRET зарегистрированный OAuth-клиент в gnexus-auth (§5; до этого можно оставить placeholder — вход в SPA не работает, стек работает)
GAUTH_REDIRECT_URI https://<домен>/auth/callback
SPA_PUBLIC_URL пусто (тот же origin — SPA раздаётся этим же контейнером, docs/06)
GAUTH_WEBHOOK_SECRET секрет вебхука клиента в gnexus-auth (показывается один раз)
MCP_TOKEN openssl rand -hex 32 (пусто → MCP выключен; токен = как API-ключ)
SYNAPSE_PORT порт API на хосте (дефолт 8013)
S2S_SECRET_* для каждой s2s-цели (Navi): openssl rand -hex 16; channel_targets.config.token_ref ссылается на имя переменной (docs/05)

Секреты (пароль БД, GAUTH_CLIENT_SECRET, GAUTH_WEBHOOK_SECRET, MCP_TOKEN, S2S_SECRET_*) — скопировать в репозиторий gnexus-creds.

3. Подъём

docker compose up -d --build
  • Entrypoint api сам применяет миграции Alembic. Проверка: docker compose exec api alembic current → головная ревизия (см. последнюю в alembic/versions/).
  • curl localhost:8013/api/healthz → {"status": "ok"}.
  • Воркер жив: docker compose exec redis redis-cli ping и логи docker compose logs worker | grep -i ready.
  • SPA открылась: curl -s localhost:8013/ | head -c 200 (index.html).

4. Публичный вход (nginx gnexus.space)

Location на gnexus.space nginx → proxy на <host>:8013, TLS по сертификату домена. Важно для MCP и SSE-стримов:

location / {
    proxy_pass http://<synapse-host>:8013;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_buffering off;   # SSE (/mcp) и стриминг без буферизации
    proxy_read_timeout 300s;
}

Smoke: https://<домен>/ открывает SPA, GET /api/healthz зелёный. GET /mcp без токена → 401 (MCP включён) или 200 index (не включён) — не 500.

5. OAuth-клиент в gnexus-auth (владелец, после подъёма)

Стек уже живёт; вход в SPA включен этим шагом. Попросить владельца зарегистрировать в gnexus-auth приложение Synapse с настройками:

  • redirect URI: https://synapse.gnexus.space/auth/callback
  • скоупы: openid email profile roles permissions
  • вебхуки: auth.logout, auth.global_logout, session.revoked → endpoint https://synapse.gnexus.space/auth/webhooks (маршрут POST /auth/webhooks, секрет = значение ниже)
  • из формы выдачи взять: GAUTH_CLIENT_ID, GAUTH_CLIENT_SECRET, GAUTH_WEBHOOK_SECRET (секрет показывается один раз) → в .env и в gnexus-creds.

Затем перезаписать .env и перечитать конфиг:

docker compose up -d   # api пересоздаётся с новыми GAUTH_*

Проверка: curl -s localhost:8013/auth/login -o /dev/null -w '%{http_code}\n' → 302 на authorize gnexus-auth (до §5 будет ошибка/401 — это ок).

6. SSO smoke

  1. Открыть https://<домен>/ → редирект на gnexus-auth → логин владельца (роль admin/superadmin) → колбэк → дашборд.
  2. Пользователь роли user → попадает в личный раздел «Мои события», в админку не пускается ни сервером, ни роутером.
  3. Логаут (POST /auth/logout + вебхук из gnexus-auth) завершает сессию: через ~45 сек SPA после 401 уходит на /login (single sign-out, CLAUDE.md).

7. Push-канал (если включаем сразу)

  1. Сгенерировать пару VAPID по рецепту docs/06-settings-pwa.md («Как задать VAPID»): docker compose exec api python3 - <<PY ... (скрипт в доке, проверен; py_vapid уже в контейнере как зависимость pywebpush).
  2. Внести через админку (Настройки → Web Push): vapid_public_key, vapid_private_key (секрет, write-only), push_subject (mailto:ops@gnexus.space).
  3. Публичный и приватный ключи — копия в gnexus-creds.
  4. Smoke: тумблер «Получать пуши» в разделе Настройки подписывает браузер, send_test_event (MCP) по правилу с каналом push доставляет пуши.
  5. Без этого шага всё работает (push просто недоступен) — можно отложить.

8. MCP: включение и приёмка

MCP_TOKEN в .env уже включает /mcp при подъёме. Регистрация клиента:

claude mcp add --transport http synapse https://<домен>/mcp \
  --header "Authorization: Bearer <MCP_TOKEN>"

Приёмка конвейера целиком — через MCP-тулы (docs/07-mcp.md, каталог 31 тул): system_status → source_create → key_issue (plaintext один раз — сразу передать владельцу в его .env/gnexus-creds) → type_register → target_create (internal_log; телеграм-цель — отложен: не создавать, #29) → rule_create → send_test_event → deliveries_list (статус delivered). Definition of Done (CLAUDE.md): приём 202 → доставка ≥ 2 каналов → лог доставки в БД; s2s в Navi — по задаче #28.

9. После деплоя

  • Задокументировать сервис в gnexus-book (через MCP-инструменты).
  • Статусы задач gntodo (#27 тестирование и деплой → сделано; заметки о нештатных решениях — в описания задач).
  • Перед каждым будущим обновлением — pg_dump тома/БД (откат миграций вниз не поддерживается всеми ревизиями — опора на дамп).

Ловушки (известные)

  • Миграции: применяются автоматически при старте api; если alembic current не доехал до головы — смотреть docker compose logs api.
  • MCP_TOKEN пуст → /mcp просто нет: GET отдаёт SPA, POST — 405. Это не поломка — так выглядит выключенный MCP.
  • TLS gnexus-auth: в LAN self-signed → GAUTH_VERIFY_TLS=false; в проде с нормальным сертификатом — true обязательно.
  • Токены opaque: каждый запрос API ходит «call home» в gnexus-auth — недоступность auth-сервера = недоступен весь Synapse, держать в мониторинге.
  • Секреты s2s: доставка в Navi требует и цели (channel_targets.config с token_ref), и переменной S2S_SECRET_<REF> в .env с тем же суффиксом.
  • Порт: SYNAPSE_PORT меняет только наружный порт хоста; внутри всё на 8000.

Откат

docker compose exec postgres pg_dump -U synapse synapse > backup-$(date +%F).sql
git -C gnsynapse checkout <предыдущий коммит>
docker compose up -d --build   # (восстановление из дампа при необходимости:
                               #  docker compose exec -T postgres psql -U synapse synapse < backup-...sql)