Skip to content

Инициализация и фоновые задачи

Файлы: backend/src/lib.rs, setup/tauri_setup.rs, vault/manager.rs, vault/pin_encryption.rs

Порядок запуска приложения, создание менеджеров, спавн фоновых задач, механизмы auto-lock и PIN rate limiting.

Порядок инициализации

plantuml Diagram

Порядок создания менеджеров

Порядок критичен — менеджеры получают зависимости при создании:

#МенеджерБДЗависимости
1IdentityManageridentity.db
2DeviceManageridentity.db (shared)
3VaultManagervault.db
4SpaceManagerspaces.db
5CloudManagerspaces.db (shared)
6PairingManagerIdentityManager, DeviceManager, VaultManager, SpaceManager
7SharingManagerIdentityManager, DeviceManager, SpaceManager, VaultManager, CloudManager

Auto-create identity (первый запуск)

При первом запуске, если identity не существует:

  1. IdentityManager.create_identity(None) — генерирует Ed25519 keypair
  2. Пытается сохранить signing key в vault (если vault unlocked)
  3. Если vault locked — сохраняет ключ в памяти (pending_signing_key)
  4. Привязывает текущее устройство к новой identity

Фоновые задачи

Обзор

ЗадачаФайлСпавнChannelCapacityShutdown
P2P Networkp2p/network.rsПри запуске (setup)— (работает постоянно)
Tauri Bridgeevents/tauri_bridge.rsПри запуске (Tauri only)broadcast256При закрытии приложения
Vault Cache Lifecyclelib.rsПри запускеbroadcast256При закрытии приложения
Auto-lock Timervault/auto_lock.rsПри запуске— (работает постоянно)
Pairing Servicepairing/manager.rsПо запросу (start_pairing)mpsc ServiceCommand10Stop command
Discovery Servicepairing/manager.rsПо запросу (start_discovery)mpsc DiscoveryCommand10Stop command
Sharing Servicesharing/manager.rsПо запросу (start_sharing)mpsc SharingCommand64Stop cmd + handle.abort()

P2P Network

Спавнится в configure_app() при старте Tauri:

rust
tauri::async_runtime::spawn(async move {
    p2p::start_p2p_network(event_bus, state).await;
});

Event loop на mDNS:

  • Discovered → emit peer_added, record metrics
  • Expired → emit peer_removed, record metrics
  • NewListenAddr → emit listening_addr

Подробнее: P2P Protocol.

Tauri Bridge

