«Общая папка»: P2P-обмен файлами для семьи без облака — и робот, который их глотает
Это история моей первой программы, которую не стыдно показать людям. Задача была бытовая: чтобы фото и видео между компьютерами семьи ходили сами — без облаков, аккаунтов и флешек. Получилась «Общая папка» и робот Папыч, который доставляет файлы. Расскажу, как это устроено внутри: прямые соединения через iroh, свой протокол поверх QUIC, правила синхронизации, обновления без сервера, анимированный персонаж на SVG и путь в Microsoft Store.
Задача
Брату нужно скинуть фото с дачи — пару гигабайт. Мессенджер сжимает и режет по размеру, облако забито или просит подписку, флешку везти лень. При этом оба компьютера включены и подключены к интернету. Хотелось простого: папка, в которую кладёшь файл, — а у родственника появляется «Получить / Отклонить», и файл идёт напрямую.
Требования, которые я себе поставил:
никакого своего сервера: его нужно содержать, и он видит файлы;
работает между разными городами и провайдерами, за NAT;
любой размер и докачка после обрыва;
ничего не удаляется у других без спроса;
понятно человеку, который не знает слов «синхронизация» и «P2P».
Как это выглядит
Обычная папка «Общая» в Проводнике. Положил файл — через три секунды тишины программа считает его готовым и предлагает семье. У получателя уведомление «Получить / Отклонить» (или автоприём, например, только фото). Файл идёт напрямую с компьютера на компьютер.
📎

Связь: iroh вместо своего сервера
Сердце программы — библиотека iroh от команды n0. Устройство адресуется не IP-адресом, а своим публичным ключом (Ed25519). iroh сам пробует пробить NAT и установить прямое QUIC-соединение; если не выходит, данные идут через публичные ретрансляторы n0 — зашифрованными (TLS 1.3 внутри QUIC), ретранслятор видит только ключи и IP-адреса.
Для передачи файлов в iroh есть модуль iroh-blobs, но на момент разработки он был помечен как не готовый для продакшена, поэтому протокол свой, поверх потоков QUIC:
управляющий поток с короткими сообщениями: приветствие (имя, версия, состав семьи), предложение файла, ответ, текстовые сообщения и подтверждения доставки;
для каждого файла — отдельный поток с данными, докачка с места обрыва и проверка целостности по BLAKE3.
Грабли совместимости: старая версия программы не знает новых видов сообщений и при неизвестном варианте рвёт связь. Поэтому новые сообщения отправляются только тем устройствам, чья версия из приветствия их поддерживает, а остальные получат их после обновления.
Семья и коды приглашения
Семья — это сколько угодно устройств, каждое знает ключи остальных. Новое подключается одноразовым кодом приглашения, который действует сутки: в нём ключ пригласившего и секрет. После знакомства новое устройство получает всё, что уже лежит в общей папке.
Изменения, версии и конфликты
Самое коварное оказалось не в передаче, а в правилах:
новый файл считается готовым после 3 секунд тишины, изменённый — после 20, иначе семья получит недописанный документ;
изменённый файл — это новая версия; если старое предложение ещё не принято, оно просто заменяется новым;
старая версия у получателя уходит в Корзину, а не исчезает;
если двое правили одновременно — сохраняются обе: «имя (версия от Брат).ext»;
удаление не передаётся совсем. Удалил у себя — у остальных файл остаётся. Для семейных фото это важнее, чем зеркальная синхронность.
Облако — только запасной путь
Если компьютеры не бывают включены одновременно, можно подключить своё облако: Яндекс Диск, любой WebDAV или папку OneDrive / Google Диска. Файлы шифруются (XChaCha20) ещё до загрузки, идут кусками по 32 МБ и удаляются из облака после получения. Облако необязательно и содержимого не видит.
Обновления без сервера
Раз сервера нет, раздавать обновления тоже неоткуда. Решение: установщик подписывается ключом разработчика (minisign), и устройства передают его друг другу так же, как файлы. Получатель проверяет подпись встроенным открытым ключом и ставит обновление тихо — когда ничего не передаётся и окно закрыто. Поставил новую версию на один компьютер — через пару минут она у всей семьи.
Окно: Tauri 2 и обычный HTML
«Двигатель» написан на Rust, интерфейс — Tauri 2 (системный WebView2) и обычные HTML, CSS и JavaScript без сборщика. Установщик весит около 7 МБ. Девять языков, строки общие для окна и «двигателя».
Шторка сверху экрана
Идея подсмотрена у телефонов: тащишь файл к верхнему краю экрана — выезжает чёрная шторка. Бросил на Папыча — получат все, на мини-робота родственника — только он.
📎

Технически это отдельное прозрачное окно поверх всех: прозрачные области пропускают клики к окнам под ним, видимая часть — нет. Положение курсора программа опрашивает раз в 33 мс и решает, пора ли шторке выглянуть. Чтобы отличить перетаскивание файла от перетаскивания окна, учитывается, зажата ли кнопка мыши, где её нажали и двигается ли окно под курсором.
Грабли: такое окно нельзя показывать и прятать напрямую через WinAPI (ShowWindow, SetWindowPos). Оконная библиотека Tauri пересчитывает стили из своих флагов и прячет окно, которое считает скрытым, — только через её собственные show и hide.
Папыч
Робот-папка нарисован кодом: SVG собирается в JavaScript, а каждая подвижная часть — глаза, веки, руки, «приседание», наклон — висит на пружине с жёсткостью и затуханием. Один общий цикл requestAnimationFrame двигает все пружины, а сценки («проглотить файл», «чихнуть», «уснуть в колпаке») — это async-функции, которые меняют цели пружин и ждут. Глаза следят за курсором по всему экрану: Rust передаёт в окно глобальную позицию курсора. Когда окно скрыто, анимация останавливается, чтобы не тратить процессор.
📎

После установки Папыч выпрыгивает из окна и с указкой проводит экскурсию — для этого поверх экрана на время открывается ещё одно прозрачное окно.
📎

Microsoft Store
Неподписанный установщик пугает людей предупреждением SmartScreen, а сертификат для подписи стоит денег. Выход — Microsoft Store: пакет MSIX Microsoft подписывает сам, а регистрация для частных разработчиков теперь бесплатная. Программа при этом одна и та же: она узнаёт, что запущена из пакета (Package.Current), и тогда не ставит обновления от семьи — их приносит Store, — включает автозапуск через StartupTask вместо реестра и показывает уведомления от имени пакета. Проверка в Store заняла меньше суток.
Итог
Windows 10 и 11, бесплатно, без аккаунтов, рекламы и сбора данных;
Microsoft Store: apps.microsoft.com/detail/9NX8CF7SNKBF;
страница проекта на GitHub (код открыт для чтения): github.com/ZakitKH0000/FamilyFolder.
Буду рад вопросам и критике в комментариях — особенно про протокол и правила синхронизации. И идеям, чему ещё научить Папыча.