@renbostudios/edge-ota
v0.4.3
Published
Zero-SDK, self-hostable OTA update CLI for Expo — push signed JS bundles to any EdgeOTA server
Downloads
40
Maintainers
Readme
Highlights
- Zero-SDK Footprint: Powered natively by the official
expo-updateslibrary already inside your Expo app. No proprietary runtime agents, binary bloat, or invasive hooks. - Zero Egress Bandwidth Fees: Store and distribute bundles via Cloudflare R2 and Workers. High-concurrency global cache invalidation without per-gigabyte bandwidth penalties.
- Hardware-Grade Code Signing: Every Hermes bundle is signed locally using ECDSA P-256 (
prime256v1). Your private key is stored securely in~/.config/edge-ota/keys/and never leaves your machine. - Multi-Runtime Matrix Updates: Target multiple native app versions simultaneously in a single command (
--runtime 1.0.0,1.0.1,1.0.2). - 100% Protocol v1 Compliant: Produces standard RFC multipart/mixed manifests and Structured Field Values (
SFV) signature headers expected by Expo clients. - True Open-Core & Self-Hostable: Deploy on your own Cloudflare account (
apps/worker-oss) or Docker VPS (apps/server-node), or connect directly to the hosted cloud at ota.renbo.site.
Video Walkthrough
Watch the 45-second terminal walkthrough demonstrating edge-ota init, project registration, and publishing an instant OTA hotfix:
Live Terminal Flow
60-Second Quick Start
1. Install the CLI
Install the official EdgeOTA CLI globally:
npm install -g @renbostudios/edge-ota2. Authenticate
Log in to your account (or specify your self-hosted server via --server):
edge-ota login3. Initialize Your Project
From the root of your Expo React Native project, run:
edge-ota initedge-ota init automatically:
- Generates a local cryptographic ECDSA P-256 key pair.
- Registers your project and securely records the public key with the backend.
- Automatically patches
app.jsonwith the manifest endpoint and security metadata.
4. Rebuild Native Binary (Required Once)
The expo-updates native client embeds the update server URL into your native binary at compile time:
# Clean prebuild and compile locally
npx expo prebuild --clean
npx expo run:ios
npx expo run:android
# Or build in the cloud with EAS
eas build --profile production[!IMPORTANT] Whenever
expo.updates.urlorruntimeVersionchanges, you must runprebuildor generate a new native build so native code knows where to fetch manifests.
5. Publish an OTA Update
Push your updated JavaScript code and assets live:
edge-ota push --channel productionYour app silently downloads the update in the background and applies it seamlessly on the next cold launch.
CLI Command Reference
edge-ota <command> [options]| Command | Description | Common Flags |
| :--- | :--- | :--- |
| edge-ota login | Authenticate and save credentials globally | --server <url>, --email <email> |
| edge-ota logout | Clear stored authentication session | — |
| edge-ota init | Register project, generate keys, & configure app.json | --server <url> |
| edge-ota push | Export Hermes bundle, sign locally, and publish | --channel <name>, --runtime <ver>, --platform <p>, --dry-run |
| edge-ota status | Inspect recent releases, channels, and deployments | --limit <n>, --channel <name> |
| edge-ota keygen | Generate a standalone ECDSA P-256 key pair to stdout | — |
Advanced Workflows
Multi-Runtime Matrix Deployment
Deploy a single JS patch simultaneously across multiple released versions of your native app:
edge-ota push --runtime 1.0.0,1.0.1,1.0.2 --channel productionPlatform Targeting
Deploy an update exclusively to iOS or Android devices:
edge-ota push --platform ios --channel productionDry Run & CI/CD Pipelines
Export and sign the bundle without uploading to test your pipeline:
# Validate export and ECDSA signature locally
edge-ota push --dry-run
# Upload pre-exported ./dist bundle from previous CI build step
edge-ota push --skip-export --channel productionArchitecture & Data Flow
Developer Workstation EdgeOTA Backend User Device
───────────────────── ─────────────── ───────────
edge-ota push ────────────────► POST /api/updates
(ECDSA verified)
(stores bundle & assets in R2)
◄─────────────── GET /api/updates
(expo-updates client)
────────────────► Signed Multipart Manifest
◄─────────────── GET /api/assets/:hash
(downloads bundle)
(app reloads silently)- Local Compilation & Signing:
edge-ota pushrunsexpo exportto produce Hermes bytecode bundles, hashes static assets with SHA-256, and signs the bundle manifest locally with your private ECDSA P-256 key. - Edge Verification: The EdgeOTA API validates the cryptographic signature against the registered public key and confirms all asset hashes before accepting the release.
- Multipart Manifest Protocol: When client devices check for updates, the EdgeOTA backend generates an official Expo Updates Protocol v1
multipart/mixedresponse withexpo-signatureheaders. - Silent Edge Delivery: The client downloads diffed assets directly from Cloudflare R2 edge locations with zero latency and applies the update on next launch.
Configuration (app.json)
edge-ota init populates your app.json with standard Expo configuration:
{
"expo": {
"updates": {
"url": "https://ota.renbo.site/api/projects/<projectId>/updates",
"checkAutomatically": "ON_LOAD",
"fallbackToCacheTimeout": 30000,
"requestHeaders": {
"expo-channel-name": "production"
}
},
"runtimeVersion": "1.0.0",
"extra": {
"edgeOtaServer": "https://ota.renbo.site",
"edgeOtaPublicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...\n-----END PUBLIC KEY-----"
}
}
}Self-Hosting Options
EdgeOTA is 100% open-core. You can host the entire platform on your own cloud infrastructure:
Option 1: Cloudflare Worker + D1 + R2 (Recommended)
Deploy a global serverless OTA engine with zero egress fees:
cd apps/worker-oss
npm install
# Create Cloudflare D1 database and R2 storage bucket
npx wrangler d1 create edge-ota-db
npx wrangler r2 bucket create edge-ota-assets
# Deploy to Cloudflare Edge
npx wrangler deployOption 2: Docker Compose (Node.js + SQLite / Postgres)
Deploy anywhere with a single container stack:
git clone https://github.com/renbostudios/edge-ota.git
cd edge-ota
docker compose up -dThe Node.js server will boot on port 3000 with automated database migrations and local asset storage.
Security Model
- ECDSA P-256 Code Signing: Every bundle is cryptographically verified before serving. Tampered bundles or unauthorized uploads are rejected by the backend.
- Local Key Storage: Private keys are stored on your local disk at
~/.config/edge-ota/keys/<projectId>.keywith restrictive0600file permissions. Private keys are never uploaded to EdgeOTA servers. - Structured Field Values: Signatures are serialized according to RFC 8941 Structured Field Values (
expo-signature="sig=...").
Troubleshooting
The expo-updates native module cannot contact your update server because the server URL was not embedded in the native binary.
Run a clean native build:
npx expo prebuild --clean
npx expo run:ios
npx expo run:androidEnsure android/app/src/main/AndroidManifest.xml contains the EXPO_UPDATE_URL meta-data entry. If testing against a local dev server, allow cleartext HTTP traffic:
android:usesCleartextTraffic="true"And forward the port over ADB:
adb reverse tcp:3000 tcp:3000Run edge-ota init in your project root to generate and register a key pair, or run edge-ota keygen to generate a key manually.
Because EdgeOTA uses the standard Expo Updates Protocol v1, there is zero vendor lock-in. To migrate between EdgeOTA Cloud, self-hosted Cloudflare Workers, or custom servers, simply update expo.updates.url in app.json and run npx expo prebuild --clean.
Monorepo Topology
edge-ota/
├── apps/
│ ├── cli/ → @renbostudios/edge-ota CLI (npm distribution)
│ ├── server-node/ → Self-hosted Express + SQLite/Postgres API
│ └── worker-oss/ → Serverless Cloudflare Worker + D1 + R2 engine
├── packages/
│ └── core/ → ECDSA P-256 crypto & Expo Updates Protocol v1 manifests
└── assets/ → Banners, terminal demos, and walkthrough previews