# Архитектура и Протокол Конфедерации (Federation Protocol)

Спецификация описывает механизмы взаимодействия независимых узлов (серверов) Knot Messager для обмена сообщениями, соблюдения политик безопасности и проксирования медиаконтента.

---

## 1. Основные принципы (Design Principles)

1.  **Делегированное Хранение (Remote Proxy Storage)**: Медиафайлы (фото, видео, голосовые) хранятся **только на сервере отправителя**. Сервер получателя хранит лишь "прокси-ссылку" на объект.
2.  **Сквозная Валидация (Cross-Server Policy)**: Если на сервере получателя отключены голосовые сообщения или опросы, сервер отправителя блокирует их отправку в этот федеративный чат на этапе валидации (до сетевого запроса).
3.  **Безопасный Handshake (RSA-2048)**: Узлы доверяют друг другу на основе обмена публичными ключами и подписи каждого входящего пакета (HMAC/RSA).
4.  **Zero-Knowledge Integrity**: Ключи шифрования вложений (IV) передаются получателю в зашифрованном виде (на его публичный ключ), чтобы обеспечивался E2EE даже при проксировании контента.

---

## 2. Протокол взаимодействия (Federation API)

Все запросы между серверами используют префикс `/api/federation/v1/*` и подписываются заголовком `X-Knot-Signature`.

