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.

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 via add_codesigning_common_arguments and 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 .mobileprovision files into build-input/data/provisioning-profiles/, and emits a Bazel BUILD file for the build graph.

  • build-system/Make/GenerateProfiles.py – Handles self-signed certificate workflows by creating temporary keychains, extracting .p12 data, 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.json using the template at build-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 .p12 placement.
  • 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.sh with development or distribution argument to validate and stage profiles.
  • Generate your project using Make.py with the appropriate signing flag (--xcodeManagedCodesigning, --codesigningInformationPath, or --disableProvisioningProfiles).
  • Build the IPA using the build command with --configuration=release_arm64 for 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →