npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@radhya/mach

v2.0.37

Published

Mach CLI: Cloud Build Orchestrator for React Native & Expo

Readme

Mach CLI

Cloud Build & Delivery for React Native, Expo, and Flutter

Mach is a CLI that lets you build, sign, and submit React Native and Expo apps from the cloud. Flutter support is modeled in configuration and documentation so it can be added without changing the command surface.

Installation

npm install -g @radhya/mach

Quick Start

# Login to Mach Dashboard
mach login

# Link your project
mach link

# Run a cloud build
mach build --platform ios
mach build --platform android

Commands

| Command | Description | | -------------------------- | ------------------------------------------------------------------ | | mach login | Authenticate with Mach Dashboard | | mach logout | Remove saved local Mach credentials | | mach init | Create a new Mach project | | mach link | Link local directory to an existing project | | mach me | Show current logged-in user | | mach build | Start a cloud build (iOS / Android) | | mach maestro | Queue managed Maestro tests for a build or artifact | | mach ota | Setup, publish, list, rollback, and promote OTA updates | | mach submit | Submit to App Store or Google Play | | mach credentials | Manage iOS certificates, provisioning profiles & Android keystores | | mach credentials:service | Upload App Store Connect or Google Play service credentials | | mach env | Manage, list, and download environment variables and secrets | | mach config | Show resolved configuration for a build profile | | mach start | Start the Metro development server | | mach device | Register test devices for ad-hoc distribution | | mach audit | Run automated security checks on your project | | mach agent | Manage the local device discovery agent for Install Hub | | mach host | Set up S3 static hosting and CloudFront | | mach sitemap | Generate sitemap.xml from Expo Router app routes | | mach deeplink | Configure and verify universal links and Android App Links |

Managed Maestro Testing

Mach's E2E testing layer is being implemented to run Maestro without requiring Maestro Cloud:

mach build android --profile e2e-test --maestro
mach maestro --latest --platform android --profile e2e-test
mach maestro --build-id <buildId>
mach maestro --local --build-id <buildId>
mach maestro --build-id <buildId> --maestro-use-build-source
mach maestro --build-id <buildId> --wait
mach maestro --latest --platform ios --maestro-env MAESTRO_TEST_EMAIL --maestro-env MAESTRO_TEST_PASSWORD
mach maestro --platform ios --local --maestro-flows .maestro-authenticated --maestro-env MAESTRO_TEST_EMAIL --maestro-env MAESTRO_TEST_PASSWORD

Mach manages the test execution infrastructure for you, so projects do not need a separate Maestro Cloud setup. Managed Android Maestro runs need an APK artifact and a .maestro flow file or directory. By default, mach maestro --maestro-flows .maestro uploads the local flow folder for that run when the path exists in the current project. If the local path is not present, Mach falls back to the build source snapshot. Managed Android runners use emulators, so the tested APK must include x86_64. Mach automatically includes emulator ABI support when you build with --maestro, when the profile name looks like maestro/e2e/test, or when the build profile sets "android": { "emulator": true }. Managed Maestro is billed from the Pay as you go wallet after the runner completes. Default pricing is $0.50 for the first 15 minutes, then $0.10 per additional 10-minute block; admins can change these rates from Dashboard Admin > Pricing. Use mach maestro --local to run the same flows on your connected local emulator/device. With --build-id, Mach downloads and installs the build artifact first; without a build or artifact flag, it runs against the app already installed locally. Use --maestro-env <name> for local or managed runs that need test credentials or other dynamic values. The flag is repeatable and also accepts comma-separated names. Local runs forward only names that exist in the shell or loaded dotenv environment, and the startup log lists every forwarded name. Managed runs store requested values encrypted, pass them to the iOS or Android Maestro command, redact values from runner logs, and fall back to the Mach server environment when a value is not present in the CLI environment.

Useful options:

