Skip to content
Moku
@moku-labs/native pages

Signing and store builds

Config holds identifiers and env var names. The environment holds the secrets.

On this page

How it fits together

Tauri reads the credentials itself. So nothing secret reaches tauri.conf.json, Gradle or a log line. Unsigned dev builds and simulator builds need none of this, and doctor only warns about it.

Example

src/native.ts
import { createApp } from "@moku-labs/native";

export const native = createApp({
  config: {
    app: {
      name: "MyApp",
      identifier: "com.example.myapp",
      buildNumber: "42",
      category: "Productivity"
    },
    targets: ["macos", "ios", "android"],
    signing: {
      apple: {
        teamId: "ABCDE12345",
        signingIdentity: "Developer ID Application: ACME (ABCDE12345)",
        providerShortName: "ACME",
        appStore: true,
        exportMethod: "app-store-connect"
      },
      android: {
        keystorePath: "release.jks",
        keystorePasswordEnv: "ANDROID_KEYSTORE_PASSWORD",
        keyPasswordEnv: "ANDROID_KEY_PASSWORD",
        keyAlias: "release"
      }
    }
  }
});
  1. On Android this becomes versionCode, so it must be a whole number.
  2. - means ad-hoc.
  3. The name of a variable, never its value.

What goes in the environment

VariablesFor
APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_IDNotarization with an app-specific password.
APPLE_API_KEY, APPLE_API_ISSUER, APPLE_API_KEY_PATHNotarization with an App Store Connect API key. Either set is enough.
APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORDA base64 certificate imported into a CI keychain.
The names in keystorePasswordEnv, keyPasswordEnvAndroid release signing. keyPasswordEnv falls back to keystorePasswordEnv.

What each field does

FieldEffect
apple.teamIdbundle.iOS.developmentTeam. doctor warns on an iOS target without it.
apple.signingIdentitybundle.macOS.signingIdentity. doctor counts keychain identities, and never reads their names.
apple.providerShortNameNotarization on an account with several teams.
apple.entitlementsYour plist, relative to the working directory.
apple.appStoreWith no entitlements, generates a sandbox plist for macOS.
apple.exportMethodtauri ios build --export-method: app-store-connect, release-testing or debugging. A simulator build ignores it.
apple.macosMinimumSystemVersionThe macOS floor.
apple.iosMinimumSystemVersionThe iOS floor, default "15.0". A kept tree follows it.
android.keystorePathKeeps a // MOKU-SIGNING-START block in build.gradle.kts. Dropping it removes the block.
windows.certificateThumbprintThe Windows signing certificate.

Build for a store

scripts/native-store.ts
import { native } from "../src/native";

await native.start();
await native.cli.build({ target: "android", aab: true });
await native.cli.build({ target: "ios" });
await native.stop();
  1. Play wants the .aab. The default is an installable .apk.

An iOS store app usually needs ios.privacyManifest too. Give it an absolute path. ios.infoPlist adds any other keys, such as an encryption declaration.

  • A store build refuses a page with the editor agent. Use the dev profile for a TestFlight build that carries it.
  • Android refuses a dotted buildNumber. It is checked at generate. It stays legal on other targets.
  • doctor is warn-only here. A green report does not prove the credentials work.