Project Configuration
One config file to rule them all.
The `mach.config.json` file is your source of truth. It uses a powerful inheritance system. Profiles can use any unique names, and there is no fixed profile-count limit; their fields define behavior.
Profile Inheritance
Profiles can extend other profiles with the `extends` field. Child profiles inherit all parent values and can override specific fields. Mach detects circular inheritance automatically.
Config with inheritance
{
"name": "My App",
"scheme": "myapp",
"nodeVersion": "24",
"ios": {
"bundleIdentifier": "com.myapp",
"appleTeamId": "ABCDE12345",
"ascAppId": "1234567890",
"buildNumber": "auto"
},
"android": {
"package": "com.myapp",
"versionCode": "auto"
},
"build": {
"base": {
"envGroups": ["common"],
"distribution": "internal"
},
"development": {
"extends": "base",
"developmentClient": true
},
"staging": {
"extends": "base",
"environment": "staging"
},
"production": {
"extends": "base",
"environment": "production",
"distribution": "store"
}
},
"submit": {
"production": {
"android": {
"track": "internal",
"releaseStatus": "completed"
}
}
}
}
Project Fields
projectId
Auto-generated UUID linking to your Dashboard project.
name
App display name. Used to generate Xcode schemes and build identifiers.
slug
URL-friendly project identifier for the Dashboard.
scheme
Deep link URI scheme. Auto-lowercase.
framework
Required build framework: flutter, react-native, or expo. Mach never changes behavior based on profile names.
appDirectory
Application path relative to the config root. Supports JavaScript monorepos and Dart pub workspaces.
nodeVersion
React Native/Expo Node.js version — e.g. "24" or "20.11.0". Flutter builds do not use this field.
flutter.sdkVersion
Exact Flutter SDK used by managed runners. mach init/link pins the active installed version; local builds stop on a mismatch until the configured SDK is activated or intentionally updated.
Platform Fields
ios.bundleIdentifier
iOS bundle ID (e.g., com.myapp). Used for signing, native version counter scoping, and submit.
ios.appleTeamId
Apple Developer Team ID for code signing.
ios.ascAppId
App Store Connect app id. Used for TestFlight submission.
ios.buildNumber
Set to 'auto' to let Mach assign the next iOS CFBundleVersion from its Dashboard counter.
ios.iosScheme
Explicit shared Xcode scheme when the native project has more than one buildable scheme. Flutter normally derives it from flutter.flavor.
android.package
Android package name (e.g., com.myapp). Used for signing, native version counter scoping, and submit.
android.versionCode
Set to 'auto' to let Mach assign the next Android versionCode from its Dashboard counter.
android.apk
Root Android artifact override. Flutter internal builds default to APK and store builds default to AAB unless a profile overrides apk explicitly.
Deep Link Fields
deepLinks.baseUrl
Public HTTPS app URL used to derive Universal Link and App Link domains, for example https://app.example.com.
deepLinks.domain
Optional override when the associated domain differs from the base URL hostname.
deepLinks.profile
Profile used to resolve bundle IDs, team IDs, package names, and submit metadata. Defaults to production.
deepLinks.paths
Path scopes for Apple AASA. Defaults to ["/*"], which matches all app URLs on the domain.
deepLinks.android.sha256CertFingerprints
One or more Android signing SHA256 fingerprints for verified App Links. Setup automatically reads the public fingerprint from a linked project's Mach-managed keystore when available, then optionally lets the developer paste Google Play App Signing values. Keep debug, direct-release, and Google Play fingerprints together.
deepLinks.web.outputDir
Directory where Mach writes AASA and assetlinks files. Uses public when present, otherwise deep-links.
OTA Fields
ota.enabled
Turns Mach OTA on for this project. Written by `mach ota setup`.
ota.engine
Use expo-updates for Expo/bare React Native or shorebird for Flutter. mach-react-native is reserved for a future Mach-owned native client.
ota.channel
Default update channel. Command flags such as `--channel staging` always override this value.
ota.url
Project-scoped Mach Expo Updates protocol URL. Artifacts and release metadata remain hosted by Mach.
ota.runtimeVersion
Manual runtime version override. If omitted, Mach uses the runtime version policy.
ota.runtimeVersionPolicy
Default policy is appVersion. Runtime precedence is command override, ota.runtimeVersion, this policy, Expo config, then package fallback. Mach validates the resolved runtime before builds and publishes.
Simulator Keychain and encrypted sessions
For Expo, vanilla React Native or Flutter simulator apps using Keychain-backed tokens or an encrypted storage key, enable optional simulator signing in mach.config.json. This is a build-profile setting, not a Dashboard credential or Apple device registration.
mach.config.json — simulator profile
{
"build": {
"simulator-tests": {
"ios": {
"simulator": true,
"simulatorSigning": "adhoc"
}
}
}
}
Inspect configuration, then rebuild and queue Maestro
mach config --profile simulator-tests --json
mach build --platform ios --profile simulator-tests --maestro
Omit simulatorSigning or select disabled to keep existing unsigned simulator builds. The setting is inherited through extends and applies to local and cloud builds. It requires a CLI containing the 2.9.0 feature on the build runner; publishing a CLI does not update a linked checkout automatically.
Simulator ad-hoc signing uses Xcode-generated entitlements and needs no Apple certificate, simulator UDID registration or device provisioning profile. It is separate from device Ad Hoc distribution. Device, TestFlight and App Store signing stay unchanged.
Mach preserves framework-generated entitlements and checks signatures, embedded simulator entitlements across architectures, and the packaged ZIP against Xcode metadata. Missing or inconsistent metadata fails the build with sanitized diagnostics. The build Mac needs Xcode and Python 3. Signing and ZIP checks add work; their cost has not been benchmarked.
Rebuild an unsigned artifact to apply the setting. Verify your app on an Apple Silicon simulator: log in, stop and relaunch without clearing data, then assert the authenticated screen remains visible without another login. Keep encryption and the Maestro assertions enabled. A native Keychain test does not replace testing your app's encrypted-session restoration.
If Keychain reports -34018, inspect the build signing validation and generated simulator entitlements. An empty codesign entitlement dump alone is inconclusive because simulator entitlements can be embedded in Mach-O. Changing access-group identities can make existing Keychain items inaccessible; do not replace groups just to make a test pass.
Build Profile Fields
extends
Inherit all properties from a parent profile.
environment
Maps to a Dashboard Secret set (development, staging, production).
distribution
'internal' → installable/testing artifact. 'store' → App Store / Play Store release artifact.
envGroups
Array of named secret groups to inject into this profile's builds.
developmentClient
Build an Expo Dev Client. Keep distribution as 'internal'; Android uses assembleDebug and iOS uses Debug configuration.
ios.simulator
Set true in a build profile to produce an iOS simulator app instead of a device artifact.
ios.simulatorSigning
Simulator-only: adhoc enables Xcode signing for Keychain-backed storage; omitted or disabled preserves existing unsigned behavior. Inherited through extends. Device builds reject an explicit setting.
android.emulator
Set true on an e2e/Maestro profile to include x86_64 ABI support for Android emulator runners.
android.architectures
Override Android release ABIs, for example armeabi-v7a,arm64-v8a,x86_64.
flutter.flavor
Existing Flutter Android product flavor and shared iOS scheme for this profile. Optional for unflavored apps; Mach does not create native flavors.
flutter.target
Dart entrypoint relative to appDirectory, defaulting to lib/main.dart. The path must remain inside appDirectory.
flutter.mode
Flutter compilation mode: debug, profile, or release.
flutter.dartDefinesFromEnv
Allowlisted environment-key names forwarded as --dart-define. Missing values stop the build; compiled values must not be treated as secrets.
flutter.obfuscate
Obfuscates Dart code for Android/iOS release builds and preserves downloadable split debug symbols.
flutter.splitDebugInfo
Preserves Flutter split debug symbols for Android/iOS release builds without requiring obfuscation.
android.flutter / ios.flutter
Platform-specific flavor, target, mode, or dartDefinesFromEnv overrides. Use these when Android product-flavor and iOS scheme names differ.
apk (profile)
Set true to force an Android APK or false to force an AAB for this profile; otherwise distribution selects the Flutter artifact type.
Resolve & Inspect
Use the `config` command to see the fully resolved configuration for any profile, with all inherited values merged.
mach config --profile production
mach config --profile production --json