InquirerSara Duterte to use personal funds for HK trip on Nov. 6-8Bollywood HungamaBREAKING: Maatrubhumi release date CONFIRMED; Salman Khan-starrer to release on December 4; to clash with EethaThe Jerusalem PostTrump administration coordinated with Iran on deportations for asylum seekers in middle of war – APDaily MaverickLETTER TO MAHLAMBA NDLOPFU: Sies for Sisisi’s creative accounting and the cat with crocodile tearsInquirer EntertainmentDavao Conyo leads TikTok Awards PH 2026 winners heading to SEA tiltZDF heuteAktuelle Pressemitteilungen des ZDFNHK 社会中山間地域の訪問介護 移動コストを報酬加算で評価案 厚労省Wirtualna PolskaUprowadzili i więzili mieszkańca Gdyni. To odkryli w aucie porywaczyNMEGrimes making film debut in entirely AI-generated ‘Gods Don’t Give Gifts’عالم التقنيةHonor WIN 2 يتفوق في الأداء ويتصدر تصنيفات أكتوبر متقدماً على OnePlus 16ColliderEvery Lord of the Rings Book, Ranked by GoodreadsDW 中文澳门国安首案:民主派前议员区锦新被重判12年半
The Daily Newsstand · Free, Always
Friday, November 6, 2026

Файлы из браузера в браузер и «торрент» без установки: как устроен TFile.io

Translate

Привет, Хабр! Я делаю TFile — сервис, который передаёт файлы напрямую между браузерами. Без регистрации, без установки, без загрузки файла на чей-то сервер. Расскажу, как он устроен и на какие грабли я наступил по дороге. Цифры в статье — из реальных замеров.

Два режима

⚡ Отправить сейчас. Открываете сайт, получаете ссылку вида tfile.io или QR-код. Второй человек открывает её, и дальше можно перетаскивать файлы, целые папки, текст и скриншоты из буфера обмена.

🔗 Поделиться позже. Выбираете файлы и получаете ссылку. Любой, кто её откроет, видит список и скачивает то, что ему нужно, когда ему удобно. Те, кто уже скачал, раздают куски дальше — как в торренте, только прямо в браузере.

Как идут байты

Сервер у TFile только сигнальный: небольшой WebSocket на Node.js, через который два браузера обмениваются SDP и ICE-кандидатами. Сами файлы идут по WebRTC data channel, а он всегда зашифрован (DTLS). Если прямое соединение не получается (строгий NAT, корпоративная сеть), трафик идёт через TURN-сервер (eturnal). Ему достаются уже зашифрованные данные: прочитать их он не может и ничего не хранит.

Чтобы было видно, что сервер не встал посередине, оба участника видят код безопасности: SHA-256 от DTLS-отпечатков обоих SDP, 40 бит, которые показываются как 12 цифр. Совпадают коды — сертификаты никто не подменял.

Грабли: SCTP и большие очереди

Первая версия просто писала в канал, пока bufferedAmount не дойдёт до 4 МБ. На loopback в Chrome оказалось, что SCTP отправляет всё накопленное разом: пачка около 2 МБ теряет пакеты, и передача стоит 2–3 секунды.

Решение — своё управление потоком поверх канала:

  • получатель шлёт ack каждые 128 КБ;

  • отправитель держит «в полёте» не больше окна: 2,5 × лучшая недавняя скорость × минимальный RTT;

  • окно не меньше 512 КБ и не больше 16 МБ.

В локальной сети окно остаётся минимальным, а на далёком маршруте через TURN растёт вместе с измеренной скоростью.

Большие файлы и обрывы

  • Файлы от 16 МБ записываются по мере получения в приватную файловую систему браузера (OPFS), а не в память. Поэтому предел — свободное место на диске, а не RAM.

  • Обрыв на минуту (телефон сменил сеть, ноутбук проснулся) не убивает передачу: при новом соединении стороны в hello сообщают, что у кого есть, и передача продолжается с нужного места.

  • Для страховки от собственных багов (докачка, распаковка, запись на диск) после передачи сверяется CRC-32 всего файла.