| Flag | Description | | --------------------------- | ------------------------------------------------------------- | | --maestro | Queue tests automatically after a successful mach build | | --maestro-flows <path> | Flow file or directory, defaults to .maestro | | --maestro-device <name> | Managed runner device profile, defaults to Android Pixel 6 | | --maestro-retries <n> | Retry count for failed runs, from 0 to 10 | | --maestro-record | Enable recording when supported by the runner | | --maestro-use-build-source | Use flows from the build source snapshot instead of local upload | | --maestro-env <name> | Forward one env var to Maestro; repeat or comma-separate names | | --local | Run maestro test locally instead of queueing a cloud runner | | --wait | Wait for managed tests and exit non-zero if the run fails | | --wait-timeout <minutes> | Maximum time to wait for a managed run, defaults to 60 | | --wait-interval <seconds> | Polling interval for --wait, defaults to 15 |

Cloud Builds

Trigger a build in the cloud — Mach handles zipping, uploading, compiling, and returning your IPA or APK.

# iOS build (production)
mach build --platform ios --profile production

# Android build
mach build --platform android --profile production

# Build locally
mach build --platform ios --local

Installable artifacts are downloaded through Mach public file routes, not raw S3 URLs. Public build pages and QR install links use the configured files domain (for example https://files.getmach.dev) so users do not see bucket names or presigned S3 URLs.

Framework Selection

Mach does not auto-detect your app framework during builds. The build framework is declared in mach.config.json and is treated as the source of truth:

{
  "framework": "react-native"
}

Supported values:

| Value | Status | Build behavior | | ---------------- | ------------ | ------------------------------------------------------------------------------ | | react-native | Supported | Uses committed ios/ and android/ native projects. No Expo prebuild runs. | | expo | Supported | Runs Expo prebuild before native compilation and applies Expo-specific guards. | | flutter | Planned | Reserved in config/docs. Builds fail clearly until Flutter builders are added. |

mach init and mach link create framework: "react-native" by default. Change it to "expo" for Expo projects before running builds. This explicit config prevents a React Native change from affecting Expo projects, and avoids fragile package scanning in CI.

Use the framework guide that matches your app. The command surface stays the same; only framework changes the build pipeline:

OTA Updates

Mach OTA lets you publish JavaScript and asset updates through Mach without using EAS Update. Mach hosts the update artifacts and serves the right update by channel, platform, and runtime version. Your app never needs storage credentials.

mach ota setup
mach ota verify
mach ota publish --channel production --platform all --message "Fix checkout screen"
mach ota list
mach ota rollback --update-id <updateId>
mach ota promote --update-id <updateId>

For Expo apps, mach ota setup writes ota config and wires @radhya/mach/expo-plugin. The plugin configures Expo Updates from mach.config.json, so updates are served from Mach instead of EAS Update. Install expo-updates before creating native builds that should receive OTA updates.

Command flags always win over config values. For example, mach ota publish --channel staging --runtime-version 1.2.0 overrides ota.channel and ota.runtimeVersion.

Runtime version is the OTA compatibility boundary. Mach resolves Expo-style appVersion, sdkVersion, and nativeVersion policies, stores the resolved runtime on each build, and serves OTA updates only to matching platform/channel/runtime combinations.

Vanilla React Native uses the same command surface. The native OTA client and automatic native patching are intentionally guarded until the Mach RN client package is ready.

Deep Links

Mach can generate the local configuration and hosted association files needed for iOS Universal Links and Android App Links:

mach deeplink setup
mach deeplink verify

When run from the project root, Mach reads framework, scheme, ios.bundleIdentifier, android.package, and production submit metadata from mach.config.json. In most projects the only value you need to provide is the public web URL:

mach deeplink setup --base-url https://app.example.com

Command flags always override values read from mach.config.json; config values are used as defaults.

For CI or explicit overrides:

mach deeplink setup \
  --yes \
  --profile production \
  --framework expo \
  --platform all \
  --base-url https://app.example.com \
  --scheme example \
  --ios-bundle-id com.example.app \
  --apple-team-id ABCDE12345 \
  --app-store-id 1234567890 \
  --android-package com.example.app \
  --android-fingerprint AA:BB:CC:DD \
  --install-plugin \
  --output public

setup writes compact deepLinks settings into mach.config.json, generates Apple AASA and Android assetlinks.json files, and wires an Expo config plugin for Expo projects. For Expo apps, Mach prefers @radhya/mach/expo-plugin; interactive setup offers to install @radhya/mach locally, and --yes or --install-plugin installs it automatically. Use --no-install-plugin to skip package installation; if the package is missing, Mach generates a local ./plugins/withMachDeepLinks fallback. See Deep Links.

Local Device Agent

The Install Hub can install builds on connected local devices when the Mach agent is running on your machine.

# Run in the foreground
mach agent

# Install as a background login service/task
mach agent install

# Inspect or control the background agent
mach agent status
mach agent restart
mach agent stop
mach agent uninstall

mach agent is local-only and does not require Dashboard authentication. It exposes device discovery and install endpoints on http://127.0.0.1:7070.

Build Options

| Flag | Description | | ---------------- | -------------------------------------------------------------------------------- | | --platform | ios or android (required) | | --profile | Build profile from mach.config.json (default: production) | | --local | Run build on local machine | | --dry-run | Generate build script without running it | | --simulator | iOS Simulator build (no signing needed) | | --auto-version | Force Mach-managed Android versionCode or iOS buildNumber assignment | | --auto-submit | Submit the successful build to App Store Connect or Google Play | | --verbose | Enable detailed build logging | | --json-output | Write build metadata (buildId, Dashboard URL, status) to a JSON file for CI/CD |

CI Build URLs

Use --json-output when a pipeline needs the Dashboard URL for Slack, deployment notes, or downstream steps:

mach build --platform android --profile production --json-output mach-android-build.json
ANDROID_URL=$(node -pe "require('./mach-android-build.json').url")

The JSON file includes:

{
  "status": "success",
  "buildId": "00000000-0000-0000-0000-000000000000",
  "platform": "android",
  "profile": "production",
  "url": "https://getmach.dev/build/00000000-0000-0000-0000-000000000000"
}

Set MACH_DASHBOARD_URL only for local or staging dashboards. Production build links default to https://getmach.dev.

Bitbucket Pipelines

Mach works in Bitbucket through a Dashboard access token. Create a token in Dashboard → Settings → Access Tokens, save it as MACH_TOKEN, and use --json-output when a pipeline needs the Mach build URL for later steps.

image: node:24.13.1

definitions:
  steps:
    - step: &build-android
        name: Build Android
        size: 2x
        caches:
          - node
        script:
          - npm ci
          - npm install -g @radhya/mach@latest
          - mach build --platform android --profile production --auto-version --json-output mach-android-build.json

    - step: &submit-android
        name: Submit Android
        size: 2x
        caches:
          - node
        script:
          - npm ci
          - npm install -g @radhya/mach@latest
          - mach submit --latest --platform android --profile production

Android submission uses the Google Play Developer API directly, so Bitbucket/Linux runners do not need Ruby or Fastlane for .aab uploads. Mach retries transient Play API failures before failing and records Android submissions in the Dashboard. iOS submission is queued through Mach, so the public command is the same from macOS, Linux, Windows, or CI. Users see the Mach submission URL and status.

Before enabling CI, make sure mach.config.json is committed, MACH_TOKEN is configured as a secure CI variable, credentials are uploaded in the Dashboard, and Android service credentials are attached to the exact package id.

For iOS submit, the user-facing command is universal:

mach submit --latest --platform ios --profile production

Mach handles the managed macOS submission environment internally. No provider-specific macOS job needs to be added to a user's CI configuration.

Credential Management

Manage iOS certificates, provisioning profiles, Android keystores, and App Store Connect API keys — all stored securely in the Dashboard.

# Interactive credential manager
mach credentials

Mach walks you through each step: choosing a bundle ID, picking a distribution type (development / ad-hoc / App Store), and syncing certificates. Everything is encrypted at rest and injected at build time.

For iOS development and ad-hoc profiles, Mach also asks which registered devices should be included. After registering the selected devices with Apple, Mach refreshes the provisioning profile so the uploaded profile includes those devices instead of reusing an older cached profile.

Apple Developer Team Selection

For iOS credentials, Mach asks for your Apple ID and then lists the Apple Developer teams connected to that account through @expo/apple-utils. If the account has multiple teams, choose the team to use for signing; if Apple cannot return the list, Mach falls back to manual Team ID entry. Fastlane is still used later for the certificate/profile sync, but it is no longer required just to display the team picker.

Mach saves the selected Team ID to the Dashboard for credential storage. It also asks whether to write the Team ID into mach.config.json for the selected profile. Accepting the prompt writes only submit.<profile>.ios.appleTeamId; production defaults to yes, while development and staging default to no so local config is not cluttered unless you want profile-specific signing.

Multiple Bundle IDs

One project can have multiple bundle identifiers (e.g. com.example.app, com.example.app.staging, com.example.app.dev). Each bundle ID has its own independent set of credentials per distribution type. Select or add a bundle ID at the start of mach credentials — or skip the prompt with the --bundle-id flag for CI:

mach credentials --bundle-id com.example.app.staging

The Dashboard shows each bundle ID as a row. Clicking it reveals its Distribution certificate, Provisioning profile, and Service credentials — matching the same layout as Expo's credentials screen. A Password stored badge confirms the P12 export password is saved and will be injected at build time automatically.

Service Credentials

Service credentials are used for store automation:

  • iOS: App Store Connect API key (.p8)
  • Android: Google Play service account JSON

Android service credentials are scoped to a package name. This matters when one Mach project contains multiple Android application identifiers.

# Android: upload a Google Play service account JSON
mach credentials:service --platform android --package-name com.example.app --file ~/Downloads/play-service-account.json

# If --package-name is omitted, Mach fetches saved Android package ids
# from the Dashboard and asks you to select one.
mach credentials:service --platform android --file ~/Downloads/play-service-account.json

# iOS: upload or create an App Store Connect API key
mach credentials:service --platform ios

The Dashboard also supports this under Credentials → Android → package id → Service credentials. Uploading a Google Play service key under * is legacy behavior; store submission now expects the service credential to match the Android package being submitted.

App Store Connect keys are used for iOS TestFlight submission. Mach stores the key id, issuer id, Apple Developer Team ID, and App Store Connect provider/team metadata so submit commands can run without Apple ID prompts.

Google Play service keys are only needed for Android store submission. They are not signing credentials. A dev package such as com.example.app.dev needs its own Google Play service key only if that exact package exists in Google Play and you submit that package.

Configuration

Create a mach.config.json in your project root:

{
  "projectId": "your-project-id",
  "framework": "react-native",
  "name": "MyApp",
  "slug": "my-app",
  "nodeVersion": "24",
  "ios": {
    "bundleIdentifier": "com.example.myapp",
    "appleTeamId": "ABCDE12345",
    "ascAppId": "1234567890",
    "buildNumber": "auto"
  },
  "android": {
    "package": "com.example.myapp",
    "versionCode": "auto"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "development",
      "environment": "development"
    },
    "staging": {
      "distribution": "internal"
    },
    "production": {
      "distribution": "store"
    }
  },
  "submit": {
    "production": {
      "android": { "track": "internal", "releaseStatus": "completed" },
      "ios": {
        "ascAppId": "",
        "appleTeamId": ""
      }
    }
  }
}

