How we let AI agents ship our apps without ever seeing a secret
AI coding agents are capable enough now to build, test and publish software on their own. That's also exactly what makes them dangerous to point at real release infrastructure. Here's the architecture we use to let agents ship our apps without ever handing them a credential.
The problem with giving an agent your secrets
Shipping any of our apps — NailMe, Craft, Hair, or Tattoo — to production depends on a pile of credentials that are catastrophic if leaked: Google Play service-account JSON, App Store Connect API keys, Android signing keystores, Git tokens, Firebase service credentials, RevenueCat keys. The naive way to let an AI agent operate a release pipeline is to hand it those values directly — as environment variables, or files it can read. That means every prompt, log line, and model response is a place a secret could leak.
The principle we build around
The AI can request a capability, but it never receives the underlying secret.
Instead of an agent asking to read GOOGLE_SERVICE_ACCOUNT_JSON or an SSH private key, it requests a narrowly scoped operation — for example google_play.publish. A local, trusted executor resolves that request against a credential store the AI process can never read, performs the operation, and returns only the result.
Credential handles, not raw values
In practice, this is a local provisioning agent that stores every publishing credential as a project-scoped handle in the macOS Keychain — never as a plain file or environment variable. A handle looks like nails.google-play.production or nails.appstore.production. Provisioning is guided: you drag a service-account file or paste a key into a silent prompt, and it goes straight into the Keychain — never into an AI chat.
An execution agent that wants to publish a build never asks for the Google Play service-account JSON itself. It asks for the handle. The real secret is only ever materialized inside a short-lived, auto-shredded subprocess: a temporary, permission-locked file that the child process (Fastlane, Gradle) reads directly, and that's wiped on exit. The value never reaches stdout, a log file, or the model's context.
Real validation without exposure
A credential store is only useful if you can trust the credentials in it actually work — so the provisioning agent validates each handle with genuine signed authentication: an RS256 JWT exchanged for a Google OAuth token for Play Store access, and an ES256 JWT for the App Store Connect API. A broken or fake credential fails validation cleanly, proving the whole signing and transport chain works without ever printing the key itself.
Every access, audited
Every capability request is logged — what was requested, for which brand and environment, and when — without ever recording the secret. That gives us a traceable history of what an AI agent actually did with the release pipeline, independent of whether we trust the agent's own account of its actions.
Where this fits in our stack
This credential layer sits underneath the same Kotlin Multiplatform release pipeline that ships NailMe, Craft and Hair — GitLab CI, Firebase App Distribution, Fastlane, TestFlight and the Play Store. It's why we can let AI agents help with parts of that pipeline without changing our answer to "who can see our signing keys?" — nobody but the OS Keychain, ever.
Curious about the release pipeline this protects?
See how the same Kotlin Multiplatform codebase ships four separate brand variants in why we run four marketplace brands on one Kotlin Multiplatform codebase.
Talk to us