Guide

Credential custody: where your signing keys actually live

This page is written to be forwarded. If someone has asked you where your app signing material is stored — a security review, an auditor, a client contract, or a colleague who thinks about this more than you do — the answer should be checkable rather than asserted, and it should fit on one page.

The five credentials a release needs

Every iOS and Android release runs on the same five artifacts. They are all bearer credentials: possession is authorisation. There is no password on top, no second factor, and no per-person audit trail. Whoever holds them can ship as you.

CredentialWhat it provesIf it leaks
iOS distribution certificate
dist.p12
Software signed with it came from your Apple team.Severe. Someone can sign software as you. Revoking it kills every profile built on it.
Provisioning profile
.mobileprovision
That certificate may ship that specific app.Minor alone — useless without the private key.
App Store Connect API key
AuthKey_*.p8
You may act on App Store Connect with a given role.Severe. Upload builds, edit your listing. No expiry — lives until revoked.
Android upload keystore
upload.keystore
This bundle came from you.Recoverable — Play can reset an upload key.
Play service account
*.json
You may publish to these Play apps.Severe. Delete the key in Google Cloud and revoke in Play Console.

Who holds each one

A hosted build service signs on its own machines. That is not a criticism — it is what the product is — but it has an unavoidable consequence: the machine doing the signing must have the private key, so your certificate and your API key have to be uploaded and stored by the vendor.

Where it restsHosted serviceleas, local buildleas, cloud build
Certificate and keystoreVendor storageYour diskYour repo secrets
API keysVendor storageYour diskYour repo secrets
Machine that signsVendor fleetYour MacA runner you rent
Who can read themVendor systems and staff with accessYouYou and your CI provider
Revocation if the vendor is breachedRotate everythingNot applicableNot applicable

leas has no upload endpoint. Not “we delete them after the build”, not “they are encrypted at rest” — there is no route that accepts a certificate, because the signing happens where the files already are.

Verify it rather than trusting it

The engine is MIT licensed, so this is checkable in an afternoon, and a claim you can check is worth more than one you cannot. Four things a reviewer can do:

  1. List every host the engine can reach. One command, and it is the whole answer:
    grep -rhoE "https://[a-z0-9.-]+" src/ | sort -u

    What comes back is Apple (api.appstoreconnect.apple.com), Google (androidpublisher.googleapis.com, www.googleapis.com for the OAuth token exchange), Google Fonts — which the local dashboard’s page loads in your browser, not during a build — and three placeholder URLs that only appear in help text and usage examples (cli.github.com, updates.example.com, your-site.vercel.app). There is no host belonging to us in that list, because there is nothing to send anywhere.

  2. Note what is missing: GitHub. Cloud builds dispatch through your own gh CLI, under your own login. leas never holds a GitHub token and never makes an authenticated request to GitHub on your behalf — it shells out to a tool you already installed and already trust.
    grep -rn "'gh'," src/cloud.js
  3. Watch the traffic anyway. Run a build behind a proxy. There is no telemetry, no licence check, and no phone-home to find.
  4. Inspect what the optional dashboard sends. Point LEAS_SITE_URL at a server you control and read the request body yourself. The field list is below.

What the hosted dashboard would receive

The dashboard is optional and not built yet. When it exists, what leas sync sends about credentials is metadata by construction — a name, a date, and a truncated public fingerprint:

{
  "id": "ios-distribution-certificate",
  "platform": "ios",
  "kind": "Distribution certificate",
  "identity": "Apple Distribution: Your Company",
  "expires": "2027-09-25T00:00:00.000Z",
  "daysLeft": 399,
  "status": "ok",
  "fingerprint": "7CEE3DB540F010D9"
}

No key material, no passwords, no PEM or DER blocks, and only the first sixteen characters of a fingerprint that is public information anyway — it is derived from the certificate embedded in every build you have ever shipped. That is what makes an expiry watch something a web page may hold at all.

The limits we cannot remove

A page arguing about custody has to be straight about where custody still ends.

  • Cloud builds put your secrets in your CI provider. If you run leas build --cloud on GitHub Actions, GitHub holds encrypted secrets and runs the job. That is a third party — a different one, chosen by you, under your own contract, and probably one you already trust with your source code. But it is not nobody. If it must be nobody, build locally.
  • iOS requires Apple hardware. Apple’s macOS licence forbids running macOS elsewhere, so an iOS build happens on a Mac — yours, or a rented one.
  • Bearer credentials stay bearer credentials. Keeping them on your own disk does not make them safer than your disk. Nothing here is a substitute for encrypted storage, least-privilege API keys, and rotation.
  • Local builds mean local responsibility. No vendor is patching your toolchain or backing up your keystore. Lose the upload keystore and you are asking Google for a reset.

For a security questionnaire

Short answers, if you are the one filling in the form:

QuestionAnswer
Does the vendor store our signing keys?No. There is no endpoint that accepts them.
Does the vendor process our source code?No. Builds run on your machines or your rented runners.
What data leaves our environment?Nothing, unless you enable the optional dashboard — then credential metadata and build history, as above.
Where is the signing performed?On hardware you control or rent under your own account.
Can we audit the implementation?Yes. The engine is MIT licensed.
What happens if the vendor disappears?Nothing. The licence is irrevocable for published code and it runs without us.
Sub-processors?For the engine, none. Apple, Google, and your CI provider are your relationships, not ours.

Working through this for a regulated team and finding a question this page does not answer? Tell us what is missing and we will answer it here rather than only to you.