Skip to content

SharingManager

Файл: backend/src/sharing/manager.rs

Оркестрация P2P sharing и синхронизации между устройствами. Управляет фоновым libp2p-сервисом: обнаружение пиров, публикация и приватное расшаривание пространств, файловая синхронизация по чанкам, state sync между устройствами одной identity. Подробнее о протоколе: P2P Protocol: Sharing.

Структура

ПолеТипОписание
identity_managerArc<Mutex<IdentityManager>>Менеджер идентичности
device_managerArc<Mutex<DeviceManager>>Менеджер устройств
space_managerArc<Mutex<SpaceManager>>Менеджер пространств
vault_managerArc<Mutex<VaultManager>>Менеджер хранилища
cloud_managerArc<Mutex<CloudManager>>Менеджер облаков
command_txOption<Sender<SharingCommand>>Канал команд к background service
service_handleOption<JoinHandle<()>>Handle фоновой задачи
known_peersArc<Mutex<HashMap<String, KnownPeerInfo>>>Обнаруженные пиры

Методы

МетодСигнатураОписание
Lifecycle
newfn(im, dm, sm, vm, cm) -> SelfСоздать менеджер
start_sharingfn(&mut self, event_bus) -> Result<()>Запустить P2P сеть
stop_sharingfn(&mut self) -> Result<()>Остановить P2P сеть
is_runningfn(&self) -> boolРаботает ли сервис
Peers
get_known_peersfn(&self) -> Vec<KnownPeerInfo>Список известных пиров
Sharing
publish_spacefn(&self, space_id) -> Result<()>Broadcast пространства
share_spacefn(&self, space_id, target_id) -> Result<()>Приватное расшаривание
Sync
request_file_syncfn(&self, space_id, file_hash) -> Result<()>Запросить файл
request_space_syncfn(&self, space_id) -> Result<()>Запросить все файлы
broadcast_state_updatefn(&self, update) -> Result<()>Синхронизировать состояние
Remote
request_directory_listfn(&self, device_id, path) -> Result<Receiver>Листинг удалённой директории

Зависимости

  • IdentityManager — identity данные для подписи и шифрования
  • DeviceManager — данные устройства, keypair для libp2p
  • SpaceManager — CRUD пространств, файлов, tombstones
  • VaultManager — signing key для шифрованного расшаривания
  • CloudManager — credentials для state sync
  • libp2p — Noise + Yamux transport, mDNS discovery, gossipsub

start_sharing

fn start_sharing(&mut self, event_bus: EventBus) -> Result<()>

Запускает фоновый сервис P2P sharing. Создаёт libp2p Swarm с mDNS, Noise, Yamux и gossipsub.

Логика

  1. Получает device keypair из DeviceManager
  2. Создаёт SharingService (libp2p Swarm)
  3. Спавнит background tokio task (run_sharing_service)
  4. Сохраняет command channel и handle
plantuml Diagram

Используется в: start_sharing_svc


publish_space

fn publish_space(&self, space_id: &str) -> Result<()>

Публикует пространство всем подключённым пирам (plaintext broadcast). На уровне сервиса (publish_space_svc) проверяется владение пространством через IdentityProof.

Логика

  1. Сервис: проверяет IdentityProof + space.identity_id == proof.identity_id()
  2. Отправляет команду SharingCommand::Publish { space_id } через channel
  3. Background service: загружает space + files из SpaceManager
  4. Формирует SpacePayload
  5. Отправляет SharingMessage::Publish всем известным пирам

Используется в: publish_space_svc


share_space

fn share_space(&self, space_id: &str, target_identity_id: &str) -> Result<()>

Приватно расшаривает пространство конкретной identity. Данные шифруются SealedEnvelope (hybrid encryption поверх primitives StrongholdManager::seal_for_recipients / unseal_envelope). На уровне сервиса (share_space_svc) проверяется владение через IdentityProof.

Источник pubkey'ев — таблица peers (хранит device_box_pubkey каждого пира из SharingMessage::Announce). Share fan-out'ится на все известные device-box-pubkey'и target identity. Пиры без объявленного device_box_pubkey (легаси) silently пропускаются с warning'ом в логе.

Логика

  1. Сервис: проверяет IdentityProof + space.identity_id == proof.identity_id()
  2. Отправляет команду через channel
  3. Background service:
    • Загружает space + files
    • Собирает все peer.device_box_pubkey пиров target_identity_id
    • Если коллекция пустая — log warning «No device_box_pubkeys for target identity» и не шлёт
    • Генерирует random 32-байтный content_key
    • Fan-out wrap content_key для каждого recipient device-box-pubkey через seal_to_recipients (WrappedSpaceKey per recipient)
    • AES-256-GCM(content_key, payload, AAD) — content layer
    • AAD: "kontinuum sharing; target={identity_id}" (HKDF info: b"kontinuum sharing key-wrap")
    • Шлёт один и тот же SealedEnvelope каждому из target_peers (each device receives its own wrapped key inside)
plantuml Diagram

Используется в: share_space_svc Receiver-side: SharingServiceEvent::SpaceSharedstronghold.unseal_envelope(sealed, aad, hkdf_info).


request_file_sync

fn request_file_sync(&self, space_id: &str, file_hash: &str) -> Result<()>

