@viettelpost/react-native-ota
v0.1.10
Published
ViettelPost React Native OTA runtime and deploy CLI
Maintainers
Readme
@viettelpost/react-native-ota
Standalone React Native OTA runtime and deploy CLI.
This package contains:
ota deploy: build, zip assets, hash, sign, and register OTA release metadata.- JS API:
OTA.configure(),OTA.sync(), status helpers, and dev utilities. - Native runtime: Android Kotlin and iOS Swift/ObjC++ bundle resolver, installer, verification, rollback, and cleanup.
This package does not integrate itself into a host app. The host app must wire the native bundle resolver at startup before OTA bundles can run.
Each host app must provide the appKey generated for its OTA app row so the backend can resolve its bundle ID and channel.
Setup
Install the package and initialize OTA files from the host React Native app root:
yarn add @viettelpost/react-native-ota
yarn ota initThe initializer creates missing ota.config.js and .env.ota files, adds
.env.ota to .gitignore, and never overwrites existing files. Fill the
runtime app identity in .env.ota:
OTA_API=https://stg-f2c-api.viettelpost.vn/vipo-ota
OTA_APP_KEY=The generated ota.config.js reads these values automatically:
module.exports = {
api: process.env.OTA_API,
appKey: process.env.OTA_APP_KEY,
entryFile: 'index.js',
};CLI
yarn ota deploy --platform android --dry-run
yarn ota deploy --platform ios --dry-run
yarn ota sign --platform ios --bundle ./build/main.jsbundle --version 2026.06.26-001
yarn ota serve --platform androidDeploys use backend storage by default. The CLI uploads bundle files to
vipomall-ota, and the backend stores them through its configured S3 provider.
Use --storage external only when registering files already hosted elsewhere.
Non-dry-run deployment and release-management commands require
OTA_ADMIN_KEY to be injected by the CI secret manager. Do not store that key
in .env.ota or bundle it into the mobile app.
JS API
import { OTA } from '@viettelpost/react-native-ota';
OTA.configure({
appKey: 'apk_...',
baseURL: 'https://ota.example.com',
});
const result = await OTA.sync();For automatic install behavior, keep scheduling and UI in the host app:
import {
checkForUpdate,
installUpdate,
reloadApp,
} from '@viettelpost/react-native-ota';
const update = await checkForUpdate('https://ota.example.com', {
appKey: 'apk_...',
deviceId: 'stable-installation-id',
});
if (update?.isMandatory) {
// Show the host app's blocking update dialog.
await installUpdate(update);
await reloadApp();
}- Mandatory releases should block normal app use, offer an
Update nowaction, and auto-proceed after a short grace period. - Non-mandatory releases should remain silent and call
installUpdate()only after the app resumes from a sufficiently long background period. reloadApp()reloads the React Native bridge; it does not kill the OS process.- Always provide a stable
deviceIdso the backend can consistently enforce rollout percentages before returning update metadata. OTAConfig.policyis deprecated. Install timing belongs to the host app because the package is intentionally headless.
Security
The client verifies SHA-256 for the bundle and optional assets.zip, then verifies an RSA-SHA256 signature over the canonical metadata payload. The private key must never be committed.
Runtime update checks send only X-APP-KEY; they never send X-ADMIN-KEY or
require a logged-in user. The app key identifies the app channel and is not a
release-management credential.
Example
The package test host lives in example/AwesomeProject and uses React Native
0.77.3. Use it for package-level OTA testing instead of validating package
changes through a production host app.
