---
work_item: ORCH-100
stage: architecture
author_agent: architect
status: proposed
created_at: 2026-06-10
model_used: claude-opus-4-8
---

# 07 — Инфра-требования: ORCH-100 — FND/F1b: sidecar-watchdog

Work Item: **ORCH-100** · Repo: **orchestrator** · Стадия: architecture

> When-applicable: топология **меняется** (новый рантайм-контейнер). Разовое инфра-действие выполняет
> человек (Слава/Стрим) на хосте mva154; дальше код `watchdog/` катится через конвейер (self-hosting).

## I-1. Топология / окружения

Новый сервис `orchestrator-watchdog` в `docker-compose.yml` — **отдельный контейнер** рядом с
`orchestrator` (8500) и `orchestrator-staging` (8501, profile staging).
- **Образ:** `build: ./watchdog` (`watchdog/Dockerfile`, `python:3.12-slim`, stdlib-only).
- **Сеть:** `network_mode: host` — достаёт `/metrics` орка как `http://127.0.0.1:8500/metrics` и
  хост-интерфейсы (ADR-001 D2).
- **Тома (все read-only к наблюдаемому, NFR-4):**
  - `/var/run/docker.sock:/var/run/docker.sock:ro` — статусы контейнеров (GET-only).
  - `/home/slin/repos:/repos:ro` и `./data:/app/data:ro` (или эквивалент) — дисковые метрики хоста
    через `shutil.disk_usage` (те же пути, что у `disk_watchdog`).
- **Лимиты:** `mem_limit: 128m` + `mem_reservation: 32m` (тонкость измерима/принудительна, NFR-2);
  `restart: unless-stopped` (самовосстановление, FR-1).
- **Kill-switch:** `WATCHDOG_ENABLED` (env). `false` → демон инертен (idle-loop, не exit — чтобы
  `restart` не крутил петлю), нулевой эффект на орк.
- **Контейнеры под наблюдением (BR-4):** минимум `orchestrator`; список `WATCHDOG_CONTAINERS` (CSV).
- **Образец сервиса (ориентир для developer; точные пути сверить с актуальным `docker-compose.yml`):**
  ```yaml
  orchestrator-watchdog:
    build: ./watchdog
    container_name: orchestrator-watchdog
    restart: unless-stopped
    network_mode: host
    mem_limit: 128m
    mem_reservation: 32m
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /home/slin/repos:/repos:ro
      - ./data:/app/data:ro
    env_file: .env.watchdog        # ЛИБО общий .env с префиксом WATCHDOG_ (деталь — developer/оператор)
    group_add: ["999"]             # docker-группа для чтения docker.sock (как у орка)
  ```

## I-2. Переменные окружения / секреты

Канон (без секретов) — в `.env.example` (TRZ §2). Префикс `WATCHDOG_` (изоляция от `ORCH_`):
- **Секреты (только на хосте, в гит НЕ коммитятся):** `WATCHDOG_TG_BOT_TOKEN`, `WATCHDOG_TG_CHAT_ID`
  — **собственные** bot/chat sidecar, независимые от Telegram орка (BR-8). Отсутствие → sidecar
  логирует и не шлёт (fail-safe), но не падает.
- **Управление:** `WATCHDOG_ENABLED` (kill-switch), `WATCHDOG_INTERVAL_S` (дефолт 60),
  `WATCHDOG_ORCH_METRICS_URL` (дефолт `http://127.0.0.1:8500/metrics`).
- **Пороги/таймауты (дефолты — ADR-001 D5):** `WATCHDOG_ORCH_DOWN_TICKS=3`, `WATCHDOG_MEM_PCT=90`,
  `WATCHDOG_DISK_CRIT_ENABLED=false`, `WATCHDOG_DISK_CRIT_PCT=97`, `WATCHDOG_AGENT_HUNG_MIN=20`,
  `WATCHDOG_AGENT_CPU_FLOOR=0.01`, `WATCHDOG_STAGE_STUCK_MIN=120`, `WATCHDOG_QUEUE_DEPTH=20`,
  `WATCHDOG_COOLDOWN_S` (анти-спам realert), `WATCHDOG_HTTP_TIMEOUT_S`.
- **Цели:** `WATCHDOG_CONTAINERS` (CSV, дефолт `orchestrator`), `WATCHDOG_DEP_PLANE_URL`/
  `WATCHDOG_DEP_GITEA_URL`/`WATCHDOG_DEP_ANTHROPIC_URL` (health/ping).

> Анти-дубль диск-алерта (ADR-001 D6): штатный 85%-алерт остаётся за внутренним `disk_watchdog`
> (ORCH-063). `WATCHDOG_DISK_CRIT_ENABLED` по умолчанию `false` — sidecar НЕ дублирует диск, пока
> оператор осознанно не включит независимый критический потолок.

## I-3. Деплой / рестарт

- **Разовое действие человеком на хосте (Слава/Стрим):**
  1. Создать **отдельного** Telegram-бота watchdog + получить chat-id; положить `WATCHDOG_TG_*` в
     `.env.watchdog` (или `.env`) на хосте.
  2. Заполнить пороги/интервалы (дефолты годятся), включить `WATCHDOG_ENABLED=true`.
  3. Добавить сервис в `docker-compose.yml` (приходит с PR) и поднять **только его:**
     `docker compose up -d --build orchestrator-watchdog`.
- **Self-hosting инвариант (критично):** поднятие/пересборка `orchestrator-watchdog` **НЕ** трогает
  прод-контейнер `orchestrator` (отдельный сервис) — конвейер всех проектов не прерывается. **НЕ**
  выполнять `docker compose up -d` без явного имени сервиса, если это спровоцирует рекреейт орка.
- **Прод-выкат кода watchdog** — через штатный self-hosting-конвейер и **обязательный staging-гейт
  (8501)** перед прод-деплоем; деплой sidecar не рестартит прод-контейнер орка.
- **Проверка после старта (NFR-7):** `docker logs orchestrator-watchdog` показывает старт + тики;
  тестовый алерт приходит в канал watchdog; остановка орка (на staging) → приходит `orch_down`.

## I-4. CI/CD

- Без изменений `.gitea/workflows/` по существу: новые тесты sidecar (`watchdog/tests/` или
  `tests/watchdog/`) подхватываются существующим `pytest tests/`/прогоном (изолированы, чистые
  функции — без контейнера/таймера). Если выбран отдельный путь `watchdog/tests/`, developer
  обеспечивает его включение в существующий тест-ран (без нового workflow-файла).
- Docker-сборка нового образа — стандартным `docker compose build` (отдельный `watchdog/Dockerfile`),
  без правок пайплайна CI.
</content>
