Skip to content

PairingManager

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

Оркестрация P2P-паринга устройств. Поддерживает три режима:

  1. QR-based (legacy)start_pairing + approve_pairing / reject_pairing. Signing key передаётся по проводу (до UX-флипа).
  2. Discovery PIN-based (legacy)start_discovery + initiate_pairing + confirm_pairing. Также передаёт signing key.
  3. Join cross-sign (текущий, slice 14–15)initiate_join_request + libp2p req-resp JoinRequest/JoinResponse. Signing key НЕ передаётся — хост cross-sign'ит joiner-cert своим DeviceSignKey (Stronghold).

Управляет фоновыми задачами mDNS discovery и libp2p request-response. Подробнее о протоколе: P2P Protocol: Pairing.

Join flow (cross-sign cert-chain)

Joiner → Host:

text
1. Joiner: generate_device_keypair(DeviceSignKey) + (DeviceBoxKey) внутри Stronghold
2. Joiner: PairingJoinRequest { joiner_device_sign_pubkey,
                                joiner_device_box_pubkey,
                                joiner_device_transport_pubkey }
                                → отправка через DiscoveryCommand::InitiateJoin
3. Host:   DiscoveryServiceEvent::JoinRequest получен
4. Host:   pre-read (sync) account_root_pubkey + host SignedDeviceCert
5. Host:   host_cross_sign_device_cert_with_data() →
              build DeviceCertBody { account_id, account_root_pubkey,
                 device_id=blake3(joiner_sign_pk), all three joiner pubkeys,
                 parent=hash(host_cert) }
              sign body через Stronghold(DeviceSignKey)
6. Host:   PairingJoinResponse { issued_cert: SignedDeviceCert (joiner),
                                 host_cert:  SignedDeviceCert (host, для chain-verify) }
                                 → send_join_response
7. Joiner: DiscoveryServiceEvent::JoinResponse получен
8. Joiner: apply_join_response_with_arcs() →
              verify_chain (host_cert.parent → joiner_cert)
              set_account_root_pubkey, set_device_pubkeys, set_device_cert
9. Joiner: emit "join_response_applied" в EventBus

Host-side также эмитит join_response_sent после cross-sign'а. Любая ошибка — join_response_error.

Структура

ПолеТипОписание
stateArc<Mutex<PairingState>>Текущее состояние паринга
identity_managerArc<Mutex<IdentityManager>>Менеджер идентичности
device_managerArc<Mutex<DeviceManager>>Менеджер устройств
vault_managerArc<Mutex<VaultManager>>Менеджер хранилища
space_managerArc<Mutex<SpaceManager>>Менеджер пространств
service_txArc<Mutex<Option<Sender<ServiceCommand>>>>Канал к QR-сервису
service_handleArc<Mutex<Option<JoinHandle<()>>>>Handle QR-задачи
pending_qr_channelArc<Mutex<Option<ResponseChannel>>>Канал ответа QR
discovery_txArc<Mutex<Option<Sender<DiscoveryCommand>>>>Канал к discovery-сервису
discovery_handleArc<Mutex<Option<JoinHandle<()>>>>Handle discovery-задачи
discovered_devicesArc<Mutex<Vec<DiscoveredDevice>>>Обнаруженные устройства
pending_requestsArc<Mutex<Vec<PendingPairingRequest>>>Ожидающие запросы (legacy)
pending_channelsArc<Mutex<HashMap<String, ResponseChannel>>>Каналы ответов (legacy)
pending_join_requestsArc<Mutex<HashMap<String, PairingJoinRequest>>>In-flight join-запросы (joiner-side)
stronghold_for_joinArc<Mutex<Option<Arc<StrongholdManager>>>>Stronghold handle для bg-задачи (host-side cross-sign)

Методы