Root Properties

| Property | Description | | ------------- | --------------------------------------------------------------------------------------------------------------- | | projectId | Auto-generated UUID linking to your Dashboard project | | framework | Required build framework: react-native, expo, or flutter. Builds never infer this from package.json | | name | App display name | | nodeVersion | Node.js version to use on the build machine (e.g. "24", "20.11.0"). Switches via nvm before installing deps |

Platform Properties

| Property | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | ios.bundleIdentifier | iOS bundle ID used for signing, version counter scoping, and submit | | ios.appleTeamId | Optional global Apple Developer Team ID used for iOS signing. Profile-specific submit.<profile>.ios.appleTeamId overrides it | | ios.ascAppId | App Store Connect app id used for iOS submission | | ios.buildNumber | Set to "auto" to let Mach assign the next iOS CFBundleVersion from its Dashboard counter | | android.package | Android package name used for signing, version counter scoping, and submit | | android.versionCode | Set to "auto" to let Mach assign the next Android versionCode from its Dashboard counter |

Profile Properties

| Property | Description | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | extends | Inherit settings from another profile | | environment | Dashboard Secret Environment to use (development, staging, production) | | developmentClient | Expo only: true to build with expo-dev-client | | distribution | development for iOS development signing, internal for ad-hoc/internal testing builds, or store for App Store / Play Store release builds | | android.emulator | true to include Android emulator ABI support (x86_64) for Maestro/e2e APK builds | | android.architectures | Override Android release ABIs, for example armeabi-v7a,arm64-v8a,x86_64 | | preBuild / postBuild | Shell command or script reference to run around the build | | envGroups | Array of environment group names to merge into this profile |

