У меня в браузере есть вкладки, которые живут неделями: почта, трекер задач, мессенджер, пара веб-интерфейсов для серверов. Они теряются среди остальных сорока, закрываются вместе с окном и не попадают в переключатель приложений по Cmd+Tab. Обычное решение — завернуть сайт в отдельное приложение. Обычная цена — Electron, то есть свой экземпляр Chromium в каждой обёртке. Pake обещает то же самое одной командой и, по словам README, почти в двадцать раз легче.
У проекта 62 тысячи звёзд на GitHub, история с 14 октября 2022 года и 2241 коммит без учёта слияний. Он даёт программу, которая будет жить у пользователя рядом с почтой и рабочими сессиями. Поэтому я не стал ограничиваться README и прочитал исходники версии 3.17.6 (коммит 19c32e6 от 19 октября 2026 года). Приложение я не собирал и не запускал: всё, что ниже, взято из кода, истории репозитория и файлов его релизов на GitHub.
Вопросов у меня было три: что на самом деле делает «одна команда», откуда берётся малый размер и что окно Pake позволяет сайту, который в нём открыт. На третий вопрос ответ меня удивил, и он же определяет вывод в конце.
Что это и из чего состоит
Pake — это две программы в одном репозитории. Первая — CLI на TypeScript: он принимает адрес и параметры и собирает установщик. Вторая — шаблон настольного приложения на Rust и Tauri 2, который CLI каждый раз компилирует заново, подставив твои настройки.
| Часть | Где лежит | Объём |
|---|---|---|
| CLI | bin/, TypeScript |
5845 строк |
| Приложение | src-tauri/src/, Rust |
6131 строка |
| Скрипты, внедряемые в страницу | src-tauri/src/inject/, JavaScript |
3522 строки |
| Тесты | tests/: модульные, браузерные, интеграционные |
12 278 строк |
У CLI 54 опции: размеры окна, иконка, скрытый заголовок, трей, глобальная клавиша вызова, прокси, свой CSS и JS, режим инкогнито, несколько окон. Готовых приложений в релизах 16, их список лежит в default_app_list.json: ChatGPT, Gemini, YouTube, Notion, X, WeChat и другие. Собирает их конвейер GitHub Actions той же командой pake, которая доступна пользователю.
Проект держится на одном человеке: из 2241 коммита 1397 подписаны его именем Tw93. Второй по числу коммитов участник сделал 301.
Что делает «одна команда»
В README всё выглядит так:
pnpm install -g pake-cli
pake https://github.com --name GitHub
За этой строкой стоит компиляция Rust-проекта на твоей машине. Порядок такой, по bin/builders/BaseBuilder.ts и bin/utils/build-workspace.ts:
- CLI ищет
rustc. Если его нет, спрашивает разрешения и ставит Rust черезhttps://sh.rustup.rs(на Windows — черезwinget). Без терминала, например в CI, не спрашивает, а завершается с ошибкой. - Копирует шаблон
src-tauriво временный каталогpake-build-*. Установленный пакет при этом не меняется. - Пишет в копию конфигурацию: адрес, размеры окна, иконку, твои CSS и JS.
- Запускает
tauri build. Каталогtargetс результатами компиляции общий для всех сборок и защищён файлом блокировки, поэтому вторая сборка идёт быстрее первой.
Время первой сборки CLI называет сам: «5–10 минут», на Windows — «10–15 минут». Таймаут на сборку — 15 минут. На Linux нужны системные пакеты WebKitGTK, на Windows — Visual Studio Build Tools или MinGW (--windows-toolchain gnu). Так что «одна команда» верна только во второй раз. В первый раз это установка Rust-тулчейна и компиляция зависимостей: Cargo.lock перечисляет 567 пакетов.
Для тех, кто не хочет ставить Rust, есть сборка в GitHub Actions: форк, параметры в форме, готовый установщик в артефактах.
Почему пакет маленький и где это не так
Малый размер получается из двух вещей. Первая — в установщик не входит браузерный движок. Tauri открывает страницу в системном webview: WKWebView на macOS, WebView2 на Windows, WebKitGTK на Linux. Вторая — профиль сборки, настроенный на размер, а не на скорость:
[profile.release]
panic = "abort"
codegen-units = 1
lto = "thin"
opt-level = "z"
strip = true
opt-level = "z" — оптимизация по размеру, strip убирает символы, panic = "abort" выкидывает код раскрутки стека. Для обёртки над сайтом скорость Rust-части почти не важна: вся работа идёт в webview.
Обещание «обычно меньше 10 МБ» я сверил по файлам последнего релиза. Вот приложение ChatGPT из V3.17.6:
| Формат | Размер |
|---|---|
.msi для Windows |
3,6 МБ |
.deb для Linux |
4,6 МБ |
.dmg для macOS |
9,5 МБ |
.AppImage для Linux |
76,7 МБ |
.dmg вдвое больше остальных, потому что релизы для macOS собираются с --multi-arch: в одном файле два бинарника, для Intel и для Apple Silicon. AppImage — исключение из обещания: он везёт WebKitGTK с собой, чтобы запускаться на любом дистрибутиве. Сравнение «в 20 раз меньше Electron» по репозиторию не проверить, своих замеров Electron-сборок в нём нет. Порядок величин правдоподобен.
У системного webview есть обратная сторона. Один и тот же сайт открывается тремя разными движками, и на каждом ведёт себя по-своему. Это видно по коду: строка User-Agent по умолчанию на macOS и Linux выдаёт себя за Safari 26, на Windows — за Chrome 147. На Windows и Linux движку дополнительно передаётся --disable-blink-features=AutomationControlled, чтобы сайты не принимали окно за автоматизированный браузер. В заметках к V3.17.3 есть пример такой подгонки: Linux-сборки перестали представляться Chrome, и у YouTube заработали субтитры.
Что Pake делает со страницей
Окно Pake — это не «голый» webview. Перед загрузкой сайта Rust-часть внедряет в него до 13 скриптов, часть из них зависит от опций: в window.rs они подключаются через initialization_script. Самый крупный, event.js на 1666 строк, добавляет горячие клавиши (назад, вперёд, масштаб, копирование адреса), перехватывает скачивания, переписывает window.open и чинит ввод через китайские методы ввода в WebKit.
Строчка README про «удаление рекламы» — это CSS. В style.js лежат правки для 13 групп сайтов, и каждая применяется только на своём домене. Для YouTube правка такая:
#masthead-ad {
display: none !important;
}
Для X скрываются продвигаемые посты ([data-testid="placementTracking"] article) и ссылка на платное продвижение. Это не блокировщик: запросы к рекламным серверам уходят, просто блок не показывается. Свой CSS и JS можно добавить опцией --inject, тогда CLI склеит файлы в custom.js и встроит в приложение.
Отдельная деталь — скачивания. Ссылку на файл перехватывает скрипт и передаёт Rust-команде download_file. Та скачивает файл сама и с версии 3.17.3 подставляет куки и Referer окна, чтобы работали файлы за логином. Тут начинается главный вопрос разбора: какая страница может вызывать такие команды.
Ключевое решение: кто может вызывать команды приложения
В Tauri страница общается с Rust-частью через IPC: вызывает команды по имени. У Pake их 11: скачать файл, показать уведомление, поставить счётчик на иконку в Dock, изменить масштаб, перейти по адресу и ещё несколько. К ним добавляются разрешения плагинов: открыть ссылку в системном браузере, развернуть окно на весь экран. В конфигурации стоит "withGlobalTauri": true, то есть объект window.__TAURI__ есть на каждой странице, открытой в окне. Что из этого реально сработает, решает список разрешений — capability.
С 5 декабря 2024 года, когда вызовы перевели на Tauri 2 (первая версия с этим — V3.0.0 от 19 декабря), в capabilities/default.json было записано:
"remote": {
"urls": ["https://*.*"]
}
Шаблон https://*.* подходит к любому сайту по HTTPS. Собственные команды приложения вообще не были занесены в манифест разрешений и проверку не проходили. Значит, команды Pake мог вызывать не только упакованный сайт, но и любая страница, на которую окно перешло. А переходить окно умеет: on_navigation в window.rs разрешает любые переходы, а опции --force-internal-navigation и --safe-domain специально оставляют внешние ссылки внутри окна, например для входа через корпоративный SSO.
Исправление вышло 30 сентября 2026 года коммитом 211e1eb9 («fix: restrict native IPC to configured origins») и попало в релиз V3.17.3 от 5 октября. Теперь команды занесены в манифест в build.rs, а доступ для сайта строится при запуске из адреса, который был упакован:
let origin = format!("{}://{}{}/*", url.scheme(), host.replace(':', "\\:"), port);
...
capability.identifier = "pake-configured-origins".into();
capability.local = false;
capability.remote = Some(CapabilityRemote { urls: origins });
Права получают схема, хост и порт упакованного адреса, и ничего больше. Поддомены, страницы входа и адреса, куда сайт перенаправил, их не получают. Хост с *, ? или скобками отвергается, чтобы шаблон не превратился в маску. В заметках к релизу автор прямо пишет, что упаковывать нужно конечный адрес, https://mail.google.com, а не https://gmail.com. Иначе почта откроется, но уведомления и скачивания через приложение работать не будут.
Само исправление сделано аккуратно. Есть модульные тесты на разбор адресов, а tests/integration/ipc-security.mjs собирает настоящее приложение, открывает в нём «чужой» источник и проверяет, что ему отказано. Но этот тест работает только на macOS и в CI не запускается: по AGENTS.md его гоняют вручную.
Обновлений у собранных приложений нет
У этой истории есть продолжение, которое важнее самого исправления. В Cargo.toml нет плагина обновлений Tauri, в коде нет проверки новых версий. Приложение, собранное Pake, остаётся таким, каким его собрали. Исправление в CLI не доходит до установленных программ, их нужно пересобрать.
Масштаб можно оценить по GitHub. С 19 декабря 2024 года по 4 октября 2026 года вышло 25 релизов с готовыми приложениями, и их файлы скачали 260 318 раз. Три релиза после исправления скачали 8105 раз. Как поступить с уже установленными обёртками:
- Готовые приложения из релизов — скачать заново из V3.17.3 или новее.
- Собранные самостоятельно — обновить CLI (
pnpm install -g pake-cli@latest) и пересобрать. Версию Pake, которой собрано приложение, по нему самому не узнать: поле версии в установщике заполняет пользователь опцией--app-version, и к Pake оно отношения не имеет. - Обёртки с
--safe-domainили--force-internal-navigation— в первую очередь. Это ровно те окна, где пользователь ходит по чужим доменам.
Что уходит в сеть
Само приложение сторонних адресов в коде не содержит. Кроме упакованного сайта, в Rust-части и скриптах есть только ссылка на репозиторий Pake в меню. Данные сайта, то есть куки и локальное хранилище, лежат в профиле приложения. С --incognito они не сохраняются между запусками. Плагин tauri-plugin-oauth подключён в lib.rs, но разрешений на его команды в наборе нет, и в коде Pake он не вызывается.
CLI ходит в сеть больше, чем ожидаешь:
- При каждом запуске
update-notifierпроверяет новую версию в реестре npm. Интервал проверки в коде — одна минута. - Если не указать
--icon, CLI ищет иконку сайта и отправляет его домен внешним сервисам по очереди, пока один не ответит:
`https://img.logo.dev/${domain}?token=${token}&format=png&size=256`,
`https://cdn.brandfetch.io/${domain}/w/400/h/400?c=${key}`,
`https://logo.clearbit.com/${domain}?size=256`,
`https://www.google.com/s2/favicons?domain=${domain}&sz=256`,
`https://favicon.is/${domain}`,
Публичные ключи logo.dev и Brandfetch записаны прямо в bin/options/icon.ts. Для внутренних имён (localhost, IP-адреса, .local, .lan, .internal) CLI сначала ищет иконку по имени приложения в наборе dashboard-icons на jsDelivr. Если там её нет, внутренний домен уходит тем же пяти сервисам. Упаковал grafana.corp.internal без иконки — пять сторонних сервисов узнали имя хоста. Защита простая: всегда передавать --icon с локальным файлом.
- Зеркала в Китае (
rsproxy.cn,registry.npmmirror.com) включаются только переменнойPAKE_USE_CN_MIRROR=1. Без неё зависимости ставятся из crates.io и npm.
Конвейер проекта собран аккуратно. Сторонние действия GitHub Actions закреплены по хешу коммита, официальные actions/* — по тегу. Собранный dist/cli.js хранится в репозитории, но CI проверяет, что он совпадает с исходниками в bin/. Файл не минифицирован и читается глазами.
Установка и запуск
Нужны Node.js 20.9 или новее (README рекомендует 22) и Rust 1.85 или новее. Сам репозиторий закрепляет тулчейн 1.95.0 в rust-toolchain.toml. На macOS сборка нацелена на версию 14.0. Пример с настройками окна из README:
pake https://weekly.tw93.fun --name Weekly \
--icon https://cdn.tw93.fun/pake/weekly.icns \
--width 1200 --height 800 --hide-title-bar
Для автоматизации есть --json: результат одним JSON-объектом в stdout, предупреждения отдельным массивом. Есть --config app.json со схемой в schema/pake.schema.json для описания приложения файлом. Есть упаковка локальной сборки фронтенда: pake ./dist --name MyTool. Для ИИ-агентов проект держит файл llms.txt с описанием этого контракта и готовый навык для агента с доступом к файлам и командной строке.
Лицензии
До 11 июня 2026 года Pake распространялся по MIT. С версии V3.11.10 он под GPL-3.0-or-later с отдельным исключением в LICENSE-EXCEPTION. Исключение говорит, что приложения, собранные стандартным процессом, не становятся GPL и их можно распространять на любых условиях, в том числе закрытых. Изменённая версия самого Pake остаётся под GPL. Отдельный TRADEMARK.md просит форки не называться Pake.
Зависимости приложения — Tauri и его плагины — под MIT или Apache-2.0. В CLI есть sharp (Apache-2.0) для обработки иконок, а он тянет собранную библиотеку libvips под LGPL-3.0. Она работает только при сборке и в готовое приложение не попадает.
Ограничения и риски
- Установленные приложения не обновляются. Любое исправление безопасности доходит до пользователя только через пересборку.
- Расширенные права действовали почти два года. С V3.0.0 до V3.17.2 команды приложения были доступны любой HTTPS-странице в окне.
- Три движка — три поведения. Сайт, который работает в Chrome, может вести себя иначе в WKWebView или WebKitGTK. CSS-правки в
style.jsнаписаны для 13 групп популярных сайтов, а не для твоего. - Сборка требует Rust. По оценке самого CLI, первая сборка занимает от 5 до 15 минут, а на Linux нужны ещё системные пакеты WebKitGTK.
- Домен уходит сервисам иконок. Без
--iconэто касается и внутренних адресов. - Один основной автор. Больше половины коммитов сделал один человек.
- Тест на изоляцию прав не в CI. Его запускают вручную и только на macOS.
Кому подойдёт
Pake хорош в том, что обещает. Обёртка над сайтом весом 4–10 МБ, собранная одной командой и с нормальными горячими клавишами, — это удобно, и код за ней явно поддерживается: за последние три месяца в репозитории 265 коммитов, у сложных мест подробные комментарии со ссылками на номера задач, а модульных тестов 62 файла.
Мой вывод такой:
- Личные обёртки для своих сайтов и веб-интерфейсов — да. С
--iconиз локального файла, на версии 3.17.3 или новее, без--safe-domain, если в нём нет нужды. - Готовые приложения из релизов — да, но скачанные до 5 октября 2026 года стоит заменить.
- Раздача обёрток сотрудникам — с оглядкой. Обновлений нет, значит, нужен свой процесс пересборки и доставки, иначе следующее исправление до людей не дойдёт.
- Тем, кто пишет на Tauri, — репозиторий стоит прочитать как пример. Выдача прав только точному источнику в
config.rsи история сhttps://*.*до неё хорошо показывают, где в Tauri 2 проходит граница между страницей и приложением.