# How to Code-Sign Telegram-iOS: Provisioning Profiles, Certificates, and Build Configuration

> Learn to code-sign Telegram-iOS with provisioning profiles and certificates. Configure your app, get a certificate, and use Make.py for automatic or manual signing.

- Repository: [TelegramMessenger/Telegram-iOS](https://github.com/TelegramMessenger/Telegram-iOS)
- Tags: how-to-guide
- Published: 2026-04-07

---

**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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/my-config.json)) containing your application identifiers and team information. The repository ships a template at [`build-system/template_minimal_development_configuration.json`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/build-system/template_minimal_development_configuration.json) that defines the required schema.

```json
{
  "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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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:

```bash
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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/build-system/copy-provisioning-profiles-Telegram.sh) with the profile set type (`development` or `distribution`) to validate the environment variables and copy files:

```bash
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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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:

```bash
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:

```bash
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:

```bash
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:

```bash
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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/my-config.json)** using the template at [`build-system/template_minimal_development_configuration.json`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/copy-provisioning-profiles-Telegram.sh)** with `development` or `distribution` argument to validate and stage profiles.
- **Generate your project** using [`Make.py`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/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.