Только в desktop-режиме (#[cfg(not(feature = "headless"))]):

rust
spawn_tauri_bridge(app.handle(), event_bus);

Подписывается на broadcast EventBus (capacity 256). Перенаправляет все события в app.emit().

Подробнее: Events.

Pairing Service / Discovery Service

Спавнятся по запросу пользователя. Используют tokio::select! для мультиплексирования libp2p событий и mpsc команд:

rust
loop {
    tokio::select! {
        event = service.poll_event() => {
            // Обработка P2P событий
        }
        Some(cmd) = rx.recv() => {
            match cmd {
                ServiceCommand::Stop => break,
                // Другие команды...
            }
        }
    }
}

Vault Cache Lifecycle

Файл: backend/src/lib.rsspawn_vault_cache_lifecycle()

Спавнится при запуске (и в Tauri, и в headless). Подписывается на EventBus и координирует кэш signing key в IdentityManager:

  • vault_unlockedIdentityManager.materialize_signing_key() — загружает signing key из vault в кэш
  • vault_lockedIdentityManager.clear_cached_keys() — очищает кэш

Это позволяет фоновым задачам (sharing в режиме pause) получать signing key через get_cached_signing_key() без прямого обращения к vault.

При инициализации, если vault уже разблокирован (headless с auto-unlock), signing key материализуется сразу в init_managers().

Sharing Service

Наиболее сложная фоновая задача. Обрабатывает:

  • mDNS discovery и подключение пиров
  • Heartbeat (Ping/Pong каждые 30 сек)
  • Публикацию и приём пространств
  • Файловый трансфер по чанкам
  • State sync (digest → snapshot → merge)
  • Детекцию offline пиров (90 сек без Pong)

Команды через mpsc::channel(64):

rust
enum SharingCommand {
    Stop,
    PublishSpace { space_id },
    ShareSpace { space_id, target_identity_id },
    RequestFileSync { space_id, file_hash },
    RequestSpaceSync { space_id },
    RequestDirectoryList { device_id, path, response_tx },
    BroadcastStateUpdate { update },
}

Паттерн: tokio::select! + mpsc

Все фоновые задачи используют один паттерн:

  1. mpsc::channel(N) для команд от основного потока к background task
  2. tokio::select! для мультиплексирования libp2p событий и команд
  3. try_send() для non-blocking отправки команд (без ожидания)
  4. oneshot::channel для request-response (например, DirectoryListRequest)
plantuml Diagram

Auto-lock механизм

plantuml Diagram

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

Activity-based автоблокировка vault. Три разделённых операции:

  • is_unlocked() — чистая проверка (master_key.is_some()), без побочных эффектов
  • touch_activity() — обновляет last_activity_at (только на unlocked vault)
  • check_and_lock_if_expired() — проверяет таймаут и блокирует (вызывается только background timer)

Конфигурация

rust
struct SessionState {
    last_activity_at: Option<u64>,                          // Unix timestamp последней активности
    auto_lock_timeout: u64,                                 // секунды (0 = отключено, default = 300)
    background_operation_mode: BackgroundOperationMode,     // Continue (default) или Pause
}

Настройки auto_lock_timeout и background_operation_mode персистятся в таблицу vault_settings и загружаются при старте VaultManager.

Логика

rust
// Background timer (каждые 5 секунд)
fn check_and_lock_if_expired(&self) -> bool {
    if master_key.is_none() { return false; }  // ранний выход
    if auto_lock_timeout == 0 { return false; }
    let should_lock = match last_activity_at {
        None => true,
        Some(last) => now - last > auto_lock_timeout,
    };
    if should_lock { self.lock(); }
    should_lock
}

Обновление активности

touch_activity() вызывается из 5 мест:

  • POST /api/vault/heartbeat — frontend activity tracker (mousemove/keydown, throttle 30s)
  • setup_with_pin() / setup_with_recovery_phrase() — начальный timestamp
  • unlock_with_pin() / unlock_with_recovery_phrase() — начальный timestamp

Операции с секретами (save_secret, load_secret, change_pin, delete_secret) не продлевают сессию — их могут инициировать фоновые процессы без присутствия пользователя.

Событие

При срабатывании auto-lock эмитится vault_locked → frontend store → redirect на PIN-экран. Подробнее: Events: vault_locked.

PIN Rate Limiting

Файл: backend/src/vault/pin_encryption.rs

Защита от brute-force атак на PIN.

Параметры Argon2

ПараметрProductionfast-crypto (тесты)
Memory cost64 MB1 MB
Time cost3 iterations1 iteration
Parallelism4 threads1 thread
Output32 bytes32 bytes

fast-crypto feature включается автоматически при --features headless и используется в интеграционных тестах для ускорения.

Lockout политика

ПорогДействие
1–9 попытокExponential backoff (информационный)
10 попытокLockout на 1 час
15 попытокData wipe (полный сброс vault)

Exponential backoff

Попытки 1–2:   0 сек (без задержки)
Попытка 3:     5 сек
Попытка 4:     10 сек
Попытка 5:     30 сек
Попытка 6:     60 сек
Попытки 7+:    120 сек

Состояние

Хранится в vault_pin_state (singleton, id=1):

rust
struct PinState {
    failed_attempts: u32,              // Текущая серия неудач
    last_failed_attempt: Option<u64>,  // Timestamp
    is_locked: bool,                   // Заблокирован?
    lockout_until: Option<u64>,        // До когда заблокирован
    total_attempts: u64,               // Всего попыток (статистика)
}

Сброс

  • Успешный PIN: failed_attempts = 0, is_locked = false
  • Recovery phrase: полный сброс PIN state
  • Reset vault: удаление всех данных

Ссылки: VaultManager, vault service

Headless режим

При запуске с --features headless:

  1. Bootstrap + Metrics — аналогично Tauri
  2. init_managers() — аналогично Tauri
  3. Tokio Runtime создаётся вручную
  4. HTTP-сервер (Axum) на порту из HTTP_PORT
  5. SSE endpoint /api/events вместо Tauri bridge
  6. Нет P2P network (запускается через API: start_sharing, start_discovery)
rust
pub fn run_headless() -> Result<()> {
    init_bootstrap();
    init_metrics();
    let managers = init_managers(Path::new(&db_path))?;
    let runtime = tokio::runtime::Runtime::new()?;
    runtime.block_on(async {
        spawn_auto_lock_timer(managers.vault_manager.clone());
        spawn_vault_cache_lifecycle(
            managers.event_bus.clone(),
            managers.identity_manager.clone(),
            managers.vault_manager.clone(),
        );
        http_server::start_http_server(port, managers...).await
    })?;
    Ok(())
}