Environment Groups

Reusable sets of environment variables shared across profiles:

{
  "envGroups": {
    "common": {
      "API_URL": "https://api.example.com"
    }
  },
  "build": {
    "production": {
      "envGroups": ["common"]
    }
  }
}

Secrets & Environment Variables

Secrets are encrypted at rest and injected at build time, scoped per environment.

# Add a secret
mach env set API_URL=https://api.example.com SECRET_KEY=xxx

# List all secrets
mach env list

# Download development secrets to .env.local
mach env pull

# Download a specific environment
mach env pull --environment staging
mach env pull --environment production --out .env.production.local

# Replace an existing local env file
mach env pull --environment development --out .env.local --overwrite

mach env pull downloads decrypted secrets for local development. It defaults to --environment development and --out .env.local. The exported file includes global secrets plus the selected environment's overrides. Mach refuses to overwrite an existing file unless --overwrite is provided. mach env download and mach env export are aliases for the same behavior.

Mach includes the resolved environment in the build fingerprint, so development, staging, and production builds do not reuse a native build cache when their injected variables differ. If a stored secret cannot be decrypted, Mach surfaces that as a configuration problem instead of silently injecting [Decryption Failed] into a production binary.

Android Keystore Passwords

The keystore file and its passwords are stored encrypted in the Dashboard when managed through mach credentials. Legacy projects can still provide Android signing passwords as environment-scoped secrets in the Dashboard, but managed credentials are recommended so passwords stay attached to the keystore record.

