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

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

## 0. Подтвердить у владельца до старта

1. **Хост** — VM на libvirt или существующий VPS (открытый вопрос №1 CLAUDE.md).
2. **Домен** — synapse.gnexus.space? (публичный вход через nginx gnexus.space,
   внутренняя сеть 192.168.1.0/24).
3. **Telegram-бот** — новый или существующий токен (вопрос №2; слот
   `telegram_bot_token` создаётся через админку, канал #29).
4. **OAuth-клиент в gnexus-auth** — зарегистрирован ли; если нет, кто создаёт
   (владелец на сервере auth или агент с доступом к админке auth). Понадобятся:
   redirect URI = `https://<домен>/auth/callback`, скоупы
   `openid email profile roles permissions`, вебхук `auth.logout` /
   `auth.global_logout` / `session.revoked` → `GAUTH_WEBHOOK_SECRET`.

## 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. Код и окружение

```bash
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 (шаг 0.4) |
| `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. Подъём

```bash
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-стримов:

```nginx
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. 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).

## 6. 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 просто недоступен) — можно отложить.

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

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

```bash
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; телеграм — см. вопрос №2) → `rule_create` →
`send_test_event` → `deliveries_list` (статус delivered).
Definition of Done (CLAUDE.md): приём 202 → доставка ≥ 2 каналов →
лог доставки в БД; s2s в Navi — по задаче #28.

## 8. После деплоя

- Задокументировать сервис в 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.

## Откат

```bash
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)
```