### 2.1 Handshake (Установка связи)
При добавлении домена в `Admin Module`, сервер инициирует [HandshakeRequest](file:///e:/GIT/forkmessager/backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs#16-17):
*   **Передает**: Свой домен, публичный ключ, список поддерживаемых фич (AllowMedia, AllowPolls и т.д.).
*   **Получает**: Аналогичные данные от удаленного сервера.
*   **Результат**: Создается пара "Доверенный узел" с кешированной политикой возможностей.

### 2.2 Доставка Сообщений (`PushMessage`)
Когда сообщение отправляется в чат с федеративным участником:
1.  Локальный сервер проверяет кэш политики удаленного сервера.
2.  Если тип сообщения разрешен удаленным сервером, формируется `FederationPacket`:
    ```json
    {
      "sender": "user@domain-a.com",
      "chatId": "shared-uuid",
      "payload": { "type": "text", "content": "..." },
      "signature": "rsa-sig-body"
    }
    ```
3.  Для медиа-сообщений в `payload` передается `ProxyUrl` вместо прямого вложения: `https://domain-a.com/api/federation/v1/proxy/{objectId}`.

### 2.3 Discovery & Health Check (Ping)
Для поддержания актуального статуса сети используется фоновый мониторинг (Keep-Alive):
*   **Endpoint**: `GET /api/federation/v1/ping`.
*   **Механика**: Каждый узел раз в 2-5 минут опрашивает (пингует) известные ему домены из `AllowedDomains`.
*   **Статусы**: 
    *   `Online` — Сервер доступен.
    *   `Maintenance` (503) — Временно недоступен (повтор через 10 мин).
    *   `Disabled` (403) — Конфедерация на той стороне выключена принудительно.
*   **Последствия**: Если сервер `Offline`, исходящие сообщения в его сторону ставятся в локальную очередь `FederationOutbox`.

### 2.4 Global User Search (Поиск пользователей)
Поиск осуществляется по формату `username@domain.com`:
1.  Если в строке поиска есть `@`, локальный сервер делает запрос к удаленному: `GET /api/federation/v1/users/search?q={username}`.
2.  Удаленный сервер возвращает публичные данные (DisplayName, AvatarProxyUrl).
3.  **Важно**: Поиск анонимен. Удаленный сервер отдает данные только если у пользователя в настройках разрешен публичный поиск через федерацию.

### 2.5 Federated Stories (Истории между серверами)
Когда пользователь хочет посмотреть истории контакта с другого сервера:
1.  **Request**: Локальный сервер запрашивает актуальный список историй по ID пользователя: `GET /api/federation/v1/stories/user/{userId}`.
2.  **Response**: Удаленный сервер возвращает список метаданных историй (ID, Type, ProxyMediaUrl).
3.  **Proxying**: Сами файлы историй (видео/фото) проксируются через тот же механизм `Remote Proxy Storage`, что и вложения в сообщениях.
4.  **Views Logic**: Фиксация просмотра (`view_story`) также передается через федеративный пакет, чтобы автор на удаленном сервере видел счетчик просмотров правильно.

### 2.6 Remote Presence & Statuses (Синхронизация присутствия)
1.  **Status Sync**: При получении нового сообщения от контакта с другого сервера, поле `LastSeen` для этого контакта локально обновляется.
2.  **Explicit Query (Пинг статуса)**: При открытии чата с удаленным пользователем, локальный сервер может сделать запрос `GET /api/federation/v1/users/{id}/status`.
3.  **Response**: Возвращает `Online = true/false` и `LastSeen = timestamp`.
4.  **Privacy**: Если на удаленном сервере пользователь скрыл статус "Был в сети", сервер вернет пустой `LastSeen`.

### 2.7 Federated RTC (Звонки и Шаринг)
WebRTC соединение между пользователями разных серверов требует координации:
1.  **Signaling Relay**: Серверы А и Б выступают промежуточными узлами (Relay) для обмена Offer/Answer/ICE кандидатами через федеративный канал.
2.  **Policy Check**: Перед инициацией звонка сервер А запрашивает у сервера Б: `CanCall(userB)`. Если у сервера Б выключены `VoiceCalls` или `ScreenSharing`, сервер А блокирует кнопку звонка в клиенте.
3.  **TURN Proxy**: Для обхода NAT в федерации рекомендуется использовать TURN-серверы, указанные в [WebRtcConfig](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs#58-69).

### 2.8 Real-time & Interactivity (Реакции и Ответы)
Любое действие с сообщением должно быть доставлено удаленному узлу:
*   **Реакции**: Пакет `MessageReactionChanged` с указанием `emoji` и `messageId`.
*   **Ответы на Истории**: Пакет `StoryResponse` с типом (Reply/Reaction) и метаданными истории.
*   **Удаление у всех (Delete for Everyone)**: Пакет `MessageDeleted`. Сервер получателя обязан удалить локальную копию сообщения при получении подписанного пакета от сервера-владельца.
*   **Редактирование (Edit)**: Пакет `MessageUpdated` с новым телом сообщения. Применяется локально на сервере получателя.

---

## 3. Remote Proxy Storage (Проксирование медиа)

## 3. Remote Proxy Storage (Проксирование медиа)

Это критическая часть системы, позволяющая не дублировать файлы на чужих серверах.

### Процесс скачивания файла пользователем Сервера B (отправленного Сервером A):
1.  Клиент на Сервере B запрашивает файл через локальный API: `GET /api/files/{remoteId}`.
2.  Модуль [Storage](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Storage/IFileStorageService.cs#6-26) сервера B видит, что файл является "внешним" (External).
3.  Сервер B делает внутренний авторизованный запрос к Серверу A: `GET /api/federation/v1/proxy/{objectId}`.
4.  Сервер A стримит байты зашифрованного файла Серверу B.
5.  Сервер B на лету расшифровывает контент (используя полученный ранее IV) и отдает клиенту.

**Важно:** Сервер B никогда не сохраняет файл локально на постоянной основе (только в In-Memory кэш для скорости раздачи).

---

## 4. Групповые чаты (Fan-out Architecture)

В Knot Messager групповые чаты децентрализованы.

1.  **Рассылка (Fan-out)**: При отправке сообщения в группу, локальный сервер отправителя определяет список уникальных доменов участников и рассылает пакет каждому серверу один раз.
2.  **Автономность**: Если один из серверов-участников (например, Server C) уходит в оффлайн, остальные участники (Server A, B, D) продолжают общаться. Сообщения для Server C накапливаются в очередях на других серверах.
3.  **Медиа-матрица**: В одном групповом чате могут быть вложения, хранящиеся на 5 разных серверах одновременно. Каждый сервер проксирует файлы своих пользователей самостоятельно.

---

## 5. Валидация политик (Policy Enforcement)

Модуль Конфедерации Knot Messager работает по принципу **Intersected Policy** (Пересечение политик):

1.  **Rule**: Действие разрешено только если оно разрешено ОБОИМИ серверами (отправителем и получателем).
2.  **Mapping**:
    *   Если Сервер А разрешил `ScreenSharing`, а Сервер Б запретил — шаринг экрана в этом чате **невозможен**.
    *   Если Сервер А запретил `Copying` (Для запрета копирования), Сервер Б **обязан** передать этот флаг своему клиенту, чтобы тот заблокировал контекстное меню для сообщений из этого чата.
3.  **Audit**: Все входящие федеративные пакеты проходят через `PolicyValidator`. Если сервер А пытается прислать Опрос на сервер Б, где опросы выключены, пакет будет отклонен с кодом `403 Forbidden`.

---

## 6. План реализации

### Этап 1: Инфраструктура
*   Обновить [FederationDomainConfig](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs#82-87), добавив туда `PublicKey` и `RemoteCapabilities`.
*   Реализовать `FederationHttpClient` для подписанных запросов между серверами.

### Этап 2: Проксирование (Storage Proxy)
*   Добавить в `Storage Module` поддержку типа `ExternalFile`.
*   Реализовать эндпоинт прокси-стриминга в модуле [Federation](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs#88-94).

### Этап 3: Доставка сообщений
*   Реализовать `FederationOutbox` (очередь доставки через Celery/Hangfire или простой BackgroundService).
*   Интегрировать `ICommandHandler<SendMessageCommand>` с вызовом службы федерации.
