mirror of
https://github.com/by-sonic/tglock.git
synced 2026-10-06 17:25:59 +03:00
fix: маршруты, медиа и Worker; Android APK и статический ARM64 CLI (#60)
* fix: address routing, media, worker and platform issues * ci: use available Android tools and verify Windows CLI artifact * fix(gui): return a result from asynchronous stop command * test(android): verify installed APK and proxy lifecycle on emulator * fix(media): use native MTProto for CDN203 and retain recent diagnostics * Fix owned WebSocket split after transport boxing * Clarify CDN transport and connection status guarantees * Recognize accessible Android power button labels * chore(release): prepare 2.0.0-beta.15 with verified draft publication --------- Co-authored-by: babin <Thartewerner536e@engineer.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# Android (experimental)
|
||||
|
||||
Issue #9 is implemented as a Tauri Android application sharing the current Rust
|
||||
proxy engine and UI with the desktop application. The old Android PR scaffold is
|
||||
retained, but its stale engine and desktop code are not imported.
|
||||
|
||||
## Install a test APK
|
||||
|
||||
Open this PR's **Android APK** check, then the workflow run's **Artifacts** section.
|
||||
Download `tglock-android-arm64-debug`, unzip it, and install the `.apk` on an
|
||||
ARM64 Android 7.0+ device. GitHub requires signing in to download CI artifacts.
|
||||
No compiler or Android Studio is needed on the phone. The artifact expires after
|
||||
14 days; maintainers can rerun the workflow to create a fresh build.
|
||||
|
||||
This is an automatically debug-signed test build, not a Play Store release.
|
||||
Different CI runs can use different debug signing keys: if Android rejects an
|
||||
update because the signatures differ, uninstall the previous test build first.
|
||||
Uninstalling deletes settings and changes the proxy secret, so reconnect Telegram
|
||||
with the new link. A future production release needs a stable signing key.
|
||||
|
||||
1. Open TGLock and press **Включить защиту**.
|
||||
2. Accept the proxy in Telegram when prompted. If opening Telegram fails, return
|
||||
to TGLock and use **Открыть Telegram** or **Скопировать ссылку**.
|
||||
3. Keep LAN access off when Telegram runs on this same phone (`127.0.0.1`).
|
||||
4. To stop, return through the ongoing notification and press **Выключить**.
|
||||
|
||||
## Lifecycle and limitations
|
||||
|
||||
The native foreground service starts only for an explicitly started proxy and
|
||||
stops when its Rust accept loop finishes, including a normal Stop action. It is
|
||||
not stopped by Activity destruction or rotation. Android 14+ declares the
|
||||
`specialUse` service type, its dedicated permission, and a subtype describing the
|
||||
user-controlled local proxy. Notification permission denial does not prevent the
|
||||
foreground service from running; Android still exposes it in its task manager.
|
||||
|
||||
The service uses `START_NOT_STICKY`: after Android kills the process or the user
|
||||
force-stops it, it does not restart a notification without a Rust engine. Open
|
||||
TGLock and enable protection again. No boot receiver or automatic background
|
||||
restart is installed. Vendor battery management and network changes may still
|
||||
interrupt connections. The app does not claim to be a device-wide VPN.
|
||||
|
||||
The proxy secret lives in the app's private configuration directory. Desktop
|
||||
installs migrate an existing valid legacy secret, preserving saved Telegram
|
||||
links. The link-copy control intentionally contains this secret; the public
|
||||
report-copy control includes only counters, port, route and mode.
|
||||
|
||||
## Build and verification
|
||||
|
||||
CI uses Java 17, Android SDK 36, NDK 28, Rust 1.88, and the locked npm/Rust
|
||||
manifests. `npm run tauri -- android build --debug --apk --target aarch64 --ci`
|
||||
bundles the frontend inside a signed APK; no development web server is required.
|
||||
CI verifies the signature and the presence of each architecture's Rust library.
|
||||
The x86_64 build is installed on an Android 15 emulator. The bounded smoke checks
|
||||
Activity launch, process survival, and crash/ANR logs. When UIAutomator exposes
|
||||
the WebView buttons, it also checks Start, five seconds in the background, Stop,
|
||||
restart, and explicit force-stop/relaunch. It verifies the foreground service and
|
||||
performs a real SOCKS5 greeting through ADB port forwarding to the Rust listener.
|
||||
If buttons are inaccessible after a bounded wait, CI explicitly reports the
|
||||
lifecycle checks as skipped; a launch-only pass is not lifecycle evidence.
|
||||
`android-emulator-smoke-evidence` retains the exact result, UI dumps, service
|
||||
state and logcat. This does not test automatic low-memory eviction, battery
|
||||
behavior, Telegram connectivity, or a physical phone.
|
||||
|
||||
Checked-in `gen/android` contains the native source and Gradle wrapper. Tauri's
|
||||
machine-specific generated glue, SDK paths, native build output and signing files
|
||||
remain ignored. Run the same build command locally after installing Tauri's
|
||||
[Android prerequisites](https://v2.tauri.app/start/prerequisites/#android).
|
||||
|
||||
No physical-phone or Telegram end-to-end test has been performed by this change.
|
||||
Before promoting it beyond an experimental APK, test Android 13 notification
|
||||
permission grant/denial, Android 14+ service startup, Start/Stop/restart, switching
|
||||
to Telegram for at least 10 minutes, rotation, Activity recreation, process death,
|
||||
Wi-Fi/mobile-data handover, and restoration with the same persisted secret.
|
||||
|
||||
Native bridging follows [Tauri mobile plugins](https://v2.tauri.app/develop/plugins/develop-mobile/)
|
||||
and uses [Tauri opener](https://v2.tauri.app/plugin/opener/) for `tg://` links.
|
||||
+28
-10
@@ -201,22 +201,38 @@ Ping приходит в читающую половину, а отвечает
|
||||
|
||||
1. сохранённый успешный маршрут;
|
||||
2. точный Telegram IP с `kwsN` или `kwsN-1` в TLS SNI и WebSocket Host;
|
||||
3. дополнительный Telegram IP, если он определён;
|
||||
4. системный DNS;
|
||||
5. явно настроенный пользователем Cloudflare Worker.
|
||||
3. явно настроенный пользователем Cloudflare Worker;
|
||||
4. дополнительные Telegram IP и варианты хоста;
|
||||
5. системный DNS (кроме CDN DC203).
|
||||
|
||||
Поддерживаются DC1–5 и media/CDN DC203. DC203 использует WebSocket-host DC2,
|
||||
но подключается к собственному IP.
|
||||
Поддерживаются DC1–5 и CDN DC203. Для DC203 первым используется обычный MTProto
|
||||
TCP к закреплённому `91.105.192.100:443`; этот сервер может принимать MTProto,
|
||||
не принимая TLS/WebSocket. Резерв через Worker соединяется с тем же IP.
|
||||
WebSocket-попытки также сохраняют закреплённый адрес. DNS fallback на DC2 для DC203 запрещён:
|
||||
успешный WebSocket handshake с другим DC не доставляет CDN-запрос в нужный
|
||||
датацентр. Worker также получает именно IP DC203.
|
||||
|
||||
После ошибки маршрут получает exponential cooldown от 30 секунд до 30 минут.
|
||||
Успешный маршрут становится первым для следующего соединения того же DC и
|
||||
типа трафика.
|
||||
типа трафика. Попытки запускаются с интервалом 250 мс, не больше трёх
|
||||
одновременно на соединение; остальные отменяются после первого успеха.
|
||||
Если все маршруты на паузе, возвращается причина и время до повторной
|
||||
попытки. Новое подключение клиента не обходит cooldown. Ошибка upstream
|
||||
после handshake также снимает предпочтение маршрута и добавляет cooldown.
|
||||
|
||||
## TLS policy
|
||||
|
||||
Проверка сертификатов и hostname никогда не отключается. При подключении к
|
||||
заданному Telegram IP TCP destination отделён от URI host: TLS продолжает
|
||||
проверять сертификат настоящего `kws*.web.telegram.org`.
|
||||
проверять сертификат настоящего `kws*.web.telegram.org`. Используется rustls
|
||||
с провайдером ring и встроенными WebPKI roots; системный OpenSSL для ядра
|
||||
и CLI не нужен. Собственные корневые сертификаты ОС автоматически не
|
||||
подхватываются. Тесты проверяют доверенный сертификат, SNI, неверное имя
|
||||
хоста и недоверенного издателя.
|
||||
|
||||
Это относится к TLS-маршрутам. Прямой CDN TCP — отдельный транспорт MTProto,
|
||||
а не TLS с отключённой проверкой. Он доступен только для точного назначения
|
||||
DC203. Для произвольных адресов такой обход политики не добавляется.
|
||||
|
||||
Cloudflare Worker принимается только как пользовательская настройка. TGLock
|
||||
не загружает и не скрывает публичные списки чужих доменов.
|
||||
@@ -229,9 +245,11 @@ Worker должен принимать WebSocket на:
|
||||
/apiws?dst=<telegram-ip>&dc=<dc-id>
|
||||
```
|
||||
|
||||
и проксировать бинарные frames в TCP `<telegram-ip>:443`. Рекомендуется
|
||||
добавить собственную авторизацию до стабильного релиза; поэтому Worker
|
||||
остаётся расширенной опцией alpha-версии.
|
||||
и проксировать бинарные frames в TCP `<telegram-ip>:443`. Ответ 101 отправляется
|
||||
после открытия upstream TCP; таймаут/отказ — HTTP 504/502. Список назначений
|
||||
фиксирован, текстовые сообщения отклоняются. Очередь входящих записей ограничена
|
||||
1 МиБ и 256 сообщениями. Авторизация токеном требует собственного клиента:
|
||||
штатный TGLock токен Worker пока не передаёт.
|
||||
|
||||
Запись в сокет Telegram обязана быть последовательной: следующий чанк уходит
|
||||
после того, как записан предыдущий, и только когда писатель к этому готов
|
||||
|
||||
@@ -37,11 +37,13 @@
|
||||
|
||||
### Проверка, что воркер жив
|
||||
|
||||
Открой в браузере `https://tglock.имя.workers.dev/apiws`. Должно вернуться `expected a websocket upgrade` — это правильный ответ: значит код развёрнут и работает, просто браузер пришёл обычным запросом.
|
||||
Открой в браузере `https://tglock.имя.workers.dev/apiws`. Должно вернуться `expected a websocket upgrade` (HTTP 426). Это проверяет только публикацию скрипта. Соединение Worker → Telegram обычный GET **не проверяет**.
|
||||
|
||||
Если вернулось `not found` — проверь, что путь именно `/apiws`. Если ошибка про `cloudflare:sockets` — у воркера слишком старая дата совместимости, поставь в **Settings → Compatibility date** сегодняшнюю.
|
||||
|
||||
> **Разворачивал воркер до 2.0.0-beta.14 — обнови скрипт.** В прежней версии запись в сокет Telegram шла без ожидания предыдущей и без backpressure. Пока в клиенте голодала отправка, через воркер не проходило настоящего потока вверх и это не проявлялось; после того как голодание починили в beta.12, поток появился.
|
||||
> **Обнови скрипт при переходе с beta.14.** Теперь Worker ждёт открытия TCP к Telegram перед ответом 101: отказ возвращает 502, таймаут за 3 секунды — 504. Очередь записи ограничена 1 МиБ / 256 сообщениями; переполнение закрывает соединение с кодом 1009, ошибки сокета — 1011. Старый Worker мог показать успешное соединение ещё до попытки подключения к Telegram и не ограничивал очередь сообщений.
|
||||
|
||||
Для установки через Wrangler есть [`worker/wrangler.toml`](../worker/wrangler.toml). Из каталога `worker` можно выполнить `npx wrangler deploy` в своём Cloudflare-аккаунте. Эта команда публикует Worker; локальные тесты ничего не публикуют.
|
||||
|
||||
## Подключение в TGLock
|
||||
|
||||
@@ -54,13 +56,13 @@ tglock-cli --worker tglock.имя.workers.dev
|
||||
tglock-cli --worker первый.workers.dev --worker второй.workers.dev
|
||||
```
|
||||
|
||||
Worker всегда пробуется **последним**, после всех маршрутов Telegram. Пока обычные маршруты живы, трафик через него не пойдёт, и это осознанно: чужая инфраструктура в цепочке — это лишнее звено, а не улучшение.
|
||||
Настроенный Worker участвует в ограниченном параллельном переборе маршрутов: его проверка начинается вслед за первым Telegram-маршрутом, без ожидания всех таймаутов. Успешный маршрут запоминается. При быстром ответе Telegram Worker не нужен; при медленном прямом маршруте может победить Worker. Поэтому добавляй только свой домен или домен доверенного оператора.
|
||||
|
||||
## Ограничение доступа
|
||||
|
||||
Адрес воркера сам по себе секрет, но лучше поставить токен: **Settings → Variables → Add variable**, имя `TGLOCK_TOKEN`, значение — любая длинная строка.
|
||||
Адрес Worker не является механизмом авторизации. Для своего клиента можно задать секрет `TGLOCK_TOKEN` в настройках Cloudflare и передавать его параметром `token`.
|
||||
|
||||
Пока переменная не задана, проверка токена выключена. Когда задана — воркер начнёт отвечать `403` без параметра `?token=`. Клиент TGLock этот параметр пока не отправляет, так что включать токен есть смысл, если ты правишь и сам скрипт, и адрес.
|
||||
Штатный клиент TGLock параметр `token` пока не отправляет. **Не задавай TGLOCK_TOKEN для штатного клиента:** это приведёт к HTTP 403. При отсутствии переменной любой, кто знает домен, может использовать Worker для разрешённых адресов Telegram и расходовать лимиты твоего аккаунта.
|
||||
|
||||
Независимо от токена воркер соединяется **только** с семью адресами Telegram, которые запрашивает TGLock. Любой другой `dst` получает `403`, так что открытым TCP-прокси он не станет.
|
||||
|
||||
@@ -76,6 +78,7 @@ Sec-WebSocket-Protocol: binary
|
||||
- `dst` — адрес Telegram, к которому нужно подключиться по TCP на порт 443;
|
||||
- `dc` — номер датацентра, для логов;
|
||||
- **подпротокол `binary` обязательно нужно подтвердить в ответе** — без этого клиент разорвёт рукопожатие;
|
||||
- ответ 101 означает, что TCP к разрешённому Telegram IP уже открыт; HTTP 502/504 означает отказ/таймаут этого подключения;
|
||||
- дальше бинарные frames пересылаются в обе стороны без изменений;
|
||||
- TLS до самого воркера обеспечивает Cloudflare.
|
||||
|
||||
@@ -85,4 +88,6 @@ Sec-WebSocket-Protocol: binary
|
||||
|
||||
Скрипт написан по контракту, вычитанному из исходников клиента, и путь с параметрами закреплён тестом `connects_through_the_documented_worker_contract` — он поднимает локальный сервер, который ведёт себя ровно так, как описано выше, и проверяет, что туннель через него поднимается и данные доходят в обе стороны.
|
||||
|
||||
Чего этот тест не проверяет: развёрнутый воркер в самом Cloudflare. Если что-то не сойдётся с их API — [открой issue](https://github.com/by-sonic/tglock/issues/new), поправлю.
|
||||
`npm run test:worker` дополнительно исполняет настоящий файл Worker с заменой платформенных API: проверяет готовность TCP, отказ, таймаут, обе стороны передачи, порядок записи, переполнение и закрытие сокета. API `opened` и `close()` описаны в [документации Cloudflare](https://developers.cloudflare.com/workers/runtime-apis/tcp-sockets/).
|
||||
|
||||
Эти тесты не проверяют развёрнутый Worker в Cloudflare и доступность Telegram из конкретного региона. Если ошибка остаётся, приложи версию клиента, диагностическую строку и HTTP/close-код Worker. Секрет прокси и приватные адреса Worker перед публикацией убери.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# Открытые issues: исправления и проверка, 19 сентября 2026
|
||||
|
||||
База: `8617d25`, версия `2.0.0-beta.14`. Проверены все семь открытых issues
|
||||
и существующие PR #36 (Android), #49 (секрет GUI). Эта работа готовится как PR,
|
||||
без слияния, публикации релиза и автоматического закрытия жалоб.
|
||||
|
||||
## Что изменено
|
||||
|
||||
| Issue | Подтверждённая проблема / выполненная работа | Проверка и предел вывода |
|
||||
|---|---|---|
|
||||
| #59 — 11 тысяч неудачных соединений | Все маршруты в cooldown раньше приводили к немедленной новой попытке. Теперь пауза соблюдается; зависшие соединения не блокируют резервные | Тесты времени, числа параллельных попыток, отмены проигравших. Доступность Telegram у автора issue не проверена |
|
||||
| #58 — Worker, туннели 0/1 | Worker отвечал 101 до открытия TCP; ошибки после handshake терялись | Worker 502/504 и диагностические WebSocket close-коды, Rust-тесты раннего закрытия и reset. HTTP 426 проверяет только публикацию Worker |
|
||||
| #50 — ни один маршрут не работает | Последовательные TCP/TLS таймауты задерживали резерв; кратковременные туннели ошибочно сохраняли предпочтение маршрута | Ограниченный параллелизм, cooldown после upstream failure, ошибки Worker. Нельзя обещать обход, когда недоступны и Telegram, и собственный Worker |
|
||||
| #42 — Android через LAN | Прежние duplex-исправления уже в main; потеря ошибок upstream скрывала дальнейший отказ | Независимый Android-compatible криптографический вектор, фрагментированные init и одновременная передача 128/256 КиБ, диагностика разрывов. Нужна проверка Play Market-клиента в сети репортёра |
|
||||
| #32 — медиа | DC203 fallback через DNS попадал в DC2, хотя CDN имеет свой DC и ключи; на закреплённом CDN IP WebSocket не отвечает, но обычный MTProto TCP работает | DC203 использует точный CDN IP по TCP с преобразованием transport-шифрования; Worker остаётся резервом. Проверка живым req_pq/resPQ и тесты обеих сторон. Реальные фотографии аккаунта репортёра не проверены |
|
||||
| #53 — статическая aarch64 | Системный OpenSSL мешал самостоятельной musl-сборке | rustls + ring, ARM64 CI с проверкой ELF и запуском CLI, артефакт и OpenWrt-инструкция. Физический NanoPi R4S не проверен |
|
||||
| #9 — Android | Старый PR отстал от main, сборка требовала ручной настройки; жизненный цикл сервиса мог останавливать прокси или оставлять ложное уведомление | Перенос актуального ядра, foreground service по состоянию прокси, APK CI, постоянный секрет в каталоге приложения. HyperOS и длительная фоновая работа требуют устройства |
|
||||
|
||||
Дополнительно: журнал больше не замолкает после первых 64 разных событий;
|
||||
Unicode-секрет больше не вызывает panic; GUI сохраняет секрет
|
||||
в каталоге приложения с миграцией прежнего файла; npm lock обновляет уязвимый
|
||||
транзитивный `nanoid` без смены версии приложения.
|
||||
|
||||
## Протокол и доверие
|
||||
|
||||
- TLS проверяет имя из WebSocket URI даже при подключении к закреплённому IP.
|
||||
Переход на rustls использует встроенные WebPKI roots; системные пользовательские
|
||||
корневые сертификаты автоматически не импортируются.
|
||||
- Прямой маршрут CDN203 использует обычный MTProto TCP к одному закреплённому
|
||||
адресу, без TLS-обёртки. Ключи и шифрование содержимого Telegram не меняются;
|
||||
снимается только transport-obfuscation локального прокси и накладывается
|
||||
transport-obfuscation сервера. Если провайдер блокирует CDN IP, нужен Worker.
|
||||
- [Telegram WebSocket](https://core.telegram.org/mtproto/transports#websocket)
|
||||
является потоком байтов. Границы TCP read и WS messages не обязаны совпадать
|
||||
с MTProto-пакетами; дополнительный парсер пакетов не добавлялся.
|
||||
- [CDN DC](https://core.telegram.org/cdn) требует соответствующего назначения;
|
||||
успешный handshake другого DC не доказывает работоспособность медиа.
|
||||
- Worker остаётся опциональной инфраструктурой пользователя. Домен не является
|
||||
авторизацией; `TGLOCK_TOKEN` не поддерживается штатным клиентом.
|
||||
|
||||
## Воспроизводимые проверки
|
||||
|
||||
```text
|
||||
npm ci
|
||||
npm run test:worker
|
||||
npm run build
|
||||
cargo fmt --check
|
||||
cargo clippy --locked --all-targets -- -D warnings
|
||||
cargo test --locked --all-targets
|
||||
cargo clippy --locked --no-default-features --features cli --all-targets -- -D warnings
|
||||
cargo test --locked --no-default-features --features cli --lib --bins
|
||||
```
|
||||
|
||||
CI дополнительно проверяет MSRV 1.88, состав macOS GUI bundle, статическую
|
||||
ARM64 musl-сборку, подпись/содержимое Android debug APK и запуск на Android 15
|
||||
эмуляторе. Пропуск UI lifecycle отмечается отдельно от успешного запуска. Конкретные результаты
|
||||
и ссылки на прогоны фиксируются в описании PR после завершения CI.
|
||||
|
||||
Worker: `npm run test:worker` исполняет настоящий deployment-файл с подменой
|
||||
только Cloudflare API; `wrangler deploy --dry-run` проверяет сборку без публикации.
|
||||
Это не тест живого Cloudflare-аккаунта. Отдельный тест Rust поднимает локальный
|
||||
WebSocket и проверяет совместимость клиента с контрактом Worker.
|
||||
|
||||
Для проверки реального бинаря без Telegram-аккаунта добавлен
|
||||
[независимый Node.js probe](LIVE_PROBE.md). Он проверяет `req_pq_multi → resPQ`,
|
||||
nonce и целостность ответа; это ещё не авторизация и не скачивание медиа.
|
||||
|
||||
## Что нужно проверить на устройствах
|
||||
|
||||
Android: установка APK, старт/стоп, ссылка в Telegram, сохранение секрета после
|
||||
перезапуска, передача текста/медиа, работа в фоне 15 минут и политика батареи.
|
||||
OpenWrt: запуск CLI на NanoPi R4S, подключение нескольких устройств по LAN,
|
||||
перезапуск сервиса с прежним секретом. Инструкции: [Android](ANDROID.md),
|
||||
[OpenWrt](OPENWRT.md).
|
||||
|
||||
Issues о пользовательских симптомах остаются открыты до этой проверки.
|
||||
Наличие исправленного дефекта и зелёных регрессий не доказывает, что в конкретной
|
||||
сети отсутствует дополнительная блокировка.
|
||||
@@ -1,5 +1,8 @@
|
||||
# TGLock 2.0 issue audit
|
||||
|
||||
Актуальный разбор семи открытых issues и границ проверки:
|
||||
[19 сентября 2026](ISSUES_2026-09-19.md). Ниже сохранены исторические записи.
|
||||
|
||||
Проверено 29 июля 2026 года: все 15 issues и 5 pull requests, существовавшие
|
||||
в репозитории на момент аудита.
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Manual protocol probe
|
||||
|
||||
`scripts/probe_proxy.mjs` uses only Node.js built-ins and is independent of the
|
||||
Rust transport helpers. It connects to an already running TGLock listener at
|
||||
`127.0.0.1`, sends one unauthenticated `req_pq_multi` through a secret-protected
|
||||
obfuscated2 padded-intermediate stream, and validates the returned `resPQ`, its
|
||||
request nonce, and the lengths of its TL fields. It stops before creating an
|
||||
authorization key. No Telegram account, API ID, API hash or login is needed.
|
||||
|
||||
The secret is read from an explicitly supplied file and is never printed. Use
|
||||
the same secret file as the running CLI. Do not paste proxy links or secret
|
||||
values into public logs.
|
||||
|
||||
```sh
|
||||
# Offline validation first: no network access.
|
||||
node scripts/probe_proxy.mjs --self-test
|
||||
|
||||
# Start a local CLI separately, using a persistent secret file.
|
||||
tglock-cli --port 18080 --secret-file /private/path/tglock-secret
|
||||
|
||||
# Ordinary DC, media route, and CDN route; run each explicitly.
|
||||
node scripts/probe_proxy.mjs --port 18080 --secret-file /private/path/tglock-secret --dc 2
|
||||
node scripts/probe_proxy.mjs --port 18080 --secret-file /private/path/tglock-secret --dc -4 --fragment-size 7
|
||||
node scripts/probe_proxy.mjs --port 18080 --secret-file /private/path/tglock-secret --dc 203
|
||||
```
|
||||
|
||||
On Windows, supply the downloaded CLI executable and a Windows file path in the
|
||||
same commands. Supported DC values are `1` through `5` and `203`; a negative
|
||||
value requests a media route. `--timeout-ms` defaults to 15000 and is bounded at
|
||||
120000. The response is bounded at 2 MiB. `--fragment-size 7` sends small writes
|
||||
with 2 ms gaps to exercise stream fragmentation; TCP can still combine writes.
|
||||
|
||||
A successful JSON report contains `response: "resPQ"`, `nonceMatches: true`, the
|
||||
requested DC, the public RSA fingerprint count, and elapsed time. Exit status 1
|
||||
means connection, timeout, decryption/framing or response validation failed.
|
||||
The ordinary CI suite does not run this live probe.
|
||||
|
||||
Success demonstrates a correctly relayed protocol exchange. It does **not**
|
||||
authenticate the responding server, prove its DC identity, log into an account,
|
||||
or verify message sending, media downloads or Android lifecycle. In particular,
|
||||
`requestedDc` describes the request, not an independently confirmed backend.
|
||||
|
||||
## Isolate CDN transport failures
|
||||
|
||||
To distinguish an unavailable CDN WebSocket endpoint from an unavailable CDN
|
||||
TCP connection, explicitly run:
|
||||
|
||||
```sh
|
||||
node scripts/probe_proxy.mjs --direct-cdn --dc 203
|
||||
```
|
||||
|
||||
This optional mode bypasses local TGLock and connects **only** to the pinned
|
||||
Telegram CDN address `91.105.192.100:443`, using raw obfuscated2 TCP without a
|
||||
proxy secret or TLS. It supports no arbitrary host. It performs the same single
|
||||
unauthenticated exchange and still does not prove account or media operation.
|
||||
The parser accepts trailing bytes after the complete `resPQ` TL object because
|
||||
live CDN replies can include random padding in the declared message length.
|
||||
|
||||
Protocol references: [handshake initiation](https://core.telegram.org/mtproto/auth_key),
|
||||
[obfuscated transports](https://core.telegram.org/mtproto/mtproto-transports),
|
||||
and [unencrypted messages](https://core.telegram.org/mtproto/description#unencrypted-message).
|
||||
@@ -0,0 +1,93 @@
|
||||
# CLI для ARM64 / OpenWrt
|
||||
|
||||
Цель сборки — `aarch64-unknown-linux-musl`: ARM64 Linux, статический бинарь без
|
||||
зависимости от glibc, OpenSSL или WebView. Это подходит для 64-битной прошивки
|
||||
NanoPi R4S и других ARM64-роутеров. MIPS и 32-битный ARM требуют другой сборки.
|
||||
Проверьте архитектуру прошивки командой `uname -m`: ожидается `aarch64`.
|
||||
|
||||
Workflow **Static ARM64 CLI** собирает и запускает тесты на ARM64 runner,
|
||||
проверяет ELF (нет `INTERP` и `NEEDED`), запуск CLI, завершение по SIGTERM и
|
||||
сохранение секрета между запусками. Артефакт содержит бинарь и SHA-256.
|
||||
Release workflow прикладывает такой же проверенный артефакт к будущим релизам.
|
||||
Наличие сборки в PR не означает, что уже опубликован новый релиз.
|
||||
|
||||
Это проверка ARM64 Linux, а не испытание конкретной прошивки OpenWrt или
|
||||
доступности Telegram через вашего провайдера.
|
||||
|
||||
## Установка
|
||||
|
||||
Скачайте артефакт успешного запуска workflow нужного PR либо файл
|
||||
`tglock-cli-aarch64-unknown-linux-musl` из релиза, если он там опубликован.
|
||||
Сверьте SHA-256, скопируйте бинарь на роутер и выполните:
|
||||
|
||||
```sh
|
||||
chmod 755 /usr/bin/tglock-cli
|
||||
/usr/bin/tglock-cli --version
|
||||
mkdir -p /etc/tglock
|
||||
chmod 700 /etc/tglock
|
||||
```
|
||||
|
||||
Создайте `/etc/tglock/tglock.toml`:
|
||||
|
||||
```toml
|
||||
port = 1080
|
||||
lan = true
|
||||
secret_file = "/etc/tglock/secret"
|
||||
# worker = ["your-name.workers.dev"]
|
||||
```
|
||||
|
||||
`lan = true` нужен для телефонов и компьютеров в домашней сети. По умолчанию
|
||||
CLI слушает только loopback. Секрет создаётся при первом запуске и сохраняется
|
||||
в указанном файле; он не должен теряться при перезагрузке или обновлении.
|
||||
|
||||
```sh
|
||||
chmod 600 /etc/tglock/tglock.toml
|
||||
/usr/bin/tglock-cli --config /etc/tglock/tglock.toml
|
||||
```
|
||||
|
||||
В Telegram выберите MTProto и используйте LAN-адрес роутера, порт `1080` и
|
||||
секрет из напечатанной ссылки. `127.0.0.1` на телефоне означает сам телефон.
|
||||
Разрешайте входящий TCP `1080` только из доверенной LAN; не публикуйте порт
|
||||
в WAN. Звонки через UDP эта сборка не реализует.
|
||||
|
||||
## Сервис procd
|
||||
|
||||
После проверки ручного запуска сохраните `/etc/init.d/tglock`:
|
||||
|
||||
```sh
|
||||
#!/bin/sh /etc/rc.common
|
||||
START=95
|
||||
STOP=10
|
||||
USE_PROCD=1
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/bin/tglock-cli --config /etc/tglock/tglock.toml
|
||||
procd_set_param respawn 3600 5 5
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
chmod 755 /etc/init.d/tglock
|
||||
/etc/init.d/tglock enable
|
||||
/etc/init.d/tglock start
|
||||
logread -e tglock
|
||||
```
|
||||
|
||||
Обновление: остановите сервис, замените бинарь после проверки контрольной суммы,
|
||||
сохраните `/etc/tglock`, запустите сервис снова. Для отмены автозапуска используйте
|
||||
`/etc/init.d/tglock stop` и `/etc/init.d/tglock disable`.
|
||||
|
||||
## TLS и маршруты
|
||||
|
||||
Встроенный набор доверенных корневых сертификатов webpki обновляется вместе с
|
||||
бинарём. Проверка сертификата и имени включена: при подключении к закреплённому
|
||||
IP имя Telegram по-прежнему используется для SNI и проверки сертификата.
|
||||
На роутере должно быть установлено правильное время.
|
||||
|
||||
Если все Telegram IP недоступны, нужен доступный маршрут через собственный
|
||||
[Cloudflare Worker](CLOUDFLARE_WORKER.md). Статическая сборка сама по себе не
|
||||
устраняет блокировку всех внешних маршрутов.
|
||||
+72
-46
@@ -1,38 +1,64 @@
|
||||
# Как выпускать релиз
|
||||
|
||||
## Порядок важен
|
||||
## Подготовка через PR
|
||||
|
||||
Токен GitHub Actions создаёт релиз **только на HEAD ветки по умолчанию**. Если тег
|
||||
отстанет от `main` хотя бы на один коммит, публикация упадёт с ошибкой
|
||||
`Resource not accessible by integration` — сообщение про права, хотя права в
|
||||
порядке и дело в положении тега.
|
||||
1. Поднять версию согласованно в шести файлах:
|
||||
- `Cargo.toml` — версия пакета `tglock`;
|
||||
- `Cargo.lock` — версия только пакета `tglock`, без обновления зависимостей;
|
||||
- `tauri.conf.json` — версия приложения и имён установщиков;
|
||||
- `package.json` — версия frontend-пакета;
|
||||
- `package-lock.json` — верхняя версия и `packages[""].version`;
|
||||
- `ui/main.ts` — версия копируемого диагностического отчёта.
|
||||
2. Подготовить описание изменений и проверок, отдельно указав экспериментальные
|
||||
платформы и непроверенные сценарии. Android получает `versionName`/`versionCode`
|
||||
из конфигурации Tauri при сборке; сгенерированный `tauri.properties` не коммитится.
|
||||
3. Проверить финальные изменения PR и слить его в `main` с соблюдением обязательных
|
||||
проверок ветки. Дождаться зелёных проверок и Android workflow на итоговом HEAD
|
||||
`main` до создания тега. APK предыдущего PR-коммита не заменяет артефакт этого
|
||||
коммита: `headSha` Android run должен совпадать с коммитом будущего тега.
|
||||
|
||||
Отсюда единственное жёсткое правило: **тег ставится последним, и пока идёт
|
||||
релиз, в `main` не пушим.**
|
||||
## Тег и сборка
|
||||
|
||||
## Шаги
|
||||
Release workflow этого репозитория допускает тег только на текущем HEAD ветки
|
||||
по умолчанию. Guard сохраняет прежнюю защиту от выпуска другого коммита:
|
||||
ранее расхождение тега и `main` сопровождалось ошибкой публикации
|
||||
`Resource not accessible by integration`.
|
||||
|
||||
1. Влить в `main` всё, что должно попасть в релиз, и дождаться зелёного CI.
|
||||
2. Поднять версию **в двух местах** — `Cargo.toml` и `tauri.conf.json`. Они
|
||||
должны совпадать: имена файлов бандла берутся из `tauri.conf.json`.
|
||||
3. Закоммитить подъём версии и запушить в `main`.
|
||||
4. Убедиться, что больше ничего не уедет: `git ls-remote origin refs/heads/main`
|
||||
должен совпасть с локальным `git rev-parse HEAD`.
|
||||
5. Поставить аннотированный тег на этот же коммит и запушить его:
|
||||
1. Сверить `git ls-remote origin refs/heads/main` и локальный
|
||||
`git rev-parse HEAD` после перехода на финальный коммит `main`.
|
||||
2. Убедиться, что выбранная версия и тег ещё не существуют, затем поставить
|
||||
аннотированный тег на этот коммит:
|
||||
|
||||
```bash
|
||||
git tag -a v2.0.0-beta.N -m "TGLock 2.0.0-beta.N"
|
||||
git push origin v2.0.0-beta.N
|
||||
```
|
||||
|
||||
6. **Ничего не пушить в `main`, пока сборка не закончится.** Правки README,
|
||||
документации, чего угодно — после публикации релиза.
|
||||
3. Пока релиз собирается, не добавлять коммиты в `main`. Guard проверяет HEAD
|
||||
в начале работы и не устраняет гонку после проверки.
|
||||
4. Дождаться **всех** jobs Release. Workflow создаёт **draft** и сохраняет его
|
||||
черновиком при загрузке GUI, CLI и ARM64. Частично загруженный выпуск не должен
|
||||
становиться общедоступным до проверок.
|
||||
|
||||
## Проверить, что выпустили
|
||||
Ручной `workflow_dispatch` запускайте на релизном теге, а не на ветке: часть
|
||||
загрузчиков использует `github.ref_name` как имя релиза.
|
||||
|
||||
Зелёный workflow — это ещё не доказательство. В бетах 2 и 3 сборка была зелёной,
|
||||
а в приложение попадал headless-бинарь вместо графического. Поэтому проверяем
|
||||
содержимое, а не имя файла:
|
||||
## Проверка артефактов и публикация
|
||||
|
||||
Зелёный workflow сам по себе недостаточен. В бетах 2 и 3 в GUI-бандл попадал
|
||||
headless-бинарь, поэтому проверяются содержимое и происхождение:
|
||||
|
||||
- Windows: GUI `.exe` и `tglock-cli-x86_64-pc-windows-msvc.exe`.
|
||||
- macOS: универсальные `.dmg`, `.app.tar.gz` и CLI.
|
||||
- Linux x64: `.AppImage`, `.deb` и CLI.
|
||||
- ARM64 Linux: `tglock-cli-aarch64-unknown-linux-musl` и его `.sha256`;
|
||||
workflow проверяет ELF без динамических зависимостей и запуск бинаря.
|
||||
- Android: скачать ARM64 debug APK из успешного Android run с `headSha`,
|
||||
совпадающим с коммитом тега. Сохранить имя с версией и явной пометкой
|
||||
`android-arm64-debug`, прикрепить APK к тому же draft. Указать экспериментальный
|
||||
статус и ограничения debug-подписи из [ANDROID.md](ANDROID.md).
|
||||
|
||||
Пример проверки скачанного macOS-бандла:
|
||||
|
||||
```bash
|
||||
gh release download vX.Y.Z --repo by-sonic/tglock -p 'TGLock_universal.app.tar.gz' -D /tmp/check
|
||||
@@ -40,35 +66,35 @@ tar -xzf /tmp/check/TGLock_universal.app.tar.gz -C /tmp/check
|
||||
python scripts/verify_bundle_binary.py /tmp/check/TGLock.app/Contents/MacOS/tglock
|
||||
```
|
||||
|
||||
Скрипт ищет внутри бинаря маркеры GUI (`ipc.localhost`, `wry`) и маркеры CLI
|
||||
(`allow-direct`, `secret-file`) и ругается, если в бандле оказался не тот.
|
||||
Проверьте версию скачанного CLI и хотя бы один реальный протокольный обмен
|
||||
через него по [LIVE_PROBE.md](LIVE_PROBE.md). Зафиксируйте, какие платформы
|
||||
исполнены локально, а какие проверены CI. Секрет локального прокси в заметки и
|
||||
публичные артефакты не включается.
|
||||
|
||||
## Если публикация всё-таки упала с `Resource not accessible by integration`
|
||||
Когда набор файлов полон, подписи/контрольные суммы и версии сверены, а release
|
||||
notes готовы, опубликуйте draft. Например:
|
||||
|
||||
Значит, тег разошёлся с `main`. Сверьте:
|
||||
```bash
|
||||
gh release edit vX.Y.Z --repo by-sonic/tglock --draft=false --notes-file release-notes.md
|
||||
```
|
||||
|
||||
Исторически `v2.0.0-beta.*` в этом репозитории публикуются с `prerelease=false`;
|
||||
workflow сохраняет эту настройку. После публикации проверьте публичную страницу
|
||||
релиза, ссылки скачивания и список файлов. Краткий пост об обновлении должен
|
||||
ссылаться на опубликованный релиз и отделять проверенные исправления от
|
||||
экспериментальных платформ.
|
||||
|
||||
## Если сборка или публикация упала
|
||||
|
||||
Сначала прочитайте ошибку и сверяйте коммит тега, `main`, workflow run и версии.
|
||||
Не делайте вывод о причине только из текста `Resource not accessible by
|
||||
integration`: он может относиться и к правам токена.
|
||||
|
||||
```bash
|
||||
git ls-remote origin refs/heads/main 'refs/tags/vX.Y.Z^{}'
|
||||
```
|
||||
|
||||
Если SHA разные — переставьте тег на HEAD и запушьте заново:
|
||||
|
||||
```bash
|
||||
git tag -d vX.Y.Z
|
||||
git push origin :refs/tags/vX.Y.Z
|
||||
git tag -a vX.Y.Z <sha ветки main> -m "TGLock X.Y.Z"
|
||||
git push origin vX.Y.Z
|
||||
```
|
||||
|
||||
Перезапускать упавший workflow бесполезно: он возьмёт тот же отставший тег и
|
||||
упадёт снова.
|
||||
|
||||
## Что защищает автоматически
|
||||
|
||||
Первым шагом релиза идёт задача `guard`: она сверяет тег с HEAD ветки по
|
||||
умолчанию и валится за секунды, не запуская сборки. Она ловит тег, поставленный
|
||||
на старый коммит.
|
||||
|
||||
Она **не** ловит гонку: если запушить в `main` уже после её прохождения, но до
|
||||
конца сборки, публикация упадёт. Ровно так утонул первый заход v2.0.0-beta.8.
|
||||
От этого защищает только правило из первого раздела.
|
||||
Если это ошибка инфраструктуры, повторите упавшие jobs на том же коммите.
|
||||
Если нужна правка исходников, внесите её через PR и выберите новую версию для
|
||||
нового тега. Опубликованные теги и бинарные артефакты не заменяйте: пользователи
|
||||
должны иметь возможность воспроизвести уже выпущенную версию.
|
||||
|
||||
Reference in New Issue
Block a user