How to Code-Sign Telegram-iOS: Provisioning Profiles, Certificates, and Build Configuration
To code-sign Telegram-iOS, create a configuration JSON with your app identifiers, obtain a signing certificate (or use the fake-codesigning bundle), export provisioning profiles for all targets, and run Make.py with either --xcodeManagedCodesigning for automatic signing or --codesigningInformationPath for manual profile management.
The Telegram-iOS repository uses a sophisticated Bazel-based build system located in build-system/ to orchestrate codesigning across multiple app extensions and build variants. Understanding the Telegram-iOS codesigning workflow is essential for developers targeting physical devices, internal distribution, or the App Store, as the process involves specific environment variables, shell scripts, and Python utilities that validate and inject signing materials into the build.
Creating the Build Configuration File
Create a JSON configuration file (e.g., my-config.json) containing your application identifiers and team information. The repository ships a template at build-system/template_minimal_development_configuration.json that defines the required schema.
{
"bundle_id": "org.yourcompany.Telegram",
"api_id": "your-telegram-api-id",
"api_hash": "your-telegram-api-hash",
"team_id": "YOURTEAMID",
"app_center_id": "0",
"is_internal_build": "true",
"is_appstore_build": "false",
"appstore_id": "0",
"app_specific_url_scheme": "tg",
"premium_iap_product_id": "",
"enable_siri": false,
"enable_icloud": false
}
Replace the placeholders with values from the Apple Developer portal (Team ID) and the Telegram API portal (API ID and hash). The bundle_id must match the identifier used when creating your provisioning profiles.
Obtaining Signing Certificates
The build system in build-system/Make/Make.py supports three distinct certificate strategies, controlled via the add_codesigning_common_arguments function.
Xcode-Managed Signing (Recommended for Personal Builds)
Pass --xcodeManagedCodesigning to Make.py when generating your project. This flag instructs Bazel to emit an Xcode project that relies on Xcode’s automatic provisioning, selecting certificates tied to your Apple ID automatically. This approach requires no manual certificate export or placement in the repository.
Fake-Codesigning for CI and Simulators
Use the self-signed certificate bundle shipped in build-system/fake-codesigning/certs/. This strategy works for simulator builds and ad-hoc distribution without a paid Apple Developer account. When you pass --disableProvisioningProfiles to Make.py, the build system automatically copies the fake-codesigning resources and skips real signing.
Custom Certificates
Export your valid Apple certificate from Keychain as a .p12 file (without a password) and place it into build-system/fake-codesigning/certs/. The scripts treat this identically to the fake certificate, allowing you to use real signing identities while maintaining the repository's isolated codesigning architecture.
Collecting and Validating Provisioning Profiles
Telegram-iOS requires separate mobileprovision files for each target: the main App, Share Extension, Widget, NotificationService, NotificationContent, Intents, WatchApp, and WatchExtension.
Exporting Profiles from Apple Developer Portal
Export development or distribution profiles from the Apple Developer portal and store them in a directory, e.g., my-profiles/. Each extension requires its own profile with the appropriate entitlements.
Configuring Environment Variables
Set environment variables pointing to each specific profile file before running the validation script:
export DEVELOPMENT_PROVISIONING_PROFILE_APP="MyApp_Dev.mobileprovision"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_SHARE="MyApp_Share_Dev.mobileprovision"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_WIDGET="MyApp_Widget_Dev.mobileprovision"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_NOTIFICATIONSERVICE="MyApp_NotificationService_Dev.mobileprovision"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_NOTIFICATIONCONTENT="MyApp_NotificationContent_Dev.mobileprovision"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_INTENTS="MyApp_Intents_Dev.mobileprovision"
export DEVELOPMENT_PROVISIONING_PROFILE_WATCHAPP="MyApp_WatchApp_Dev.mobileprovision"
export DEVELOPMENT_PROVISIONING_PROFILE_WATCHEXTENSION="MyApp_WatchExtension_Dev.mobileprovision"
Running the Validation Script
Execute build-system/copy-provisioning-profiles-Telegram.sh with the profile set type (development or distribution) to validate the environment variables and copy files:
CODESIGNING_DATA_PATH=my-profiles \
./build-system/copy-provisioning-profiles-Telegram.sh development
This script creates build-input/data/provisioning-profiles/ and generates a Bazel BUILD file consumed by the build system. According to the source code in copy-provisioning-profiles-Telegram.sh, the script strictly validates that all required profile environment variables are present and non-empty before proceeding.
Self-Signed Profile Generation
For fake-codesigning workflows, run build-system/Make/GenerateProfiles.py to inject your self-signed certificate into each .mobileprovision file. This Python script creates a temporary keychain, extracts the certificate from your .p12, and re-signs each provisioning profile to match your local certificate identity.
Generating the Xcode Project or Building the IPA
The Make.py script provides distinct workflows based on your codesigning strategy.
Simulator Builds (No Signing Required)
For simulator-only development where codesigning is not required, disable provisioning entirely:
python3 build-system/Make/Make.py \
--cacheDir="$HOME/telegram-bazel-cache" \
generateProject \
--configurationPath=path/to/my-config.json \
--disableProvisioningProfiles
Xcode-Managed Project Generation
Let Xcode handle certificate selection and provisioning profile matching:
python3 build-system/Make/Make.py \
--cacheDir="$HOME/telegram-bazel-cache" \
generateProject \
--configurationPath=path/to/my-config.json \
--xcodeManagedCodesigning
Manual Signing with Custom Profiles
Point to your validated provisioning profiles directory:
python3 build-system/Make/Make.py \
--cacheDir="$HOME/telegram-bazel-cache" \
generateProject \
--configurationPath=path/to/my-config.json \
--codesigningInformationPath=path/to/my-profiles
Open the generated Telegram.xcodeproj to build and run on device.
Building Distribution IPAs
Produce a signed .ipa for TestFlight or App Store distribution:
python3 build-system/Make/Make.py \
--cacheDir="$HOME/telegram-bazel-cache" \
build \
--configurationPath=path/to/my-config.json \
--codesigningInformationPath=path/to/my-profiles \
--buildNumber=100001 \
--configuration=release_arm64
The build command invokes Bazel with the specified distribution profiles and release configuration.
Understanding the Build Scripts
The Telegram-iOS codesigning process relies on specific utilities that isolate signing data from the source tree:
-
build-system/Make/Make.py– Orchestrates the entire build, parsing configuration viaadd_codesigning_common_argumentsand resolving whether to use Xcode-managed signing, local profiles, or disabled provisioning. -
build-system/copy-provisioning-profiles-Telegram.sh– Validates environment variables against required extension targets, copies.mobileprovisionfiles intobuild-input/data/provisioning-profiles/, and emits a BazelBUILDfile for the build graph. -
build-system/Make/GenerateProfiles.py– Handles self-signed certificate workflows by creating temporary keychains, extracting.p12data, and re-signing provisioning profiles to match the local certificate. -
build-system/prepare-build.sh– Internal helper called during build preparation to copy selected provisioning profiles into the build input tree before Bazel execution.
Summary
- Create
my-config.jsonusing the template atbuild-system/template_minimal_development_configuration.json, providing your Team ID, bundle ID, and Telegram API credentials. - Choose a certificate strategy: Xcode-managed (
--xcodeManagedCodesigning), fake-codesigning (build-system/fake-codesigning/certs/), or custom.p12placement. - Export provisioning profiles for every target (App, Share, Widget, Notifications, Intents, Watch) from the Apple Developer portal.
- Set environment variables (e.g.,
DEVELOPMENT_PROVISIONING_PROFILE_APP) pointing to each profile file. - Run
copy-provisioning-profiles-Telegram.shwithdevelopmentordistributionargument to validate and stage profiles. - Generate your project using
Make.pywith the appropriate signing flag (--xcodeManagedCodesigning,--codesigningInformationPath, or--disableProvisioningProfiles). - Build the IPA using the
buildcommand with--configuration=release_arm64for distribution.
Frequently Asked Questions
Do I need a paid Apple Developer account to build Telegram-iOS?
No, you can build for the iOS simulator using --disableProvisioningProfiles without any Apple Developer account. For physical device testing, you can use the fake-codesigning bundle in build-system/fake-codesigning/ with free provisioning profiles, though this limits distribution to your own registered devices. App Store submission requires a paid membership and valid distribution certificates.
How do I handle provisioning profiles for app extensions?
Each extension (Share, Widget, NotificationService, etc.) requires its own dedicated provisioning profile with specific entitlements. You must export separate .mobileprovision files for each target from the Apple Developer portal, then set corresponding environment variables like DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_SHARE before running copy-provisioning-profiles-Telegram.sh to validate their presence.
Can I build Telegram-iOS for the simulator without any certificates?
Yes. Pass --disableProvisioningProfiles to Make.py when generating the project. According to the repository README, codesigning is not required for simulator-only builds, and the build system will skip all signing steps while still generating a fully functional Telegram.xcodeproj for simulator testing.
What is the difference between fake-codesigning and Xcode-managed signing?
Fake-codesigning uses a self-signed certificate shipped in build-system/fake-codesigning/certs/ and requires running GenerateProfiles.py to re-sign provisioning profiles locally, making it ideal for CI environments or when lacking Apple Developer access. Xcode-managed signing uses --xcodeManagedCodesigning to let Xcode automatically download and match profiles with your Apple ID-associated certificates, which is recommended for development on personal Macs but requires valid Apple Developer credentials.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →