# Match

> Set up Match for iOS code signing certificate management

- **Type:** Skill
- **Install:** `agentstack add skill-greenstevester-fastlane-skill-match`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [greenstevester](https://agentstack.voostack.com/s/greenstevester)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [greenstevester](https://github.com/greenstevester)
- **Source:** https://github.com/greenstevester/fastlane-skill/tree/main/skills/match

## Install

```sh
agentstack add skill-greenstevester-fastlane-skill-match
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

## Code Signing with Match

Set up Fastlane Match to manage iOS code signing certificates and provisioning profiles in a shared Git repository.

### Pre-flight Checks
- Fastlane installed: !`fastlane --version 2>/dev/null | grep "fastlane " | head -1 || echo "✗ Not installed - run: brew install fastlane"`
- Fastfile exists: !`ls fastlane/Fastfile 2>/dev/null && echo "✓ Found" || echo "✗ Not found - run /setup-fastlane first"`
- Existing Matchfile: !`ls fastlane/Matchfile 2>/dev/null && echo "✓ Already configured" || echo "○ Not configured yet"`
- Git available: !`git --version 2>/dev/null | head -1 || echo "✗ Git not installed"`

### Arguments: ${ARGUMENTS:-setup}

---

## What is Match?

Match stores your iOS certificates and provisioning profiles in a **private Git repository**, encrypted with a passphrase. Benefits:

- **Team sharing**: Everyone uses the same certificates (no more "works on my machine")
- **CI/CD ready**: Clone the repo in your pipeline, no manual cert management
- **Revoke protection**: Prevents accidental certificate revocation
- **Audit trail**: Git history shows who changed what and when

---

## Step 1: Create a Private Git Repository

Create a **private** repository to store your encrypted certificates:

```bash
# GitHub CLI (recommended)
gh repo create certificates --private

# Or manually at github.com/new (select Private)
```

**Repository naming conventions:**
- `certificates` or `ios-certificates`
- `fastlane-certs`
- `{company}-signing`

> **Security**: This repo will contain encrypted certificates. Keep it private and limit access to team members who need to build the app.

---

## Step 2: Initialize Match

Run match init to create your Matchfile:

```bash
fastlane match init
```

When prompted:
1. **Storage mode**: Select `git`
2. **Git URL**: Enter your private repo URL (e.g., `git@github.com:yourorg/certificates.git`)

This creates `fastlane/Matchfile`:

```ruby
git_url("git@github.com:yourorg/certificates.git")

storage_mode("git")

type("development") # Default type, can be overridden per-lane

# app_identifier(["com.yourcompany.app"])  # Optional: limit to specific apps
# username("user@example.com")              # Optional: Apple ID
```

---

## Step 3: Generate Certificates

Generate certificates for each distribution type:

### Development (for debugging on devices)
```bash
fastlane match development
```

### App Store (for TestFlight and App Store)
```bash
fastlane match appstore
```

### Ad Hoc (for direct device distribution)
```bash
fastlane match adhoc
```

**First run prompts:**
1. **Apple ID**: Your Apple Developer account email
2. **Passphrase**: Create a strong passphrase to encrypt certificates (save this securely!)

> **Important**: Save the passphrase in a password manager. You'll need it for CI/CD and new team members.

---

## Step 4: Integrate with Fastfile

Update your lanes to use Match before building:

```ruby
default_platform(:ios)

platform :ios do
  desc "Sync all certificates"
  lane :sync_signing do
    match(type: "development")
    match(type: "appstore")
  end

  desc "Install signing assets on CI (read-only)"
  lane :certificates do
    setup_ci                                 # temp keychain on CI; no-op locally
    match(type: "appstore", readonly: true)
  end

  desc "Build for TestFlight"
  lane :beta do |options|
    setup_ci                                 # temp keychain on CI; no-op locally
    match(type: "appstore", readonly: true)
    increment_build_number unless options[:skip_build_increment]
    gym(scheme: "YourApp", export_method: "app-store")
    pilot(skip_waiting_for_build_processing: true)
  end

  desc "Build for App Store"
  lane :release do
    setup_ci                                 # temp keychain on CI; no-op locally
    match(type: "appstore", readonly: true)
    increment_build_number
    gym(scheme: "YourApp", export_method: "app-store")
    deliver(submit_for_review: false, force: true)
  end
end
```

**Key pattern**: Use `readonly: true` in build lanes to prevent accidental certificate regeneration.

**CI keychain**: `setup_ci` creates a temporary keychain on CI runners (and is a no-op locally) so `match` can import certs without keychain-permission errors. Call it before `match` in any lane that runs on CI.

---

## Step 5: Onboard Team Members

New team members run:

```bash
# Clone and decrypt existing certificates (readonly)
fastlane match development --readonly
fastlane match appstore --readonly
```

They'll need:
1. Access to the private certificates repository
2. The Match passphrase
3. Apple Developer team membership

**Readonly mode** ensures they can't accidentally revoke or regenerate certificates.

---

## Step 6: CI/CD Setup

Set these environment variables in your CI/CD system:

```bash
# Required
MATCH_PASSWORD="your-match-passphrase"
MATCH_GIT_URL="git@github.com:yourorg/certificates.git"

# For App Store Connect (choose one method)
# Method 1: App-specific password
FASTLANE_USER="your@appleid.com"
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"

# Method 2: API Key (recommended for CI)
APP_STORE_CONNECT_API_KEY_KEY_ID="ABC123"
APP_STORE_CONNECT_API_KEY_ISSUER_ID="xyz-xyz-xyz"
APP_STORE_CONNECT_API_KEY_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
```

### GitHub Actions Example

```yaml
- name: Install certificates
  run: fastlane ios certificates   # runs setup_ci + match(readonly: true)
  env:
    MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
    MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
```

### Xcode Cloud

Add to `ci_scripts/ci_post_clone.sh`:
```bash
# Install Fastlane and sync certificates
brew install fastlane
fastlane ios certificates   # setup_ci + match(readonly: true)
```

Set `MATCH_PASSWORD` in Xcode Cloud environment variables.

---

## Troubleshooting

### "Couldn't decrypt the repo"
Wrong passphrase. Verify `MATCH_PASSWORD` is correct.

### "Code signing error: No provisioning profiles"
Run `fastlane match appstore` (without `--readonly`) to generate profiles.

### "Your certificate has been revoked"
Someone revoked certs in Apple Developer portal. Regenerate:
```bash
fastlane match nuke development  # Removes all development certs
fastlane match development       # Regenerates
```

### "Multiple teams found"
Specify team in Matchfile:
```ruby
team_id("ABCD1234")
```

### "Unable to find app with bundle identifier"
Register the app first (SKU and username are required):
```bash
fastlane produce create -u your@appleid.com -a com.yourcompany.app -n "Your App Name" --sku YOUR_SKU
```

---

## Match Commands Reference

```bash
# Setup
fastlane match init                    # Create Matchfile
fastlane match development             # Generate dev certs
fastlane match appstore                # Generate App Store certs
fastlane match adhoc                   # Generate Ad Hoc certs

# Team use (readonly - won't modify certs)
fastlane match development --readonly
fastlane match appstore --readonly

# Maintenance
fastlane match nuke development        # Revoke all dev certs
fastlane match nuke distribution       # Revoke all dist certs
fastlane match change_password         # Change encryption passphrase

# Debugging
fastlane match development --verbose   # Detailed output
```

---

## Security Best Practices

1. **Private repository**: Never make the certificates repo public
2. **Strong passphrase**: Use 20+ characters, store in password manager
3. **Limit access**: Only team members who build should have repo access
4. **Rotate periodically**: Change passphrase annually with `match change_password`
5. **API keys over passwords**: Use App Store Connect API keys for CI/CD

---

## Files Created

```
fastlane/
└── Matchfile          # Match configuration

# In your certificates repo:
certs/
├── development/       # Development certificates
└── distribution/      # App Store/Ad Hoc certificates
profiles/
├── development/       # Development provisioning profiles
├── appstore/          # App Store provisioning profiles
└── adhoc/             # Ad Hoc provisioning profiles
```

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [greenstevester](https://github.com/greenstevester)
- **Source:** [greenstevester/fastlane-skill](https://github.com/greenstevester/fastlane-skill)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-greenstevester-fastlane-skill-match
- Seller: https://agentstack.voostack.com/s/greenstevester
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