Automated Versioning

Mach can automatically assign the next Android versionCode and iOS buildNumber from Dashboard counters, then increment atomically so concurrent builds do not receive the same native version.

Counters are scoped by Android package name or iOS bundle identifier, so development, staging, and production apps can advance independently.

For existing apps already uploaded to App Store Connect or Google Play, seed the selected Dashboard environment once with the latest uploaded value (BUILD_NUMBER for iOS or ANDROID_VERSION_CODE for Android). Mach will assign the next value and continue from its own counter after that.

Usage:

mach build --platform android --auto-version
mach build --platform ios --profile production --auto-version

Or in config:

"android": { "versionCode": "auto" },
"ios": { "buildNumber": "auto" }

When android.versionCode is set to "auto", Mach assigns an Android versionCode automatically for store builds. For internal builds, pass --auto-version when you also want Mach to assign a versionCode.

When ios.buildNumber is set to "auto", Mach assigns the next iOS CFBundleVersion from the scoped Dashboard counter. Concurrent builds are safe because the Dashboard API increments the counter atomically.

This only changes iOS CFBundleVersion / build number. It does not bump the public App Store version (CFBundleShortVersionString, usually expo.version in app.json). After a version like 1.17.0 is approved/released, Apple closes that train for new uploads; bump the app version, for example to 1.17.1, rebuild, then submit.