МетодСигнатураОписание
QR-based (legacy)
start_pairingfn(&self, event_bus) -> Result<StartPairingResult>Запустить QR-паринг хост
stop_pairingfn(&self) -> Result<()>Остановить QR-паринг
scan_services_asyncfn(&self, device_mgr, timeout) -> Result<Vec<DiscoveredPairing>>Поиск хостов (joiner)
submit_pairing_request_asyncfn(&self, ...) -> Result<PairingResponse>Отправить запрос на паринг
approve_pairingfn(&self, token) -> Result<PairingResponse>Одобрить запрос
reject_pairingfn(&self, token) -> Result<()>Отклонить запрос
Discovery (PIN-based, legacy)
start_discoveryfn(&self, event_bus) -> Result<()>Запустить mDNS discovery
stop_discoveryfn(&self) -> Result<()>Остановить discovery
get_discovered_devicesfn(&self) -> Vec<DiscoveredDevice>Список обнаруженных устройств
initiate_pairingfn(&self, peer_id, identity_id) -> Result<String>Инициировать PIN-паринг
confirm_pairingfn(&self, peer_id, approved, pin) -> Result<()>Подтвердить/отклонить с PIN
Join cross-sign (текущий)
set_join_strongholdfn(&self, stronghold: Arc<StrongholdManager>)Инжектировать Stronghold для bg-задачи (вызывается из init_managers)
host_handle_join_requestfn(&self, stronghold, host_device_id, &req) -> async Result<PairingJoinResponse>Host: cross-sign joiner-cert, вернуть response
joiner_apply_join_responsefn(&self, joiner_device_id, &req, &resp) -> Result<()>Joiner: verify chain + persist
initiate_join_requestfn(&self, target_peer_id, stronghold, password) -> impl Future<...> + Send + 'staticJoiner: generate keys → send JoinRequest → bg-task ждёт response

Зависимости

  • IdentityManager — identity данные для обмена
  • DeviceManager — данные устройства для обмена
  • VaultManager — vault должен быть разблокирован (signing key)
  • SpaceManager — синхронизация пространств при паринге

start_pairing

fn start_pairing(&self, event_bus: EventBus) -> Result<StartPairingResult>

Запускает QR-based паринг: поднимает libp2p сервис с request-response протоколом, генерирует QR-код для сканирования joiner'ом.

Возвращает

StartPairingResult — содержит qr_data (JSON: version, token, identity_id, service_name)

Логика

  1. Проверяет, что vault разблокирован
  2. Генерирует случайный токен (32 bytes hex)
  3. Получает identity и device данные
  4. Поднимает background libp2p task с mDNS + request-response
  5. Возвращает QR данные для отображения
plantuml Diagram

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


start_discovery

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

Запускает mDNS discovery для обнаружения устройств в локальной сети. Обнаруженные устройства сохраняются в discovered_devices и отправляются через event bus.

Логика

  1. Проверяет, что vault разблокирован
  2. Поднимает background task с libp2p mDNS + gossipsub
  3. Обнаруженные пиры фильтруются (исключаются устройства той же identity)
  4. Каждое обнаружение эмитит событие device_discovered
plantuml Diagram

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


initiate_pairing

fn initiate_pairing(&self, target_peer_id: &str, target_identity_id: &str) -> Result<String>

Инициирует паринг с обнаруженным устройством. Генерирует 6-значный PIN и отправляет запрос через P2P.

Возвращает

String — 6-значный PIN для подтверждения на другом устройстве

Логика

  1. Генерирует случайный 6-значный PIN
  2. Собирает данные identity и device
  3. Отправляет PairingRequest через P2P channel
  4. Возвращает PIN для отображения инициатору

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


confirm_pairing

fn confirm_pairing(&self, peer_id: &str, approved: bool, pin: &str) -> Result<()>

Подтверждает или отклоняет запрос на паринг. При подтверждении происходит обмен identity данными между устройствами.

Логика

  1. Верифицирует PIN
  2. Если одобрено — обменивается identity данными:
    • Сравнивает количество устройств (у кого больше — тот «старший»)
    • «Младшее» устройство принимает identity «старшего»
  3. Обновляет IdentityManager и DeviceManager
  4. Отправляет ответ через P2P channel
plantuml Diagram

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


get_discovered_devices

fn get_discovered_devices(&self) -> Vec<DiscoveredDevice>

Возвращает список обнаруженных устройств. Фильтрует устройства с той же identity (они уже «свои»).

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


approve_pairing / reject_pairing

fn approve_pairing(&self, token: &str) -> Result<PairingResponse>fn reject_pairing(&self, token: &str) -> Result<()>

QR-based паринг: одобрение или отклонение запроса по токену.

  • approve_pairing — собирает identity + device данные, отправляет joiner'у через response channel
  • reject_pairing — отправляет rejection через response channel

Используется в: approve_pairing_svc, reject_pairing_svc


stop_pairing / stop_discovery

Остановка фоновых задач:

  • stop_pairing() — останавливает QR-based background service
  • stop_discovery() — останавливает mDNS discovery task

Используется в: stop_pairing_svc, stop_discovery_svc