[2026-06-16] balda: sync setup.md with current state; fix claudio owner note; correct zulip-router plan for balda bots

This commit is contained in:
Alexey Martemyanov
2026-06-16 13:09:28 +06:00
parent fd9cc805cc
commit 6cf9ba0989
3 changed files with 127 additions and 219 deletions
+56 -52
View File
@@ -1,34 +1,40 @@
---
tags: [project, hermes, zulip, go, infra]
status: deployed
created: 2026-06-16
---
# Zulip Router
_Последнее обновление: 2026-06-16_
## Цель
Event-роутер между Zulip и несколькими Hermes-инстансами.
Решает проблему конкурентного получения событий (Валера, Клавдий и другие боты видели все сообщения одновременно, `reset` срабатывал у всех).
Event-роутер между Zulip и balda-ботами (Валера, Клавдий).
Решает проблему конкурентного получения событий — сейчас оба бота через Events API видят все сообщения одновременно.
## Проблема (root cause)
- Каждый Hermes-инстанс в polling-режиме регистрирует собственную event queue в Zulip
- `reset` — системная команда Hermes, обрабатывается до хука `whale-thread-guard`
- Нет механизма ownership топика между инстансами
- Каждый balda-бот регистрирует собственную event queue в Zulip Events API
- Без роутера оба получают все сообщения, и каждый может ответить на @mention другого
- `extractOtherMention()` патч в коде — костыль, не решает проблему полностью
## Архитектура
## Как сейчас (до роутера)
```
Zulip
├── Events API ← Валера (valera-bot, poll на :8091)
└── Events API ← Клавдий (claudio-bot, poll на :8092)
└── Webhook → оба (outgoing webhook bot_type=3 через @mention)
```
## Архитектура (с роутером)
```
Zulip Event Queue
↓ poll (valera-bot credentials)
[zulip-router]
├── @Валера → http://hermes-valera:8765/zulip-webhook
── @Клавдий → http://hermes-klavdiy:8765/zulip-webhook
↓ poll (отдельный бот-роутер или reuse одного из существующих)
[zulip-router] (~/Docker/zulip-router/)
├── @Валера / @Valera → POST http://balda-agent-valera-1:8091/zulip/webhook
── @Клавдий / @Klavdiy → POST http://claudio-agent-claudio-1:8092/zulip/webhook
└── (default / unknown) → POST http://balda-agent-valera-1:8091/zulip/webhook
↕ ownership: stream+topic → bot (TTL 24h, persisted to /data/ownership.json)
```
Hermes/Орёл — не трогаем, остаётся в polling-режиме.
**Важно:** таргеты — не Hermes инстансы, а balda-боты (Go бинарники). У каждого уже есть webhook endpoint `/zulip/webhook`.
## Правила маршрутизации
@@ -36,11 +42,24 @@ Hermes/Орёл — не трогаем, остаётся в polling-режим
2. `@Клавдий` или `@Klavdiy` в тексте → Клавдий (+ ownership обновляется)
3. Топик уже у кого-то → туда же (TTL refreshed)
4. Новый топик без @mention → Валера (default)
5. `reset` без @mention уходит только к владельцу топика
5. `reset` без @mention → только владельцу топика
## Что меняется в balda
После деплоя роутера у обоих ботов нужно **отключить Events API polling** в config.yaml:
```yaml
zulip:
events_polling:
enabled: false # ← было true
```
После этого сообщения приходят **только через webhook** (от роутера).
Перезапуск: `docker-compose up -d` (в каждом compose).
## Код
`~/docker/zulip-router/` — Go, ~260 LOC
`~/Docker/zulip-router/` — Go, ~260 LOC (план)
| Файл | Назначение |
|------|------------|
@@ -48,47 +67,32 @@ Hermes/Орёл — не трогаем, остаётся в polling-режим
| `config.go` | Config struct + YAML loading + env var expansion |
| `zulip.go` | Zulip API client (register_queue, get_events, long-poll) |
| `ownership.go` | Topic ownership store (RW mutex + JSON persistence) |
| `forwarder.go` | HTTP POST к Hermes webhook endpoints |
| `forwarder.go` | HTTP POST к balda webhook endpoints |
## Deployment
**Compose:** `~/docker/hermes/docker-compose.yml` — сервис `zulip-router`
**Compose:** `~/Docker/zulip-router/docker-compose.yaml`
**Env vars** (`~/docker/hermes/.env`):
```
ZULIP_ROUTER_API_KEY=<valera-bot api key из ~/.hermes/profiles/valera/config.yaml>
ZULIP_WEBHOOK_TOKEN=zr-secret-2026
**Env vars:**
```env
ZULIP_ROUTER_API_KEY=<api-key бота-роутера>
```
**Запуск после заполнения .env:**
**Запуск:**
```bash
cd ~/docker/hermes
docker compose up -d --force-recreate
cd ~/Docker/zulip-router
docker-compose up -d
```
**Перезапуск после изменений в профилях:**
```bash
docker compose restart hermes-valera hermes-klavdiy
```
## Hermes профили
Валера и Клавдий переведены в webhook-режим:
```yaml
platforms:
zulip:
webhook_port: 8765
webhook_path: /zulip-webhook
webhook_token: 'zr-secret-2026'
```
Токен должен совпадать с `ZULIP_WEBHOOK_TOKEN` в `.env`.
## TODO
- [ ] Заполнить ZULIP_ROUTER_API_KEY в ~/docker/hermes/.env
- [ ] `docker compose up -d --force-recreate`
- [ ] Проверить логи: `docker compose logs -f zulip-router`
- [ ] Протестировать: написать @Клавдий и @Валера в отдельных топиках
- [ ] Проверить ownership: обратиться в топик без @mention — должен отвечать последний бот
- [ ] При необходимости: добавить Hermes/Орёл в роутер (отдельная задача)
- [ ] Написать код роутера (~260 LOC)
- [ ] Создать compose `~/Docker/zulip-router/docker-compose.yaml`
- [ ] Создать бота-роутера в Zulip (или реюзнуть valera-bot/claudio-bot), получить API key
- [ ] `docker-compose up -d`
- [ ] Проверить логи: `docker logs zulip-router --tail 50`
- [ ] Отключить `events_polling.enabled` у Валеры → `docker-compose up -d`
- [ ] Отключить `events_polling.enabled` у Клавдия → `docker-compose up -d`
- [ ] Протестировать: @Клавдий и @Валера в разных топиках
- [ ] Проверить ownership: сообщение в топик без @mention → последний бот
- [ ] Удалить `extractOtherMention()` патч из кода (больше не нужен)