Build and publish over-the-air (OTA) updates for React Native, Expo and HarmonyOS. Native build registration, staged rollouts, CI/CD and sourcemap archiving from your terminal.
简体中文 · Pakta · Quick start · CLI guide · SDK · Pricing
| Capability | What you can do |
|---|---|
| Cross-platform builds | Register APK / AAB / IPA / HarmonyOS APP files and create .ppk updates |
| Channel-based rollouts | Deploy one artifact to multiple channels and native versions at independent percentages |
| CI/CD automation | Token authentication, non-interactive publishing, zero-write previews and exact deployment retries |
| Hermes & diagnostics | Reuse verified Hermes bases, archive sourcemaps and symbolicate error stacks |
Requires Node.js ≥18.17, your platform build toolchain and native integration of the rn-update SDK. Run these commands from your React Native project root.
Register and verify your email in the Pakta dashboard, then:
npm install -g rn-update-cli
pakta login
pakta selectApp --platform androidNeed an app? Run pakta createApp --platform android --name MyApp first. Select each platform separately; configuration is saved in update.json.
pakta uploadApk ./app-release.apkFor other formats, use uploadAab, uploadIpa or uploadApp. The native build is the compatibility baseline for OTA updates.
pakta bundle --platform android --output .pakta/output/android.ppk --no-interactive
pakta publish .pakta/output/android.ppk --platform android --name fix-001 --channel default --packageVersion 1.0.0 --rollout 10 --sourcemap .pakta/intermedia/android/index.bundlejs.mapReplace default and 1.0.0 with your registered channel and native version. Add --expo when bundling Expo apps. Omitting --rollout defaults to 100%.
Publishing behavior and channel rules
An explicit --output avoids guessing timestamped filenames. bundle --name ... also publishes; omit --name to build only.
Interactive publishing without targets asks whether to bind a native build after upload; declining only stores the artifact. Non-interactive publishing requires --targets, --packageId or --packageVersion.
Channels come from native metadata, defaulting to default. Upload --channel asserts identity without rewriting it. Standalone uploads/publishes create missing channels. A channel/native-version pair cannot have conflicting JS fingerprints; buildTime stays an opaque string.
Create targets.json, with one independent deployment per entry:
[
{ "channel": "default", "packageVersion": "1.0.0", "rollout": 10 },
{ "channel": "huawei", "packageVersion": "1.0.0", "rollout": 30 }
]Set RNU_SERVICE_URL in CI and inject PAKTA_API_TOKEN from your secret store, using a personal token created in account settings. Validate the built artifact’s targets, then publish:
pakta publish .pakta/output/android.ppk --platform android --targets targets.json --dryRun --no-interactive
pakta publish .pakta/output/android.ppk --platform android --targets targets.json --name fix-001 --sourcemap .pakta/intermedia/android/index.bundlejs.map --no-interactive--dryRun does not upload, create channels or write packages/deployments. A target’s rollout overrides the global value, falling back to 100%. Save the returned packageId and deploymentIds for exact retries:
pakta publish --deploymentIds DEPLOYMENT_ID_1,DEPLOYMENT_ID_2 --platform android --no-interactive| Purpose | Commands |
|---|---|
| Account | login, logout, me |
| Apps | createApp, apps, selectApp, deleteApp |
| Channels | channels, createChannel, updateChannel, deleteChannel |
| Native builds | uploadApk, uploadAab, uploadIpa, uploadApp, packages, deletePackage |
| Inspect | parseApk, parseAab, parseIpa, parseApp, extractApk |
| OTA | bundle, publish, versions, update, updateVersionInfo, deleteVersion |
| Patches | hdiff, hdiffFromApk, hdiffFromIpa, hdiffFromApp, registerPdiff |
| Diagnostics | symbolicate, cache, cache clean |
Local hdiff* commands only generate patches; register exact native-build patches separately with registerPdiff. Full options: cli.json.
Hermes & sourcemaps
bundle generates sourcemaps by default and composes the final Hermes map. Archive it with publish --sourcemap, then symbolicate:
pakta symbolicate stack.txt --platform android --hash UPDATE_HASHThe hash comes from the SDK’s getUpdateMetadata().currentVersion. Alternatively use --versionId, or - to read the stack from stdin.
Hermes defaults to --hermesBase auto; use none to disable it or pass a .hbc/.ppk/.apk/.ipa path. --verifyHermesBase checks readable disassembly and then raw HBC operands against complete binary strings, constants, function references and control-flow targets. Unknown or unreadable layouts fail closed and fall back to plain compilation; patch size depends on your build. The probe, verification and compiler/sourcemap subprocesses have bounded deadlines (30/120/300 seconds), configurable with PAKTA_HERMES_PROBE_TIMEOUT_MS, PAKTA_HERMES_VERIFY_TIMEOUT_MS and PAKTA_HERMES_COMPILE_TIMEOUT_MS. See Hermes base verification.
For Sentry, configure platform sentry.properties, use @sentry/react-native/metro for matching bundle/map Debug IDs, and use the bundle publishing flow for automatic uploads. Legacy Sentry accepts explicit --sentry-release and --sentry-dist, matching runtime values.
Provider API
Install a development dependency when importing the API in build scripts:
npm install --save-dev rn-update-cliConfigure service and authentication first. Place this in an async function or a module supporting top-level await:
import { CLIProviderImpl } from 'rn-update-cli';
const provider = new CLIProviderImpl();
const bundle = await provider.bundle({
platform: 'android',
output: '.pakta/output/android.ppk',
sourcemap: true,
});
if (!bundle.success) throw new Error(bundle.error);
const release = await provider.publish({
filePath: '.pakta/output/android.ppk',
sourcemap: '.pakta/intermedia/android/index.bundlejs.map',
platform: 'android',
name: 'fix-001',
targets: 'targets.json',
});
if (!release.success) throw new Error(release.error);Methods return CommandResult; check success before consuming data. Full interfaces: src/types.ts.
Connect your own service
The default backend is https://pakta.yoghourt.space; the CLI appends /admin/api/v1. To use another deployment:
export RNU_SERVICE_URL=https://YOUR_HOSTPowerShell:
$env:RNU_SERVICE_URL = 'https://YOUR_HOST'Credentials are scoped by service URL.
Environment variables and CLI automatic updates
| Variable | Purpose |
|---|---|
RNU_SERVICE_URL |
Standalone service URL |
PAKTA_API_TOKEN |
Personal access token for CI |
NO_INTERACTIVE=true |
Disables prompts |
RNU_LANG |
Optional language override: zh/zh-CN selects Chinese; any other value selects English. If unset, the CLI uses LC_ALL, LC_MESSAGES, LANG, then the system locale. |
RNU_DEBUG=1 |
Prints error stacks |
HTTPS_PROXY / HTTP_PROXY / NO_PROXY |
Proxies; lowercase names supported |
PAKTA_CACHE_DIR |
Hermes base cache directory |
PAKTA_HERMES_PROBE_TIMEOUT_MS / PAKTA_HERMES_VERIFY_TIMEOUT_MS / PAKTA_HERMES_COMPILE_TIMEOUT_MS |
Hermes probe, equivalence verification and compiler/sourcemap deadlines in milliseconds |
RNU_AUTO_UPDATE=0 |
Disables background updates |
Global installations can update in the background after a successful command, retaining their package manager, registry, authentication, proxy and global prefix. CI, local dependencies and npx installs are not modified. Alternatively set RNU_DISABLE_AUTO_UPDATE=1, or select npm, pnpm, yarn or bun with RNU_AUTO_UPDATE_PACKAGE_MANAGER.
MIT · Copyright (c) 2026 Yoghourt Technology(BeiJing) Company Limited.