Запрашивает конкретный файл у пира. Файл передаётся чанками по 32 KB с верификацией blake3 hash. Поддерживает resume через .part файлы.

Логика

  1. Проверяет наличие .part файла — если есть, отправляет resume_offset в FileRequest
  2. Отправляет FileRequest всем пирам, у которых есть этот файл
  3. Remote peer читает файл, пропускает resume_offset байт, разбивает остаток на чанки (32 KB)
  4. Отправляет каждый чанк как FileChunk
  5. Локально: записывает чанки в staging .part файл с fsync после каждой записи
  6. При получении последнего чанка: верифицирует blake3 hash
  7. Эмитит события file_sync_progress и file_sync_complete
plantuml Diagram

Используется в: request_file_sync_svc


broadcast_state_update

fn broadcast_state_update(&self, update: SyncUpdate) -> Result<()>

Рассылает обновление состояния всем пирам с той же identity. Используется для синхронизации устройств, пространств, облаков.

Типы обновлений

  • DeviceAdded / DeviceUpdated / DeviceRemoved
  • SpaceAdded / SpaceUpdated / SpaceRemoved
  • CloudAdded / CloudUpdated / CloudRemoved

Используется в: внутренне при изменениях состояния


request_directory_list

fn request_directory_list(&self, device_id: &str, path: &str) -> Result<Receiver<Result<Vec<RemoteFileEntry>>>>

Запрашивает листинг директории на удалённом устройстве. Возвращает oneshot Receiver для асинхронного ожидания ответа.

Безопасность

Remote peer выполняет проверки:

  • Путь должен быть абсолютным
  • Canonicalize для предотвращения traversal
  • Ограничение: только home dir, DB_PATH, Android storage

Используется в: request_remote_directory_svc


Background Service: State Sync

Фоновый сервис автоматически синхронизирует состояние между устройствами одной identity.

Механизм

  1. При обнаружении пира с той же identity — отправляет SyncDigest (hash текущего состояния)
  2. Если digest отличается — запрашивает/отправляет SyncSnapshot (полное состояние)
  3. Snapshot включает: devices, spaces, clouds, tombstones
  4. Merge: INSERT OR REPLACE для новых/обновлённых записей, tombstones для удалений
plantuml Diagram

Background Service: Vault Integration

Background Operation Mode

При start_sharing() режим фоновых операций определяет, как сервис получает signing key:

РежимПолучение ключаПоведение при lock
ContinueVaultManager.issue_scoped_key()ScopedKeyКлюч остаётся рабочим, операции продолжаются
PauseIdentityManager.get_cached_signing_key() при каждой операцииПри lock кэш очищается, операции ждут unlock

При расшифровке полученного пространства (SpaceShared):

  1. Режим Continue → используется ScopedKey.secret()
  2. Режим Pause → используется IdentityManager.get_cached_signing_key()
  3. Если ни одного ключа нет → fallback на прямой VaultManager.load_secret()

.part File Cleanup

При запуске run_sharing_service():

  1. Удаляет протухшие .part файлы (старше 7 дней)
  2. Обнаруживает незавершённые трансферы для потенциального resume

PeerTracker — State Machine управления пирами

Файл: backend/src/sharing/peer_tracker.rs

PeerTracker — автомат состояний для отслеживания жизненного цикла каждого обнаруженного пира. Заменяет ad-hoc логику с timestamp проверками.

Состояния

СостояниеОписание
OnlineПир подключён, понги приходят
ReconnectingTCP-соединение закрыто, попытка переподключения
OfflineПереподключение не удалось, пир недоступен

Переходы

plantuml Diagram

Ключевые методы

МетодТриггерДействия
on_discoveredAnnounce полученNew → EmitOnline; Offline → EmitOnline; Online → noop
on_pongPong полученReconnecting → EmitOnline; Online → UpdateStorage
on_disconnectedConnectionClosed (n_est == 0)Online → Reconnecting + Reconnect
on_reconnect_failedOutgoingConnectionErrorReconnecting → Offline + EmitOffline
on_expiredmDNS expiredRemove peer + EmitOffline
check_timeoutsКаждые 5 секундReconnecting > 5s → Offline; Online > 30s → Offline

Таймауты

КонстантаЗначениеНазначение
RECONNECT_TIMEOUT_SECS5 секВремя ожидания reconnect после ConnectionClosed
STALE_TIMEOUT_SECS30 секТаймаут без pong для Online пиров (half-open TCP fallback)

PeerAction

Методы PeerTracker возвращают Vec<PeerAction> — команды для вызывающего кода:

  • EmitOnline — эмитировать SSE-событие peer_online
  • EmitOffline — эмитировать SSE-событие peer_offline
  • Reconnect — вызвать service.reconnect_peer(peer_id)
  • UpdateStorage — обновить хранилище устройства в БД

Background Service: Heartbeat

Каждые 30 секунд:

  1. Отправляет Ping всем известным пирам (с количеством устройств)
  2. При получении Pong — PeerTracker обновляет last_seen, возвращает действия
  3. check_timeouts() вызывается каждые 5 секунд — выявляет stale/reconnecting пиров
  4. При несовпадении device count — запускает DeviceSync