Secrets Naming Conventions
The names Mainline writes secrets to — and how to adapt when your workflows expect different ones.
The default schema
When Mainline injects code-signing and ASC credentials into a GitHub repository’s Actions secrets, it uses these names by default:
| Secret slot | Default name | What it holds |
|---|---|---|
| Distribution P12 | CERTIFICATE_P12_BASE64 |
Base64-encoded PKCS#12 distribution cert + private key |
| P12 password | P12_PASSWORD |
The P12’s inner password (empty string is valid) |
| App Store provisioning profile | APP_PROFILE_BASE64 |
Base64-encoded .mobileprovision for the app target |
| Widget extension profile (optional) | WIDGET_PROFILE_BASE64 |
Base64-encoded .mobileprovision for a widget extension |
| Share extension profile (optional) | SHARE_PROFILE_BASE64 |
Base64-encoded .mobileprovision for a share extension |
| ASC API key (private key) | APP_STORE_CONNECT_API_KEY_KEY |
Base64-encoded .p8 |
| ASC API key ID | APP_STORE_CONNECT_API_KEY_KEY_ID |
The 10-character key ID |
| ASC API key issuer ID | APP_STORE_CONNECT_API_KEY_ISSUER_ID |
The team’s issuer UUID |
These names match what the test_app reference workflow consumes, so a fresh project that copies that workflow needs no schema customisation.
Per-app Secret Schema
Many existing workflows use different names — MATCH_PASSWORD, IOS_CERT_BASE64, ASC_KEY_ID, etc. Rewriting your workflow to match Mainline’s defaults isn’t necessary, and isn’t recommended — your workflow file is yours, and rewriting it is the kind of churn that creates merge conflicts and breaks rollback.
Instead, Mainline keeps a per-app Secret Schema that maps each slot to whatever name your workflow already expects. The schema lives on the app record, so different apps in your portfolio can use different conventions side by side.
Editing the schema
- Open the App Detail view.
- Tap Secrets → Edit Schema.
- For each slot, enter the secret name your workflow reads. For example, if your workflow has
$, set the P12 slot toIOS_DIST_CERT. - Save. Future injections write to those names; injections from before the schema change aren't retroactively renamed.
Auto-detection
If your workflow already exists and is using the secrets it needs, Mainline can read the YAML and infer the schema:
- In the Secret Schema editor, tap Detect from Workflows.
- Mainline scans
.github/workflows/*.ymlfor$references and proposes a mapping based on the names it recognises (and on conventional naming heuristics like*_P12_*,*PROFILE*,ASC_*). - Review the proposed mapping, adjust any uncertain slots, and save.
This is the recommended path for apps you’re onboarding to Mainline that already have working CI — it’s faster than retyping names and less error-prone than copy-paste.
.p8 encoding
ASC API keys are issued as .p8 PEM files. Some workflows expect the raw PEM contents in the secret, others expect base64-encoded bytes. Mainline’s schema editor has a per-app toggle for this — choose what your workflow’s Upload to TestFlight step expects, and Mainline writes the value in the matching encoding.
If you’re authoring a fresh workflow, the base64-encoded form is slightly more robust because it survives any newline-stripping that GitHub Actions might apply to the secret value during decoding.
Optional vs. required slots
Not every workflow needs every slot. The Secret Schema editor marks each slot as required or optional:
- Required for any TestFlight-uploading workflow: P12, P12 password, app profile, ASC API key + key ID + issuer ID.
- Optional depending on your app: widget profile, share extension profile, additional per-target profiles.
Mainline only writes the slots you’ve populated; unused slots stay absent from the GitHub repo, so you don’t end up with empty WIDGET_PROFILE_BASE64 secrets cluttering the list.
What if a secret already exists with the right name?
If a secret with the same name already exists in the repo, Mainline overwrites it on injection — it’s an idempotent write rather than a “create only if missing.” This is intentional: the most common reason to inject is to refresh a rotated cert or profile, and refusing to overwrite would defeat the purpose.
The Secrets Setup sheet shows the current state of each slot (Present in GitHub / Missing / Stale) so you know what’s about to change before you tap Inject.
Common pitfalls
GitHub Actions silently treats an undefined secret as the empty string — there's no "secret not found" error. If your workflow has $ (note the typo), the cert step will run with an empty input and fail at security import with a confusing error. Auto-detect catches most of these because the proposed mapping won't have an obvious slot for the misspelled name.
If your app has multiple targets that need separately-signed profiles (widget, share extension, watch app), each needs its own slot in the schema. Mainline's defaults cover app + widget + share; for additional targets, add custom slots in the schema editor.
The test_app workflow uses Mainline's default secret names verbatim. If you're starting fresh, copying its env: blocks gives you a known-good naming baseline.