Regular iOS development builds should set distribution: "development" and resolve Apple Development certificates/profiles. For custom profile names, Mach follows the profile fields, not the profile name: environment chooses Dashboard secrets, and distribution / iosExportMethod chooses signing. Dev-client builds can keep distribution: "internal" and set developmentClient: true; Mach will still use development signing credentials while producing an internal installable artifact.

For Android dev-client builds, Mach defaults to Gradle assembleDebug so the output opens the Metro/debug server instead of behaving like a standalone release APK. For iOS dev-client builds, Mach uses the Debug Xcode configuration.

Store Submission

# Submit the latest successful build for a profile
mach submit --latest --platform android --profile production
mach submit --latest --platform ios --profile production

# Build and submit the exact successful build in one command
mach build --platform ios --profile production --auto-submit
mach build --platform android --profile production --auto-submit --submit-track internal

# Android track and release status can be supplied on the command or in mach.config.json
mach submit --latest --platform android --profile production --track internal --release-status completed

# Staged rollout example
mach submit --latest --platform android --profile production --track production --release-status inProgress --rollout 0.1

# If Play asks for explicit review handling
mach submit --latest --platform android --profile production --track internal --changes-not-sent-for-review

# Submit a specific build id
mach submit --platform android --build-id <build-id>

For Android, Mach resolves the package from mach.config.json / Expo config and looks for a matching google_service_account credential. If none exists, upload it with:

mach credentials:service --platform android --package-name com.example.app --file ~/Downloads/play-service-account.json

Android submit reads submit.<profile>.android.track, submit.<profile>.android.releaseStatus, and optional submit.<profile>.android.rollout, falling back to submit.production.android.*, then internal / completed. Valid Google Play release status values are completed, draft, halted, and inProgress. Use rollout only with inProgress or halted; it must be greater than 0 and less than 1.

Android submit does not require Fastlane. Mach uploads the .aab, assigns the returned versionCode to the requested track, commits the edit through the Google Play Developer API, and records the submission in the Dashboard. Transient Google Play failures are retried up to four times before the command fails.

iOS submit queues a managed submission through Mach. Users and CI call the normal mach submit --platform ios ... command, and Mach handles the required macOS upload environment. The Dashboard submission row is connected to the build, records who created it, and is marked failed if the upload fails.

If --profile is provided with --latest, Mach chooses the latest successful build from that profile only. This avoids accidentally submitting a staging or preprod build to production.

Development Server

mach start --build-profile development
mach start --ios
mach start --android
mach start --web
mach start --tunnel
mach start --clear

When running an Expo app, mach start keeps the normal Expo keyboard flow available: press i for iOS, a for Android, w for web, and r to reload. Use the flags above when CI/scripts or muscle memory should launch a target immediately.

Security Audit

mach audit
mach audit --severity high
mach audit --fix
mach audit --json   # CI/CD-friendly JSON output

Authentication

Interactive

mach login
mach logout

Credentials are stored in ~/.mach/credentials.json. mach logout removes this local credentials file. If MACH_TOKEN is set in the shell, unset it separately.

CI/CD (Token-based)

export MACH_TOKEN=your-personal-access-token
mach build --platform ios --profile production

Generate tokens from the Mach Dashboard under Settings → Access Tokens. Access tokens can be named like CI robots (for example BitbucketPipeline). Builds and submissions store the token identity so the Dashboard can show who or what created the run without storing a user's email/name on the build row.

Example CI flow:

export MACH_TOKEN=your-personal-access-token
mach build --platform android --profile production
mach submit --latest --platform android --profile production

Environment Variables Reference

| Variable | Description | | ------------------- | --------------------------------------------------------------- | | MACH_TOKEN | Personal access token for CI/CD (skips login) | | MACH_DEBUG | Set to 1 to print resolved build profile and platform details | | MACH_P12_PASSWORD | iOS certificate password (avoids interactive prompt) |

Requirements

  • Node.js 18+
  • For iOS builds: Xcode (local builds) or cloud build subscription
  • For Android builds: Android SDK (local builds) or cloud build subscription

License

ISC


Built by Radhya Softlabs