PairingManager
Файл:
backend/src/pairing/manager.rs
Оркестрация P2P-паринга устройств. Поддерживает три режима:
- QR-based (legacy) —
start_pairing+approve_pairing/reject_pairing. Signing key передаётся по проводу (до UX-флипа). - Discovery PIN-based (legacy) —
start_discovery+initiate_pairing+confirm_pairing. Также передаёт signing key. - Join cross-sign (текущий, slice 14–15) —
initiate_join_request+ libp2p req-respJoinRequest/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:
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" в EventBusHost-side также эмитит
join_response_sentпосле cross-sign'а. Любая ошибка —join_response_error.
Структура
| Поле | Тип | Описание |
|---|---|---|
state | Arc<Mutex<PairingState>> | Текущее состояние паринга |
identity_manager | Arc<Mutex<IdentityManager>> | Менеджер идентичности |
device_manager | Arc<Mutex<DeviceManager>> | Менеджер устройств |
vault_manager | Arc<Mutex<VaultManager>> | Менеджер хранилища |
space_manager | Arc<Mutex<SpaceManager>> | Менеджер пространств |
service_tx | Arc<Mutex<Option<Sender<ServiceCommand>>>> | Канал к QR-сервису |
service_handle | Arc<Mutex<Option<JoinHandle<()>>>> | Handle QR-задачи |
pending_qr_channel | Arc<Mutex<Option<ResponseChannel>>> | Канал ответа QR |
discovery_tx | Arc<Mutex<Option<Sender<DiscoveryCommand>>>> | Канал к discovery-сервису |
discovery_handle | Arc<Mutex<Option<JoinHandle<()>>>> | Handle discovery-задачи |
discovered_devices | Arc<Mutex<Vec<DiscoveredDevice>>> | Обнаруженные устройства |
pending_requests | Arc<Mutex<Vec<PendingPairingRequest>>> | Ожидающие запросы (legacy) |
pending_channels | Arc<Mutex<HashMap<String, ResponseChannel>>> | Каналы ответов (legacy) |
pending_join_requests | Arc<Mutex<HashMap<String, PairingJoinRequest>>> | In-flight join-запросы (joiner-side) |
stronghold_for_join | Arc<Mutex<Option<Arc<StrongholdManager>>>> | Stronghold handle для bg-задачи (host-side cross-sign) |
Методы
| Метод | Сигнатура | Описание |
|---|---|---|
| QR-based (legacy) | ||
start_pairing | fn(&self, event_bus) -> Result<StartPairingResult> | Запустить QR-паринг хост |
stop_pairing | fn(&self) -> Result<()> | Остановить QR-паринг |
scan_services_async | fn(&self, device_mgr, timeout) -> Result<Vec<DiscoveredPairing>> | Поиск хостов (joiner) |
submit_pairing_request_async | fn(&self, ...) -> Result<PairingResponse> | Отправить запрос на паринг |
approve_pairing | fn(&self, token) -> Result<PairingResponse> | Одобрить запрос |
reject_pairing | fn(&self, token) -> Result<()> | Отклонить запрос |
| Discovery (PIN-based, legacy) | ||
start_discovery | fn(&self, event_bus) -> Result<()> | Запустить mDNS discovery |
stop_discovery | fn(&self) -> Result<()> | Остановить discovery |
get_discovered_devices | fn(&self) -> Vec<DiscoveredDevice> | Список обнаруженных устройств |
initiate_pairing | fn(&self, peer_id, identity_id) -> Result<String> | Инициировать PIN-паринг |
confirm_pairing | fn(&self, peer_id, approved, pin) -> Result<()> | Подтвердить/отклонить с PIN |
| Join cross-sign (текущий) | ||
set_join_stronghold | fn(&self, stronghold: Arc<StrongholdManager>) | Инжектировать Stronghold для bg-задачи (вызывается из init_managers) |
host_handle_join_request | fn(&self, stronghold, host_device_id, &req) -> async Result<PairingJoinResponse> | Host: cross-sign joiner-cert, вернуть response |
joiner_apply_join_response | fn(&self, joiner_device_id, &req, &resp) -> Result<()> | Joiner: verify chain + persist |
initiate_join_request | fn(&self, target_peer_id, stronghold, password) -> impl Future<...> + Send + 'static | Joiner: 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)
Логика
- Проверяет, что vault разблокирован
- Генерирует случайный токен (32 bytes hex)
- Получает identity и device данные
- Поднимает background libp2p task с mDNS + request-response
- Возвращает QR данные для отображения
Используется в:
start_pairing_svc
start_discovery
fn start_discovery(&self, event_bus: EventBus) -> Result<()>
Запускает mDNS discovery для обнаружения устройств в локальной сети. Обнаруженные устройства сохраняются в discovered_devices и отправляются через event bus.
Логика
- Проверяет, что vault разблокирован
- Поднимает background task с libp2p mDNS + gossipsub
- Обнаруженные пиры фильтруются (исключаются устройства той же identity)
- Каждое обнаружение эмитит событие
device_discovered
Используется в:
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 для подтверждения на другом устройстве
Логика
- Генерирует случайный 6-значный PIN
- Собирает данные identity и device
- Отправляет PairingRequest через P2P channel
- Возвращает PIN для отображения инициатору
Используется в:
initiate_pairing_svc
confirm_pairing
fn confirm_pairing(&self, peer_id: &str, approved: bool, pin: &str) -> Result<()>
Подтверждает или отклоняет запрос на паринг. При подтверждении происходит обмен identity данными между устройствами.
Логика
- Верифицирует PIN
- Если одобрено — обменивается identity данными:
- Сравнивает количество устройств (у кого больше — тот «старший»)
- «Младшее» устройство принимает identity «старшего»
- Обновляет IdentityManager и DeviceManager
- Отправляет ответ через P2P channel
Используется в:
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 channelreject_pairing— отправляет rejection через response channel
Используется в:
approve_pairing_svc,reject_pairing_svc
stop_pairing / stop_discovery
Остановка фоновых задач:
stop_pairing()— останавливает QR-based background servicestop_discovery()— останавливает mDNS discovery task
Используется в:
stop_pairing_svc,stop_discovery_svc