Сейчас SAC — **односторонний** канал: агент → ingest. Для задач из [roadmap.md](roadmap.md) (§ «Добавить») нужен **обратный канал** SAC ↔ агент и управление хостами:
**Эталонный git для prod и агентов:** [git.kalinamall.ru/PapaTramp](https://git.kalinamall.ru/PapaTramp). GitHub — публичная sanitized-копия; mirror-скрипты (`push-mirror.sh`, `Rewrite-GitHostUrls.ps1`) живут на kalinamall и нужны только для синхронизации URL в docs между remotes, к runtime агентов не относятся.
---
## 2. Общая архитектура
```mermaid
flowchart TB
subgraph sac [SAC Ubuntu]
API[FastAPI]
UI[Vue SPA]
PG[(PostgreSQL)]
VAULT[Encrypted secrets]
end
subgraph win [Windows host]
RDP[RDP-login-monitor]
end
subgraph linux [Linux host]
SSH[ssh-monitor]
end
RDP -->|POST events / heartbeat| API
SSH -->|POST events / heartbeat| API
RDP -->|GET agent/commands poll| API
SSH -->|GET agent/commands poll| API
UI --> API
API --> PG
API --> VAULT
```
**Принципы:**
- Агент за NAT — только **исходящий HTTPS**; inbound на хост не требуется.
- Команды и конфиг — **poll** на heartbeat (или отдельный timer 30–60 с).
- Секреты (admin password, SSH private keys, WinRM password) — **encrypted at rest** в PostgreSQL; master key в `SAC_HOST_KEY_ENCRYPTION_KEY` (env).
- Audit: все команды и результаты — события ingest `agent.command.*`, `agent.update.*`.
---
## 3. П.1 — RDG flap 302→303 и qwinsta / logoff
### 3.1. Смысл проблемы
При неудачном RDP через RD Gateway типична пара событий Windows **302 → 303** за несколько секунд. Клиент не удерживает сессию; **«зависшая» сессия обычно остаётся на компьютере пользователя** (session host / рабочая станция из `details.internal_ip`), а не на gateway.
SAC уже принимает:
| Windows | SAC `type` | Пример severity |
|---------|------------|-----------------|
| 302 | `rdg.connection.success` | info / warning |
| 303 | `rdg.connection.disconnected` | info |
| 303 (alt) | `rdg.connection.failed` | warning |
Правило должно учитывать **оба** варианта 303.
### 3.2. Правило детекта `rule:rdg_session_flap`
```
WHEN event_B.type IN (rdg.connection.disconnected, rdg.connection.failed)
AND exists event_A within 1..10 seconds BEFORE event_B
AND event_A.type = rdg.connection.success
AND same host_id
AND same details.user
AND (recommended) same details.internal_ip
OPTIONAL reduce false positives:
AND event_B.details.session_duration_sec <= 15 # если агент передаёт из 303
AND/OR low bytes transferred in 303 payload
THEN:
mark event_B.details.rdg_flap = true
create Problem rule:rdg_session_flap (dedup 30 s, см. ниже)
send notification (Telegram / email / webhook по policy)
```
**Порядок:** только **302 → 303**, не наоборот.
**Окно:****1–10 секунд** между 302 и 303.
**Dedup Problem:****30 секунд** по ключу `{host_id}|{user}|{internal_ip}`. Пользователи часто пробуют 1–2 раза подряд и звонят админам — 60 с слишком долго.
**Хост:** правило **универсальное** для любого хоста с RDG-событиями в домене (любой DC / gateway / session host, где стоит агент).
**Маршрутизация команды qwinsta:** команда уходит на хост, где **установлен агент** и куда привязано событие (`host_id`). Если session host ≠ gateway, в перспективе — таблица «gateway → session hosts» или агент на session host; MVP — агент на том же сервере, что шлёт события RDG.
### 3.3. UI — «Обзор» → «Последние события»
В таблице [DashboardView](../frontend/src/views/DashboardView.vue) для строки события с `rdg_flap = true` (обычно **303**, второе в паре):
| … | Title | **Действия** |
|---|-------|--------------|
| … | RD Gateway event 303 | **[qwinsta]** |
**Поток:**
1. Оператор нажимает **qwinsta**.
2.`POST /api/v1/events/{id}/actions/qwinsta` → SAC ставит команду в очередь хоста.
3. Агент на poll выполняет `qwinsta` под **доменным admin** (см. §5).
4. Результат → модальное окно: таблица SESSIONNAME, USERNAME, ID, STATE.
5.**logoff:**
- по умолчанию — кнопка только у строк с **matching user** из события;
- дополнительно — возможность **выбрать любую строку** (на серверах бывает нестандартный вывод qwinsta).
Если за **N минут** нет `agent.update.*` или статус `failed`:
| OS | Метод | Credentials |
|----|--------|-------------|
| Linux | SSH из SAC | ключ после bootstrap (§5) |
| Windows | WinRM | encrypted login/password в БД |
---
## 5. П.2C — Доступ SAC к хостам
### 5.1. Windows — WinRM
- **Один доменный admin** в **Настройки → Управление хостами → Windows**: `DOMAIN\user` + password (encrypted).
- Override на карточке хоста — опционально позже.
- Используется для fallback-обновления и (при необходимости) remote ops; для qwinsta/logoff MVP — **через агента** с теми же creds, переданными в command poll (TLS + API key, не пишутся на диск агента).
| ssh-monitor | mirror-скрипты + URLs | без mirror-скриптов |
Mirror-скрипты: временно переписывают URL в docs под целевой host, пушат на remote, **откатывают** локальный main — чтобы в одной ветке не смешивать kalinamall/github URLs.
- При парах событий 302/303 за период 5-10 сек отправлять на агентский компьютер qwinsta / logoff для "зависшей" или сбрасывающей коннект сессии пользователя.
- Устанавливать новые версии агентов на целевые компьютеры помипо GPO и самим SAC.
-В разделе "Хосты" в карточке каждого компьютера сделать возможность настройки параметров агента/монитора и передачи этих настроек из SAC в агента
См. [agent-control-plane.md](agent-control-plane.md):
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.