# Native client update service `cmd/telesrv-update` is a standalone HTTP service that provides two Telegram-compatible update surfaces: - `/current4` and `/files/*` for the built-in Telegram Desktop updater; - `/v1/resolve` for the main `telesrv` process to answer `help.getAppUpdate` for Android, iOS, and other supported builds. The service has no HTTP upload endpoint. Operators publish a release by placing an immutable artifact in the configured `files` directory and atomically replacing `manifest.json`. The catalog validates the file size and SHA-256 before exposing it, so truncated or accidentally replaced packages fail closed. ## Quick start Create the working directories and start with the disabled example catalog: ```powershell New-Item -ItemType Directory -Force data\updates\files Copy-Item deploy\update\manifest.example.json data\updates\manifest.json go run ./cmd/telesrv-update -check go run ./cmd/telesrv-update ``` Check the local endpoints: ```powershell Invoke-RestMethod http://127.0.0.1:2402/readyz Invoke-RestMethod http://127.0.0.1:2402/current4 ``` Connect the main server: ```dotenv TELESRV_UPDATE_PUBLIC_URL=https://updates.example.test TELESRV_UPDATE_SERVICE_URL=http://127.0.0.1:2402 TELESRV_UPDATE_REQUEST_TIMEOUT=2s ``` `PUBLIC_URL` must be reachable by clients and is advertised as `help.getConfig.autoupdate_url_prefix`. `SERVICE_URL` may remain a loopback or private route. When both routes are identical, `SERVICE_URL` may be omitted. Production deployments should place an HTTPS reverse proxy in front of the service without rewriting `/current4` or `/files/*`. Standalone service settings: ```dotenv TELESRV_UPDATE_LISTEN=127.0.0.1:2402 TELESRV_UPDATE_MANIFEST=data/updates/manifest.json TELESRV_UPDATE_FILES_DIR=data/updates/files ``` The manifest is reloaded automatically when its timestamp or size changes. A malformed replacement makes readiness and catalog requests return `503`; it is never combined with the previously validated snapshot. Validate a candidate before atomically replacing the active file: ```powershell go run ./cmd/telesrv-update ` -manifest .\manifest.next.json ` -files .\data\updates\files ` -check ``` ## Telegram Desktop contract TDesktop requests `/current4`. A Windows x64 stable release is represented as: ```json { "win64": { "stable": { "released": 7000007, "link": "/files/tx64upd7000007" } } } ``` The client compares `released` with its numeric `AppVersion`, downloads the artifact with HTTP Range support, verifies the embedded RSA signature, unpacks it, and only then exposes the normal update banner. A regular EXE or ZIP is not a valid update package. Build packages with TDesktop's `Packer` target. The equivalent Windows x64 command is: ```powershell Packer.exe ` -version 7000007 ` -path Telegram.exe ` -path Updater.exe ` -path "modules\x64\d3d\d3dcompiler_47.dll" ` -target win64 ``` It produces `tx64upd7000007` and must report `Signature verified!` before the artifact is published. | Platform | `/current4` key | Typical package name | |---|---|---| | Windows x64 | `win64` | `tx64upd` | | Windows ARM64 | `winarm` | `tarm64upd` | | Windows x86 | `win` | `tupdate` | | macOS Intel | `mac` | `tmacupd` | | macOS Apple Silicon | `armac` | `tarmacupd` | | Linux | `linux` | `tlinuxupd` | ## Update signing The public TDesktop sources contain Telegram's public update key; the matching private key is not published. A custom deployment must establish its own update signing identity: 1. Generate a dedicated RSA-1024 key pair and keep the private key only in a protected build secret store. 2. Provide the private key to TDesktop's local `DesktopPrivate/packer_private.h`. 3. Embed the matching public key in both the client update verifier and Packer. 4. Rebuild the bootstrap client and Packer before publishing updates. 5. Do not rotate the key without a transition client that trusts both identities. The update service never reads the private key and never creates signatures. It checks SHA-256 and serves an artifact that Packer has already signed. A stock TDesktop binary cannot install a package signed only by a custom key; the first custom client build must be distributed out of band. ## HTTP behavior - `/healthz` reports process liveness. - `/readyz` validates the current catalog. - `/current`, `/current1` ... `/current4` return desktop metadata with `Cache-Control: no-cache`. - `/files/` serves only artifacts referenced by the current validated catalog, supports GET/HEAD and Range, and emits an immutable cache policy and a SHA-256-based ETag. - `/v1/resolve` returns a newer application release or `204 No Content`. Unknown package names are not exposed merely because a file exists in the directory. Published package names are immutable: changing an active file makes the endpoint return `503` until a matching manifest snapshot is loaded. ## Android and iOS The main server forwards the client platform, current `app_version`, source, channel, and `lang_code` to `/v1/resolve`. The resolver selects localized notes, does not offer an equal or older version, and applies `url_by_source` when a matching installer/store source is configured. - A standalone Android build may open or install an APK URL, but the APK must be signed with the same Android application signing key as the installed build. - Google Play and other store builds should use the corresponding store URL; this mechanism does not bypass store policy. - iOS may display information returned by `help.getAppUpdate`, but installation still happens through App Store, TestFlight, or MDM. A normal iOS application cannot replace itself from an arbitrary IPA URL. - Set `can_not_skip` only after confirming that the target release is actually available to every affected client. ## Manifest fields - `desktop...build`: numeric TDesktop `AppVersion`. - `file`, `sha256`, `size`: immutable signed artifact and integrity metadata. - `apps...id`: stable positive release identifier. - `version`: value compared with the client's `initConnection.app_version`. - `notes`: localized text keyed by `en`, `ru`, `ru-ru`, and similar codes. - `url_by_source`: source-specific installer or store URL. - `can_not_skip`: whether the client may dismiss the application update. - `disabled`: keep a valid entry as a draft without publishing it. Supported channels are `stable`, `beta`, and `alpha`. See `deploy/update/manifest.example.json` for a complete disabled example.