A self-hosted alternative to Fastlane for signing and distributing iOS apps via the App Store Connect API. Designed for modern cloud infrastructure — no Ruby, no fragile toolchain, no per-machine setup headaches.
git clone https://github.com/MichaelProjects/blazelane
cd blazelaneCopy the template and fill it in:
cp create-template.yaml myapp.yaml# App Store Connect API credentials
# Create an API key at: App Store Connect > Users and Access > Integrations > API Keys
system:
issuer_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Issuer ID from the API Keys page
key_id: "XXXXXXXXXX" # 10-character key ID
key:
path: "/path/to/AuthKey_XXXXXXXXXX.p8" # Path to the downloaded .p8 file
# GitLab project where CI/CD variables will be stored
gitlab:
url: "gitlab.com"
project_id: 12345678
auth_key: "glpat-xxxxxxxxxxxxxxxxxxxx" # GitLab personal access token
# Your app and its bundle IDs
# Add one entry per signing target (main app + each extension)
app:
name: "My App"
app_id: "1234567890" # Numeric App ID from App Store Connect
team_id: "XXXXXXXXXX" # Apple Developer Team ID
bundles:
- bundle: "com.example.app"
name: APP_PROVISIONING # Used as the GitLab variable prefix
- bundle: "com.example.app.share"
name: SHARE_EXTENSION_PROVISIONINGblazelane prepare setup --project-cfg myapp.yamlblazelane logs each step as it runs.
If no valid distribution certificate exists, blazelane generates a new RSA 2048 CSR, creates the certificate via the API, and writes a csr_info.json to the working directory containing the private key. Store this file securely — it cannot be recovered if lost.
Once prepare completes, the following variables will be set in your GitLab project:
| Variable | Contents |
|---|---|
DISTRIBUTION_CERT_P12 |
Base64-encoded PKCS#12 archive (cert + private key) |
DISTRIBUTION_CERT_PASSWORD |
Password to import the .p12 |
<PREFIX>_ID |
Profile id for each bundle |
<PREFIX>_PROFILE |
Base64-encoded .mobileprovision for each bundle |
Fastlane works, but it's painful to set up and maintain, especially in CI environments. blazelane takes a different approach:
- Talks directly to the App Store Connect API
- Stores certificates and secrets in your existing secret manager
- Access to secrets is controlled by your IAM provider — no shared passwords or
.envfiles - Single binary, no runtime dependencies
blazelane is split into two distinct phases that map naturally to your workflow.
The prepare phase is a one-time (or infrequent) setup step run from a developer machine. It handles everything that requires human interaction or elevated App Store Connect permissions:
- Generating an RSA 2048 distribution certificate via the App Store Connect API
- Generating provisioning profiles for each bundle ID (main app + extensions)
- Uploading certificates and profiles to your secret manager
After running prepare, all secrets needed for CI are stored securely and centrally. No developer machine state is required to reproduce the build environment.
blazelane prepare setup --project-cfg ./myapp.yamlThe CI phase is designed to run headlessly in any standard CI environment (GitHub Actions, GitLab CI, Cloud Build, etc.). It pulls the certificates and profiles stored by the prepare phase from your secret manager and uses them to sign and distribute your .ipa.
blazelane sign --project-cfg ./myapp.yaml --ipa ./MyApp.ipa
blazelane distribute --project-cfg ./myapp.yaml --ipa ./MyApp.ipa test-flight
blazelane distribute --project-cfg ./myapp.yaml --ipa ./MyApp.ipa app-storeCI runners authenticate to the secret manager via their attached IAM role or service account — no credentials need to be injected manually.
| Step | Status | Notes |
|---|---|---|
| Generate RSA 2048 CSR | ✅ Implemented | |
| Create distribution certificate | ✅ Implemented | |
| Fetch registered bundle IDs | ✅ Implemented | |
| Generate provisioning profiles | ✅ Implemented | One profile per bundle ID |
| Store secrets in GitLab CI/CD variables | ✅ Implemented | |
| Validate before creation (skip if valid cert/profile exists) | Duplicate creation not yet guarded | |
| Detect and regenerate expired profiles | is_valid() exists, auto-regeneration not wired up |
|
| Atomic / resumable setup (fail-safe) | ❌ Not yet | If a step fails the process must be restarted from scratch |
| Feature | Status | Notes |
|---|---|---|
Code signing (re-sign .ipa) |
🔜 Planned | Will fully replace fastlane's signing step |
| Feature | Status | Notes |
|---|---|---|
| Upload to TestFlight | 🔜 Planned | |
| Submit to App Store | 🔜 Planned |
| Backend | Status |
|---|---|
| GitLab CI/CD variables | ✅ Implemented |
| Google Secret Manager | 🔜 Planned |
| Other backends (Vault, etc.) | 🔜 Optional / community |
- Not fail-safe: the
prepare setupflow is not atomic. If any step fails mid-way (e.g. profile creation succeeds but secret upload fails), the process must be run again from the beginning. Idempotency and partial-state recovery are planned. - No duplicate detection: running
prepare setuptwice may create duplicate certificates or profiles in App Store Connect. Validation to check for existing valid resources before creation is planned. - GitLab only: secret storage is currently limited to GitLab CI/CD variables. Google Secret Manager and AWS Secrets Manager support is on the roadmap.
blazelane/
cli/ # CLI entry point and commands (prepare, sign, distribute)
client/ # Generated App Store Connect API client (via progenitor)
openapi.oas.json # App Store Connect OpenAPI spec (filtered to relevant tags)