Читаемые ссылки без изменений на сервере

UUID по телефону не продиктуешь. Теперь ссылка выглядит как brave-quiet-otter-7k3x: два прилагательных и существительное (по 256 слов в каждом списке, то есть по 8 бит) плюс 4 символа Crockford base32 (20 бит). Всего 44 бита, около 17 триллионов вариантов.

Этих 44 бит хватает, чтобы закодировать их прямо в UUID с фиксированным префиксом, без хеширования. Браузер переводит слова в UUID и обратно сам, поэтому сервер не менялся совсем, а старые ссылки с UUID продолжают работать. Подобрать чужую ссылку перебором не выйдет: каждая попытка — это запрос к серверу с лимитом запросов на IP в nginx.

Пароль, которого сервер не видит

Пароль не отправляется никуда. Браузер выводит из ссылки и пароля через PBKDF2-SHA256 (200 000 итераций) другой ID комнаты, и только его видит сервер. С неверным паролем человек попадает в пустую комнату.

Но молча ждать в пустой комнате — плохой UX. Поэтому в ссылку добавлены ещё 8 бит той же деривации (?p=7c): по ним браузер сразу говорит «Неверный пароль» в 255 случаях из 256. Компромисс осознанный: тот, у кого есть ссылка, может офлайн отсеять столько же вариантов, но каждый оставшийся всё равно стоит PBKDF2 и запроса к серверу.

«Поделиться позже»: торрент в браузере

Самое интересное.

Ссылка — это хеш. Ссылка выглядит так: /share#<SHA-256 манифеста>. Манифест — это список файлов и SHA-256 каждого куска. Хеш лежит во фрагменте после #, поэтому не попадает ни на сервер, ни в логи, ни в Referer. Сервер видит только ID комнаты, выведенный из хеша. Манифест может прислать любой участник: если он не совпал со ссылкой, отправителя отключают.

Куски. Каждый файл режется на свои куски от 256 КБ до 16 МБ, не больше 2048 на файл. Можно скачать один файл и не трогать соседние. Каждый кусок проверяется по SHA-256. Порядок — сначала самые редкие (rarest first), со случайным выбором при равенстве, чтобы участники не собирали одни и те же куски.

Почему отдельный файл на кусок. В OPFS createWritable() пишет во временный swap-файл до close(). Значит, один большой файл, который пишется по месту, нельзя раздавать другим, пока он докачивается. Поэтому каждый кусок — отдельный файл, а итоговый файл — это Blob из этих файлов, без копирования.

Счёт за трафик. TURN используется только для соединений с самим раздающим, и одновременно через relay он обслуживает не больше двух человек. Скачивающие между собой связываются только напрямую, иначе одна популярная ссылка могла бы съесть бюджет на AWS.

Лимиты. Раздающий может ограничить, сколько человек качают одновременно (это проверяет сервер), сколько всего и как долго работает ссылка. При лимитах скачавшие файлы дальше не раздают, поэтому все байты идут от раздающего, и его вкладка следит за соблюдением.

Честные ограничения

  • Это P2P, поэтому вкладка с файлами должна оставаться открытой: ваша или того, кто уже скачал.

  • В «Поделиться позже» участники одной ссылки видят IP-адреса друг друга — как в любом торренте. Скачивающий может отключить раздачу, и тогда он соединяется только с раздающим.

  • iOS усыпляет фоновые вкладки через несколько секунд. Передача ждёт возвращения на страницу, а пока она идёт, экран не гаснет (Wake Lock).

Стек

  • Фронтенд: React 19 с SSR, Redux Toolkit, Tailwind.

  • Сервер: Node.js и ws.

  • TURN: eturnal.

  • Тесты: около 500 юнит-тестов на node:test с фейками WebRTC и OPFS, плюс прогоны в headless Chrome и Firefox через CDP, где результат сверяется по SHA-256.

TFile бесплатный, без рекламы и трекинга. Код закрыт. Буду очень рад обратной связи, особенно с телефонов, Safari и Firefox: что сломалось, что неудобно, чего не хватает.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.