Инициализация и фоновые задачи
Файлы:
backend/src/lib.rs,setup/tauri_setup.rs,vault/manager.rs,vault/pin_encryption.rs
Порядок запуска приложения, создание менеджеров, спавн фоновых задач, механизмы auto-lock и PIN rate limiting.
Порядок инициализации
Порядок создания менеджеров
Порядок критичен — менеджеры получают зависимости при создании:
| # | Менеджер | БД | Зависимости |
|---|---|---|---|
| 1 | IdentityManager | identity.db | — |
| 2 | DeviceManager | identity.db (shared) | — |
| 3 | VaultManager | vault.db | — |
| 4 | SpaceManager | spaces.db | — |
| 5 | CloudManager | spaces.db (shared) | — |
| 6 | PairingManager | — | IdentityManager, DeviceManager, VaultManager, SpaceManager |
| 7 | SharingManager | — | IdentityManager, DeviceManager, SpaceManager, VaultManager, CloudManager |
Auto-create identity (первый запуск)
При первом запуске, если identity не существует:
IdentityManager.create_identity(None)— генерирует Ed25519 keypair- Пытается сохранить signing key в vault (если vault unlocked)
- Если vault locked — сохраняет ключ в памяти (
pending_signing_key) - Привязывает текущее устройство к новой identity
Фоновые задачи
Обзор
| Задача | Файл | Спавн | Channel | Capacity | Shutdown |
|---|---|---|---|---|---|
| P2P Network | p2p/network.rs | При запуске (setup) | — | — | — (работает постоянно) |
| Tauri Bridge | events/tauri_bridge.rs | При запуске (Tauri only) | broadcast | 256 | При закрытии приложения |
| Vault Cache Lifecycle | lib.rs | При запуске | broadcast | 256 | При закрытии приложения |
| Auto-lock Timer | vault/auto_lock.rs | При запуске | — | — | — (работает постоянно) |
| Pairing Service | pairing/manager.rs | По запросу (start_pairing) | mpsc ServiceCommand | 10 | Stop command |
| Discovery Service | pairing/manager.rs | По запросу (start_discovery) | mpsc DiscoveryCommand | 10 | Stop command |
| Sharing Service | sharing/manager.rs | По запросу (start_sharing) | mpsc SharingCommand | 64 | Stop cmd + handle.abort() |
P2P Network
Спавнится в configure_app() при старте Tauri:
tauri::async_runtime::spawn(async move {
p2p::start_p2p_network(event_bus, state).await;
});Event loop на mDNS:
Discovered→ emitpeer_added, record metricsExpired→ emitpeer_removed, record metricsNewListenAddr→ emitlistening_addr
Подробнее: P2P Protocol.
Tauri Bridge
Только в desktop-режиме (#[cfg(not(feature = "headless"))]):
spawn_tauri_bridge(app.handle(), event_bus);Подписывается на broadcast EventBus (capacity 256). Перенаправляет все события в app.emit().
Подробнее: Events.
Pairing Service / Discovery Service
Спавнятся по запросу пользователя. Используют tokio::select! для мультиплексирования libp2p событий и mpsc команд:
loop {
tokio::select! {
event = service.poll_event() => {
// Обработка P2P событий
}
Some(cmd) = rx.recv() => {
match cmd {
ServiceCommand::Stop => break,
// Другие команды...
}
}
}
}Vault Cache Lifecycle
Файл:
backend/src/lib.rs—spawn_vault_cache_lifecycle()
Спавнится при запуске (и в Tauri, и в headless). Подписывается на EventBus и координирует кэш signing key в IdentityManager:
vault_unlocked→IdentityManager.materialize_signing_key()— загружает signing key из vault в кэшvault_locked→IdentityManager.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):
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
Все фоновые задачи используют один паттерн:
mpsc::channel(N)для команд от основного потока к background tasktokio::select!для мультиплексирования libp2p событий и командtry_send()для non-blocking отправки команд (без ожидания)oneshot::channelдля request-response (например,DirectoryListRequest)
Auto-lock механизм
Файл:
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)
Конфигурация
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.
Логика
// 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()— начальный timestampunlock_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
| Параметр | Production | fast-crypto (тесты) |
|---|---|---|
| Memory cost | 64 MB | 1 MB |
| Time cost | 3 iterations | 1 iteration |
| Parallelism | 4 threads | 1 thread |
| Output | 32 bytes | 32 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):
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:
- Bootstrap + Metrics — аналогично Tauri
init_managers()— аналогично Tauri- Tokio Runtime создаётся вручную
- HTTP-сервер (Axum) на порту из
HTTP_PORT - SSE endpoint
/api/eventsвместо Tauri bridge - Нет P2P network (запускается через API:
start_sharing,start_discovery)
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(())
}