Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7d74526
fix(upstream): preserve verified API 21/23 Gradle SDK contracts (#45)
Regstar2 Oct 25, 2026
ed03766
fix(updater): retry after failure, manual and foreground checks, stri…
Regstar2 Oct 25, 2026
7d4ad12
fix(updater): reject insecure and untrusted release redirects (#45)
Regstar2 Oct 25, 2026
0d1d33f
feat(updater): manual check and trusted browser fallback in proxy set…
Regstar2 Oct 25, 2026
8e46a3d
fix(release): derive manifest minimumSdk from validated Telegram Grad…
Regstar2 Oct 25, 2026
13dc204
test(release): validate packaged APK SDK against upstream floor (#45)
Regstar2 Oct 25, 2026
8cde646
fix(updater): persist pending offer across process restart without 12…
Regstar2 Oct 25, 2026
7c8328c
test(ci): pinned/new Telegram matrix, fail-closed SDK and APK compile…
Regstar2 Oct 25, 2026
1ee1776
ci(upstream): compile candidate before updating main pin (#45)
Regstar2 Oct 25, 2026
50de139
docs: correct stable release, API 21/23 and updater recovery paths (#45)
Regstar2 Oct 25, 2026
219c2ff
test(ci): protect updater lifecycle, stable metadata and SDK invarian…
Regstar2 Oct 25, 2026
c84ffd4
fix(ci): repair complete release metadata SDK validation block (#45)
Regstar2 Oct 25, 2026
238266a
fix(release): complete APK minimum SDK check (#45)
Regstar2 Oct 25, 2026
3caa34d
fix(release): remove accidental duplicate tail; validate SDK cleanly …
Regstar2 Oct 25, 2026
f2f5234
fix(release): remove accidental duplicate tail and preserve APK SDK a…
Regstar2 Oct 25, 2026
6696dea
docs: align current release source pin with upstream config (#45)
Regstar2 Oct 25, 2026
183afab
docs(updater): document Android HTTP routing, trust policy and accept…
Regstar2 Oct 25, 2026
5b4c8a2
fix(android): bridge core settings to standalone updater without reve…
Regstar2 Oct 25, 2026
b511d36
fix(android): register standalone update actions during Application b…
Regstar2 Oct 25, 2026
c0e3dca
fix(android): route settings updater actions through TgWsProxyControl…
Regstar2 Oct 25, 2026
a373f2d
test(ci): assert core-to-standalone updater action bridge (#45)
Regstar2 Oct 25, 2026
dcc9056
fix(updater): discard stale pending offer when latest release require…
Regstar2 Oct 25, 2026
2b06916
fix(ci): preserve bootstrap controller-start contract (#45)
Regstar2 Oct 25, 2026
a0ad27b
fix(release): retry unpublished Telegram versions without overwriting…
Regstar2 Oct 25, 2026
fd25874
test(release): cover automatic retry, stable revision and no-overwrit…
Regstar2 Oct 25, 2026
e7d506c
fix(sync): retry missing stable release after validated pin was commi…
Regstar2 Oct 25, 2026
29bd048
test(ci): execute release retry and stable-revision regression matrix…
Regstar2 Oct 25, 2026
0e31148
fix(release): prevent overwriting existing signed APK and stable mani…
Regstar2 Oct 25, 2026
7c6a5c4
fix(tests): correct PowerShell interpolated case labels (#45)
Regstar2 Oct 25, 2026
21aefe5
docs: describe fully scheduled Telegram release and recovery without …
Regstar2 Oct 25, 2026
00dc184
test(ci): enforce immutable signed release assets on automatic retrie…
Regstar2 Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ jobs:
$apkSha = (Get-FileHash $apk -Algorithm SHA256).Hash.ToLowerInvariant()
$commit = (git rev-parse HEAD).Trim()
$download = "https://github.com/$($env:REPOSITORY)/releases/download/$tag/Telegram-WSP-release.apk"
# Feed SDK is derived from the actual prepared Telegram modules.
$sdkVersions = @()
foreach ($module in @('TMessagesProj', 'TMessagesProj_AppStandalone')) {
$gradle = Get-Content ".work/telegram/$module/build.gradle" -Raw
$sdkMatches = [regex]::Matches($gradle, '(?m)^[ \t]*minSdkVersion[ \t]+(\d+)[ \t]*\r?\n')
if ($sdkMatches.Count -ne 1) { throw "Ambiguous minSdkVersion in $module." }
$sdkVersions += [int]$sdkMatches[0].Groups[1].Value
}
if ($sdkVersions[0] -ne $sdkVersions[1] -or $sdkVersions[0] -notin @(21, 23)) {
throw "Unsupported or inconsistent minSdkVersion: $($sdkVersions -join ', ')."
}

$latest = [ordered]@{
versionName = [string]$upstream.telegramVersion
Expand All @@ -155,7 +166,7 @@ jobs:
apk = $download
apkSha256 = $apkSha
publishedAt = (Get-Date).ToUniversalTime().ToString('o')
minimumSdk = 21
minimumSdk = $sdkVersions[0]
packageId = 'org.telegram.messenger.web'
}
[IO.File]::WriteAllText('dist/latest.json', ($latest | ConvertTo-Json -Depth 4) + [Environment]::NewLine)
Expand Down Expand Up @@ -215,26 +226,23 @@ jobs:
)
$assets += @(Get-ChildItem 'dist/source' -File | ForEach-Object { $_.FullName })

# Published tags and their signed APK/feed are immutable. A failed
# build may be retried only while its tag has not been published.
gh release view $env:RELEASE_TAG --repo $env:GITHUB_REPOSITORY *> $null
$exists = $LASTEXITCODE -eq 0

if ($exists) {
gh release upload $env:RELEASE_TAG @assets --clobber --repo $env:GITHUB_REPOSITORY
if ($LASTEXITCODE -ne 0) { throw 'gh release upload failed.' }
gh release edit $env:RELEASE_TAG --title $env:RELEASE_TITLE --notes-file 'dist/release-notes.md' --repo $env:GITHUB_REPOSITORY
if ($LASTEXITCODE -ne 0) { throw 'gh release edit failed.' }
} else {
$arguments = @('release', 'create', $env:RELEASE_TAG) + $assets + @(
'--title', $env:RELEASE_TITLE,
'--notes-file', 'dist/release-notes.md',
'--target', $env:RELEASE_COMMIT,
'--repo', $env:GITHUB_REPOSITORY
)
if ($env:PRERELEASE -eq 'true') { $arguments += '--prerelease' }
& gh @arguments
if ($LASTEXITCODE -ne 0) { throw 'gh release create failed.' }
if ($LASTEXITCODE -eq 0) {
throw "Release $env:RELEASE_TAG already exists; refusing to overwrite signed assets."
}

$arguments = @('release', 'create', $env:RELEASE_TAG) + $assets + @(
'--title', $env:RELEASE_TITLE,
'--notes-file', 'dist/release-notes.md',
'--target', $env:RELEASE_COMMIT,
'--repo', $env:GITHUB_REPOSITORY
)
if ($env:PRERELEASE -eq 'true') { $arguments += '--prerelease' }
& gh @arguments
if ($LASTEXITCODE -ne 0) { throw 'gh release create failed.' }

- name: Release summary
shell: pwsh
env:
Expand Down
52 changes: 49 additions & 3 deletions .github/workflows/trusted-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,30 @@ jobs:
run: ./scripts/ci.ps1

integration:
name: Reproduce Telegram overlay
name: Overlay and APK (${{ matrix.telegram }})
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
telegram: [pinned, telegram-13-0-3]

steps:
- name: Checkout
uses: actions/checkout@v7.0.1
with:
persist-credentials: false

- name: Select Telegram 13.0.3 regression fixture
if: matrix.telegram == 'telegram-13-0-3'
shell: pwsh
run: |
$upstream = Get-Content config/upstream.json -Raw | ConvertFrom-Json
$upstream.pinnedCommit = '0aae959806c57be8ad457980f2a5c6277ff18635'
$upstream.telegramVersion = '13.0.3'
$upstream.telegramBuild = 7183
$upstream | ConvertTo-Json -Depth 4 | Set-Content config/upstream.json

- name: Set up Java
uses: actions/setup-java@v5
with:
Expand All @@ -59,7 +73,7 @@ jobs:

- name: Install Android SDK components
shell: bash
run: sdkmanager "platforms;android-35" "ndk;27.2.12479018"
run: sdkmanager "platforms;android-35" "platforms;android-36" "ndk;27.2.12479018"

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
Expand All @@ -73,3 +87,35 @@ jobs:
- name: Re-run repository checks on prepared tree
shell: pwsh
run: ./scripts/ci.ps1

- name: Reject incompatible SDK anchor
shell: pwsh
run: |
$path = '.work/telegram/TMessagesProj/build.gradle'
$original = Get-Content $path -Raw
$pattern = '(?m)^ minSdkVersion (21|23)$'
$modified = [regex]::Replace($original, $pattern, ' minSdkVersion 42')
if ($modified -eq $original) { throw 'Test fixture could not change minSdk.' }
try {
Set-Content $path $modified -NoNewline
$rejected = $false
try {
./scripts/apply-integration.ps1
} catch {
if ($_.Exception.Message -notmatch 'minSdk/targetSdk contract is unsupported') {
throw "Unexpected rejection: $($_.Exception.Message)"
}
$rejected = $true
}
if (-not $rejected) { throw 'Unsupported API 42 integration was accepted.' }
} finally {
Set-Content $path $original -NoNewline
}

- name: Compile Telegram-WSP prototype APK
shell: pwsh
env:
# Public Telegram upstream fallback values; compile-only CI, no release signing.
TELEGRAM_API_ID: '4'
TELEGRAM_API_HASH: '014b35b6184100b085b0d0572f9b5103'
run: ./scripts/build-apk.ps1 -SkipPrepare
75 changes: 31 additions & 44 deletions .github/workflows/upstream-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ jobs:
shell: pwsh
run: ./scripts/ci.ps1

- name: Compile candidate APK before changing main
if: steps.upstream.outputs.changed == 'true'
shell: pwsh
env:
TELEGRAM_API_ID: ${{ secrets.TELEGRAM_API_ID }}
TELEGRAM_API_HASH: ${{ secrets.TELEGRAM_API_HASH }}
run: ./scripts/build-apk.ps1 -SkipPrepare

- name: Commit validated upstream pin
if: steps.upstream.outputs.changed == 'true'
id: commit
Expand All @@ -129,54 +137,33 @@ jobs:
REQUESTED_REVISION: ${{ inputs.revision }}
run: |
$config = Get-Content 'config/upstream.json' -Raw | ConvertFrom-Json
$requestedRevision = if ($null -eq $env:REQUESTED_REVISION) { '' } else { $env:REQUESTED_REVISION.Trim() }
if (-not [string]::IsNullOrWhiteSpace($requestedRevision) -and $requestedRevision -notmatch '^\d+$') {
throw 'revision must contain decimal digits only.'
}

if ([string]::IsNullOrWhiteSpace($requestedRevision)) {
$releaseJson = & gh release list --repo $env:GITHUB_REPOSITORY --limit 100 --json tagName,isDraft,isPrerelease
if ($LASTEXITCODE -ne 0) { throw 'Failed to list GitHub Releases while resolving the WSP revision.' }
$releaseData = (($releaseJson -join [Environment]::NewLine) | ConvertFrom-Json)
$knownRevisions = @(
foreach ($release in $releaseData) {
if ($release.isDraft -or $release.isPrerelease) { continue }
if ([string]$release.tagName -match '^v.+-wsp\.(\d+)$') {
[int]$Matches[1]
}
}
)
$revision = if ($knownRevisions.Count -eq 0) {
'1'
} else {
[string](($knownRevisions | Measure-Object -Maximum).Maximum)
}
} else {
$revision = $requestedRevision
}

$changed = '${{ steps.upstream.outputs.changed }}' -eq 'true'
$releaseRef = if ($changed) { '${{ steps.commit.outputs.sha }}' } else { (git rev-parse HEAD).Trim() }
$tag = "v$($config.telegramVersion)-wsp.$revision"
if ($releaseRef -notmatch '^[0-9a-f]{40}$') { throw 'Invalid validated release ref.' }

& gh release view $tag --repo $env:GITHUB_REPOSITORY *> $null
$releaseExists = $?
# Include draft and prerelease tags so an occupied tag can never be overwritten.
$releaseJson = & gh release list --repo $env:GITHUB_REPOSITORY --limit 100 --json tagName,isDraft,isPrerelease
if ($LASTEXITCODE -ne 0) { throw 'Failed to enumerate GitHub Releases.' }
$releaseList = $releaseJson -join [Environment]::NewLine
$forced = $env:FORCE_RELEASE -eq 'true'
$required = $forced -or ($changed -and -not $releaseExists)

"release_required=$($required.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT
"release_ref=$releaseRef" >> $env:GITHUB_OUTPUT
"revision=$revision" >> $env:GITHUB_OUTPUT

Write-Host "Tag: $tag"
Write-Host "Release exists: $releaseExists"
Write-Host "Release required: $required"
Write-Host "Release ref: $releaseRef"

# A missing release is expected on first publication. Do not propagate
# the non-zero exit code from 'gh release view' to the pwsh process.
exit 0

$requested = if ($null -eq $env:REQUESTED_REVISION) { '' } else { $env:REQUESTED_REVISION.Trim() }
$decisionText = & ./scripts/resolve-upstream-release.ps1 `
-TelegramVersion ([string]$config.telegramVersion) `
-UpstreamChanged $changed `
-ForceRelease $forced `
-RequestedRevision $requested `
-ReleaseListJson $releaseList `
-ReleaseRef $releaseRef
$decision = $decisionText | ConvertFrom-Json

"release_required=$($decision.releaseRequired.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT
"release_ref=$($decision.releaseRef)" >> $env:GITHUB_OUTPUT
"revision=$($decision.revision)" >> $env:GITHUB_OUTPUT

Write-Host "Release tag: $($decision.tag)"
Write-Host "Reason: $($decision.reason)"
Write-Host "Release required: $($decision.releaseRequired)"
Write-Host "Release ref: $($decision.releaseRef)"
release:
name: Publish validated Telegram-WSP update
needs: sync
Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ Telegram-WSP использует собственные название, packa

Стадия: **MVP**.

Текущий публичный релиз: **[v12.10.5-wsp.2](../../releases/tag/v12.10.5-wsp.2)**.
Текущий публичный релиз: **[v12.10.6-wsp.2](../../releases/tag/v12.10.6-wsp.2)**.

| Компонент | Текущее состояние |
|---|---|
| Telegram upstream | 12.10.5, build 7105 |
| Telegram commit | `dc780e81ed1261c369c27870e8e0999a1eb0b600` |
| Telegram upstream | 12.10.6, build 7112 |
| Telegram commit | `f2908b14133bbffbf7ab04f641ecb5bfaf533242` |
| tgwsproxy-core commit | `8016d1b56210e8edc69da9d637416b70b30971a8` |
| Android package | `org.telegram.messenger.web` |
| Встроенный TgWsProxy | Работает через локальный listener |
Expand All @@ -50,7 +50,7 @@ Telegram-WSP использует собственные название, packa
| Автообновление приложения | Реализовано через GitHub Releases |
| Upstream sync | Автоматическая проверка DrKLO/Telegram с integration gates |

Релиз `v12.10.5-wsp.2` включает новый экран управления маршрутами, поддержку Cloudflare-доменов, WARP/AmneziaWG-профиля и Worker pools. Production APK публикуется вместе с контрольными суммами и точными исходными компонентами сборки.
Релиз `v12.10.6-wsp.2` включает новый экран управления маршрутами, поддержку Cloudflare-доменов, WARP/AmneziaWG-профиля и Worker pools. Production APK публикуется вместе с контрольными суммами и точными исходными компонентами сборки.

## Возможности

Expand Down Expand Up @@ -83,7 +83,7 @@ Telegram-WSP использует собственные название, packa

Для готового APK:

- Android **5.0 / API 21** или новее;
- Для последней опубликованной сборки Telegram 12.10.6: Android **5.0 / API 21** или новее. Telegram upstream 13.0.3 требует **Android 6.0 / API 23**; будущие релизы на его основе не должны заявлять поддержку Android 5.0;
- доступ в интернет;
- разрешение Android на установку APK не из магазина.

Expand Down Expand Up @@ -221,9 +221,11 @@ Telegram-WSP проверяет:
https://github.com/Regstar2/telegram-wsp/releases/latest/download/latest.json
```

не чаще одного раза в 12 часов.
автоматически не чаще одного раза в 12 часов **после успешной проверки**. После сетевой ошибки повторная автоматическая попытка допускается через 10 минут; кнопка ручной проверки не зависит от этого интервала.

Если `versionCode` новой сборки выше установленного, приложение предлагает обновление. После согласия APK скачивается, проверяется и передаётся системному installer.
В интерфейсе «Встроенный прокси» доступны «Проверить обновления Telegram-WSP» и «Открыть релизы Telegram-WSP». Последний пункт открывает официальный GitHub Releases в браузере, если прямой HTTPS-доступ updater отсутствует: локальный MTProto frontend не является HTTP(S)-прокси. Автоматическая проверка возобновляется при возврате Telegram на экран.

Если `versionCode` новой сборки выше установленного, приложение предлагает обновление (с точным WSP release tag и ссылкой на release notes). После согласия APK скачивается, проверяется и передаётся системному installer.

Нумерация Telegram-WSP учитывает как Telegram build, так и WSP revision:

Expand All @@ -242,6 +244,8 @@ versionCode = telegramBuild * 1000 + wspRevision * 10 + 9

Если overlay не применяется или проверки не проходят, новая версия Telegram не должна становиться публичным Telegram-WSP-релизом.

Workflow запускается по расписанию без участия пользователя. Если upstream pin уже обновлён, но подписанный stable-релиз ещё не опубликован (например, предыдущий Windows release job завершился ошибкой), следующая проверка повторно запускает release-сборку. Уже существующий stable tag/подписанный APK не перезаписывается; WSP revision наследуется от последней опубликованной стабильной ревизии.

## Разработка

Рабочий Telegram checkout и core создаются локально:
Expand Down
9 changes: 9 additions & 0 deletions integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ TELEGRAM_API_HASH=<your-api-hash>

`afatDebug` не используется: соответствующий library build включает `DEBUG_VERSION=true` и `DEBUG_PRIVATE_VERSION=true`.

## Android updater и сетевой маршрут

- `TgWsProxyBootstrap` проверяет `latest.json` и скачивает APK стандартным `HttpURLConnection` поверх HTTPS. Это обычный Android/system HTTP(S) route; он **не использует** локальный MTProto frontend `127.0.0.1:1443` как HTTP proxy.
- Разрешены GitHub Releases и GitHub release asset CDN; прочие HTTPS redirect destinations блокируются, а SHA-256, package, signing certificate и `versionCode` повторно проверяются перед installer.
- Если GitHub недоступен в текущей сети, ручная проверка сообщает ошибку. Пункт «Открыть релизы Telegram-WSP» переводит на официальные GitHub Releases через установленный браузер, который может использовать собственную поддерживаемую сетевую маршрутизацию. Успешность этого обходного пути зависит от среды пользователя.
- Автоматическая проверка: интервал 12 часов после успешно проверенного feed и минимум 10 минут после попытки; ручная проверка доступна в настройках встроенного прокси. Найденный manifest хранится локально на время интервала и повторно валидируется после перезапуска процесса.
- Для Telegram 12.10.6 принят minSdk 21, для Telegram 13.0.3 — minSdk 23. Overlay не понижает upstream SDK. Модель совместимости release проверяется по `aapt`, но upgrade и сохранность данных требуют Android device acceptance.
- Эти проверки в `scripts/ci.ps1` защищают контракт исходников. Они не заменяют instrumented UI/network tests, подписанную release-сборку и установку поверх предыдущего release.

## Release signing

Постоянный release key создаётся один раз:
Expand Down
Loading
Loading