@renbostudios/edge-ota 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +197 -199
- package/dist/index.js +1186 -890
- package/package.json +20 -15
- package/dist/core/crypto.js +0 -87
- package/dist/core/index.js +0 -2
- package/dist/core/manifest.js +0 -108
package/README.md
CHANGED
|
@@ -1,195 +1,200 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://ota.renbo.site">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/renbostudios/edge-ota/main/assets/banner-hero.png" alt="EdgeOTA — Zero-SDK OTA Engine for Expo" width="100%">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<h1 align="center">@renbostudios/edge-ota</h1>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>Zero-SDK, serverless, self-hostable OTA update CLI for Expo & React Native</strong><br>
|
|
11
|
+
Push cryptographically signed JS bundles to Cloudflare Edge or your own VPS with zero egress fees.
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://www.npmjs.com/package/@renbostudios/edge-ota"><img src="https://img.shields.io/npm/v/@renbostudios/edge-ota?style=flat-square&color=000000&labelColor=161616" alt="npm version"></a>
|
|
16
|
+
<a href="https://www.npmjs.com/package/@renbostudios/edge-ota"><img src="https://img.shields.io/npm/dm/@renbostudios/edge-ota?style=flat-square&color=000000&labelColor=161616" alt="npm downloads"></a>
|
|
17
|
+
<a href="https://github.com/renbostudios/edge-ota/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-000000?style=flat-square&labelColor=161616" alt="license"></a>
|
|
18
|
+
<a href="https://docs.expo.dev/technical-specs/expo-updates-0/"><img src="https://img.shields.io/badge/Protocol-Expo_v1-000000?style=flat-square&labelColor=161616&logo=expo&logoColor=white" alt="Expo Protocol v1"></a>
|
|
19
|
+
<a href="https://developers.cloudflare.com/r2/"><img src="https://img.shields.io/badge/Storage-Cloudflare_R2_(Zero_Egress)-000000?style=flat-square&labelColor=161616&logo=cloudflare&logoColor=white" alt="Cloudflare R2"></a>
|
|
20
|
+
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-Strict_ESM-000000?style=flat-square&labelColor=161616&logo=typescript&logoColor=white" alt="TypeScript"></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<a href="https://ota.renbo.site"><strong>Cloud Console</strong></a> ·
|
|
25
|
+
<a href="https://ota.renbo.site/docs"><strong>Documentation</strong></a> ·
|
|
26
|
+
<a href="#video-walkthrough"><strong>Video Walkthrough</strong></a> ·
|
|
27
|
+
<a href="https://www.npmjs.com/package/@renbostudios/edge-ota"><strong>npm Package</strong></a> ·
|
|
28
|
+
<a href="https://github.com/renbostudios/edge-ota/issues"><strong>Report Issue</strong></a>
|
|
29
|
+
</p>
|
|
2
30
|
|
|
3
|
-
|
|
31
|
+
---
|
|
4
32
|
|
|
5
|
-
##
|
|
33
|
+
## Highlights
|
|
6
34
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
35
|
+
- **Zero-SDK Footprint**: Powered natively by the official `expo-updates` library already inside your Expo app. No proprietary runtime agents, binary bloat, or invasive hooks.
|
|
36
|
+
- **Zero Egress Bandwidth Fees**: Store and distribute bundles via Cloudflare R2 and Workers. High-concurrency global cache invalidation without per-gigabyte bandwidth penalties.
|
|
37
|
+
- **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.
|
|
38
|
+
- **Multi-Runtime Matrix Updates**: Target multiple native app versions simultaneously in a single command (`--runtime 1.0.0,1.0.1,1.0.2`).
|
|
39
|
+
- **100% Protocol v1 Compliant**: Produces standard RFC multipart/mixed manifests and Structured Field Values (`SFV`) signature headers expected by Expo clients.
|
|
40
|
+
- **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](https://ota.renbo.site).
|
|
10
41
|
|
|
11
|
-
|
|
42
|
+
---
|
|
12
43
|
|
|
13
|
-
##
|
|
44
|
+
## Video Walkthrough
|
|
14
45
|
|
|
15
|
-
|
|
16
|
-
# 1. Log in to your EdgeOTA account
|
|
17
|
-
edge-ota login
|
|
46
|
+
Watch the 45-second terminal walkthrough demonstrating `edge-ota init`, project registration, and publishing an instant OTA hotfix:
|
|
18
47
|
|
|
19
|
-
|
|
20
|
-
|
|
48
|
+
<p align="center">
|
|
49
|
+
<video src="https://github.com/user-attachments/assets/476fd099-6834-45a5-b11b-130b3b8fab02" autoplay loop muted playsinline controls width="100%" style="max-width: 720px; border-radius: 8px; border: 1px solid #333333;">
|
|
50
|
+
</video>
|
|
51
|
+
</p>
|
|
21
52
|
|
|
22
|
-
|
|
23
|
-
npx expo prebuild --clean
|
|
24
|
-
npx expo run:ios
|
|
25
|
-
npx expo run:android
|
|
53
|
+
### Live Terminal Flow
|
|
26
54
|
|
|
27
|
-
|
|
28
|
-
edge-ota
|
|
29
|
-
|
|
55
|
+
<p align="center">
|
|
56
|
+
<img src="https://raw.githubusercontent.com/renbostudios/edge-ota/main/assets/demo.gif" alt="EdgeOTA Live Terminal Demo" width="100%" style="max-width: 720px; border-radius: 8px; border: 1px solid #27272a;">
|
|
57
|
+
</p>
|
|
30
58
|
|
|
31
59
|
---
|
|
32
60
|
|
|
33
|
-
##
|
|
61
|
+
## 60-Second Quick Start
|
|
34
62
|
|
|
35
|
-
###
|
|
63
|
+
### 1. Install the CLI
|
|
36
64
|
|
|
37
|
-
|
|
65
|
+
Install the official EdgeOTA CLI globally:
|
|
38
66
|
|
|
39
67
|
```bash
|
|
40
|
-
edge-ota
|
|
41
|
-
edge-ota login --server https://my-server.com # custom server
|
|
68
|
+
npm install -g @renbostudios/edge-ota
|
|
42
69
|
```
|
|
43
70
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
### `edge-ota logout`
|
|
71
|
+
### 2. Authenticate
|
|
47
72
|
|
|
48
|
-
|
|
73
|
+
Log in to your account (or specify your self-hosted server via `--server`):
|
|
49
74
|
|
|
50
75
|
```bash
|
|
51
|
-
edge-ota
|
|
76
|
+
edge-ota login
|
|
52
77
|
```
|
|
53
78
|
|
|
54
|
-
###
|
|
79
|
+
### 3. Initialize Your Project
|
|
55
80
|
|
|
56
|
-
|
|
81
|
+
From the root of your Expo React Native project, run:
|
|
57
82
|
|
|
58
83
|
```bash
|
|
59
84
|
edge-ota init
|
|
60
|
-
edge-ota init --server https://my-server.com # override server
|
|
61
85
|
```
|
|
62
86
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
3. Generates an ECDSA P-256 key pair
|
|
68
|
-
4. Registers the project on the server with your public key
|
|
69
|
-
5. Saves the private key to `~/.config/edge-ota/keys/<projectId>.key`
|
|
70
|
-
6. Updates your `app.json` with:
|
|
71
|
-
- `expo.updates.url` — points to your EdgeOTA server
|
|
72
|
-
- `expo.updates.checkAutomatically` — set to `"ON_LOAD"`
|
|
73
|
-
- `expo.updates.fallbackToCacheTimeout` — set to `30000`
|
|
74
|
-
- `expo.updates.requestHeaders` — sets `expo-channel-name` to `"production"`
|
|
75
|
-
- `expo.extra.edgeOtaServer` — the server URL for manual override
|
|
76
|
-
- `expo.extra.edgeOtaPublicKey` — the generated public key
|
|
77
|
-
|
|
78
|
-
**After init, you MUST rebuild your native app.** The CLI will print a prominent warning with instructions. See the [Rebuild Required](#rebuild-required) section below.
|
|
87
|
+
`edge-ota init` automatically:
|
|
88
|
+
1. Generates a local cryptographic **ECDSA P-256** key pair.
|
|
89
|
+
2. Registers your project and securely records the public key with the backend.
|
|
90
|
+
3. Automatically patches `app.json` with the manifest endpoint and security metadata.
|
|
79
91
|
|
|
80
|
-
###
|
|
92
|
+
### 4. Rebuild Native Binary (Required Once)
|
|
81
93
|
|
|
82
|
-
|
|
94
|
+
The `expo-updates` native client embeds the update server URL into your native binary at compile time:
|
|
83
95
|
|
|
84
96
|
```bash
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
edge-ota push --skip-export # skip expo export, use existing ./dist
|
|
90
|
-
edge-ota push --dry-run # build and sign, but don't upload
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**What it does:**
|
|
94
|
-
|
|
95
|
-
1. Runs `npx expo export` to create the JavaScript bundle (unless `--skip-export`)
|
|
96
|
-
2. Collects all assets from `./dist`
|
|
97
|
-
3. For each target platform (ios/android):
|
|
98
|
-
- Locates the platform-specific bundle
|
|
99
|
-
- Signs the payload with your ECDSA private key
|
|
100
|
-
- Uploads the bundle + signature to the server
|
|
101
|
-
4. The server verifies the signature before storing the update
|
|
97
|
+
# Clean prebuild and compile locally
|
|
98
|
+
npx expo prebuild --clean
|
|
99
|
+
npx expo run:ios
|
|
100
|
+
npx expo run:android
|
|
102
101
|
|
|
103
|
-
|
|
102
|
+
# Or build in the cloud with EAS
|
|
103
|
+
eas build --profile production
|
|
104
|
+
```
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
- **Project ID** — parsed from the updates URL
|
|
108
|
-
- **Runtime version** — from `expo.runtimeVersion` (string or policy object)
|
|
109
|
-
- **Private key** — from `~/.config/edge-ota/keys/<projectId>.key`
|
|
106
|
+
> [!IMPORTANT]
|
|
107
|
+
> Whenever `expo.updates.url` or `runtimeVersion` changes, you must run `prebuild` or generate a new native build so native code knows where to fetch manifests.
|
|
110
108
|
|
|
111
|
-
###
|
|
109
|
+
### 5. Publish an OTA Update
|
|
112
110
|
|
|
113
|
-
|
|
111
|
+
Push your updated JavaScript code and assets live:
|
|
114
112
|
|
|
115
113
|
```bash
|
|
116
|
-
edge-ota
|
|
117
|
-
edge-ota status --limit 20 # show more releases
|
|
114
|
+
edge-ota push --channel production
|
|
118
115
|
```
|
|
119
116
|
|
|
120
|
-
|
|
117
|
+
Your app silently downloads the update in the background and applies it seamlessly on the next cold launch.
|
|
118
|
+
|
|
119
|
+
---
|
|
121
120
|
|
|
122
|
-
|
|
121
|
+
## CLI Command Reference
|
|
123
122
|
|
|
124
123
|
```bash
|
|
125
|
-
edge-ota
|
|
124
|
+
edge-ota <command> [options]
|
|
126
125
|
```
|
|
127
126
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
| Command | Description | Common Flags |
|
|
128
|
+
| :--- | :--- | :--- |
|
|
129
|
+
| `edge-ota login` | Authenticate and save credentials globally | `--server <url>`, `--email <email>` |
|
|
130
|
+
| `edge-ota logout` | Clear stored authentication session | — |
|
|
131
|
+
| `edge-ota init` | Register project, generate keys, & configure `app.json` | `--server <url>` |
|
|
132
|
+
| `edge-ota push` | Export Hermes bundle, sign locally, and publish | `--channel <name>`, `--runtime <ver>`, `--platform <p>`, `--dry-run` |
|
|
133
|
+
| `edge-ota status` | Inspect recent releases, channels, and deployments | `--limit <n>`, `--channel <name>` |
|
|
134
|
+
| `edge-ota keygen` | Generate a standalone ECDSA P-256 key pair to stdout | — |
|
|
131
135
|
|
|
132
|
-
|
|
136
|
+
### Advanced Workflows
|
|
133
137
|
|
|
134
|
-
|
|
138
|
+
#### Multi-Runtime Matrix Deployment
|
|
139
|
+
Deploy a single JS patch simultaneously across multiple released versions of your native app:
|
|
135
140
|
|
|
136
141
|
```bash
|
|
137
|
-
|
|
138
|
-
npx expo prebuild --clean
|
|
139
|
-
|
|
140
|
-
# Rebuild
|
|
141
|
-
npx expo run:ios
|
|
142
|
-
npx expo run:android
|
|
143
|
-
|
|
144
|
-
# Or via EAS
|
|
145
|
-
eas build --profile production
|
|
142
|
+
edge-ota push --runtime 1.0.0,1.0.1,1.0.2 --channel production
|
|
146
143
|
```
|
|
147
144
|
|
|
148
|
-
|
|
145
|
+
#### Platform Targeting
|
|
146
|
+
Deploy an update exclusively to iOS or Android devices:
|
|
149
147
|
|
|
150
|
-
```
|
|
151
|
-
|
|
148
|
+
```bash
|
|
149
|
+
edge-ota push --platform ios --channel production
|
|
152
150
|
```
|
|
153
151
|
|
|
154
|
-
|
|
152
|
+
#### Dry Run & CI/CD Pipelines
|
|
153
|
+
Export and sign the bundle without uploading to test your pipeline:
|
|
155
154
|
|
|
156
|
-
|
|
155
|
+
```bash
|
|
156
|
+
# Validate export and ECDSA signature locally
|
|
157
|
+
edge-ota push --dry-run
|
|
158
|
+
|
|
159
|
+
# Upload pre-exported ./dist bundle from previous CI build step
|
|
160
|
+
edge-ota push --skip-export --channel production
|
|
161
|
+
```
|
|
157
162
|
|
|
158
163
|
---
|
|
159
164
|
|
|
160
|
-
##
|
|
165
|
+
## Architecture & Data Flow
|
|
161
166
|
|
|
162
167
|
```
|
|
163
|
-
Developer
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
168
|
+
Developer Workstation EdgeOTA Backend User Device
|
|
169
|
+
───────────────────── ─────────────── ───────────
|
|
170
|
+
|
|
171
|
+
edge-ota push ────────────────► POST /api/updates
|
|
172
|
+
(ECDSA verified)
|
|
173
|
+
(stores bundle & assets in R2)
|
|
174
|
+
◄─────────────── GET /api/updates
|
|
175
|
+
(expo-updates client)
|
|
176
|
+
────────────────► Signed Multipart Manifest
|
|
177
|
+
◄─────────────── GET /api/assets/:hash
|
|
178
|
+
(downloads bundle)
|
|
179
|
+
(app reloads silently)
|
|
174
180
|
```
|
|
175
181
|
|
|
176
|
-
1. `edge-ota push` runs `expo export` and
|
|
177
|
-
2. The
|
|
178
|
-
3. When
|
|
179
|
-
4. The
|
|
180
|
-
5. `expo-updates` downloads and applies the update automatically
|
|
182
|
+
1. **Local Compilation & Signing**: `edge-ota push` runs `expo export` to produce Hermes bytecode bundles, hashes static assets with SHA-256, and signs the bundle manifest locally with your private ECDSA P-256 key.
|
|
183
|
+
2. **Edge Verification**: The EdgeOTA API validates the cryptographic signature against the registered public key and confirms all asset hashes before accepting the release.
|
|
184
|
+
3. **Multipart Manifest Protocol**: When client devices check for updates, the EdgeOTA backend generates an official Expo Updates Protocol v1 `multipart/mixed` response with `expo-signature` headers.
|
|
185
|
+
4. **Silent Edge Delivery**: The client downloads diffed assets directly from Cloudflare R2 edge locations with zero latency and applies the update on next launch.
|
|
181
186
|
|
|
182
187
|
---
|
|
183
188
|
|
|
184
|
-
##
|
|
189
|
+
## Configuration (`app.json`)
|
|
185
190
|
|
|
186
|
-
|
|
191
|
+
`edge-ota init` populates your `app.json` with standard Expo configuration:
|
|
187
192
|
|
|
188
193
|
```json
|
|
189
194
|
{
|
|
190
195
|
"expo": {
|
|
191
196
|
"updates": {
|
|
192
|
-
"url": "https://
|
|
197
|
+
"url": "https://ota.renbo.site/api/projects/<projectId>/updates",
|
|
193
198
|
"checkAutomatically": "ON_LOAD",
|
|
194
199
|
"fallbackToCacheTimeout": 30000,
|
|
195
200
|
"requestHeaders": {
|
|
@@ -198,131 +203,124 @@ After running `edge-ota init`, your `app.json` will contain:
|
|
|
198
203
|
},
|
|
199
204
|
"runtimeVersion": "1.0.0",
|
|
200
205
|
"extra": {
|
|
201
|
-
"edgeOtaServer": "https://
|
|
202
|
-
"edgeOtaPublicKey": "-----BEGIN PUBLIC KEY-----\n
|
|
206
|
+
"edgeOtaServer": "https://ota.renbo.site",
|
|
207
|
+
"edgeOtaPublicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...\n-----END PUBLIC KEY-----"
|
|
203
208
|
}
|
|
204
209
|
}
|
|
205
210
|
}
|
|
206
211
|
```
|
|
207
212
|
|
|
208
|
-
The `runtimeVersion` field is **not modified** by the CLI — it must already exist in your `app.json`. It supports both static strings and policy objects:
|
|
209
|
-
|
|
210
|
-
```json
|
|
211
|
-
// Static (recommended)
|
|
212
|
-
"runtimeVersion": "1.0.0"
|
|
213
|
-
|
|
214
|
-
// Policy-based (auto-increments with expo.version)
|
|
215
|
-
"runtimeVersion": { "policy": "appVersion" }
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
## Global Storage
|
|
221
|
-
|
|
222
|
-
All credentials and keys are stored outside your project directory:
|
|
223
|
-
|
|
224
|
-
| File | Contents |
|
|
225
|
-
|---|---|
|
|
226
|
-
| `~/.config/edge-ota/config.json` | Auth token, email, default server URL |
|
|
227
|
-
| `~/.config/edge-ota/keys/<projectId>.key` | Private ECDSA signing key (per-project) |
|
|
228
|
-
| `~/.config/edge-ota/update-check.json` | Auto-update check timestamp |
|
|
229
|
-
|
|
230
|
-
Nothing sensitive is ever stored in your project repository.
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## Environment Variables
|
|
235
|
-
|
|
236
|
-
| Variable | Purpose |
|
|
237
|
-
|---|---|
|
|
238
|
-
| `EDGE_OTA_TOKEN` | Override the auth token (useful for CI/CD) |
|
|
239
|
-
| `EDGE_OTA_UPDATING` | Internal — prevents recursive auto-updates |
|
|
240
|
-
|
|
241
213
|
---
|
|
242
214
|
|
|
243
|
-
##
|
|
244
|
-
|
|
245
|
-
The CLI checks for newer versions on npm every 15 minutes. If a newer version is found, it auto-installs globally. This check happens silently in the background and never blocks command execution.
|
|
215
|
+
## Self-Hosting Options
|
|
246
216
|
|
|
247
|
-
|
|
217
|
+
EdgeOTA is 100% open-core. You can host the entire platform on your own cloud infrastructure:
|
|
248
218
|
|
|
249
|
-
|
|
219
|
+
### Option 1: Cloudflare Worker + D1 + R2 (Recommended)
|
|
250
220
|
|
|
251
|
-
|
|
221
|
+
Deploy a global serverless OTA engine with zero egress fees:
|
|
252
222
|
|
|
253
|
-
|
|
254
|
-
-
|
|
255
|
-
|
|
256
|
-
- Session tokens expire after 24 hours
|
|
257
|
-
- Private keys never leave your machine
|
|
223
|
+
```bash
|
|
224
|
+
cd apps/worker-oss
|
|
225
|
+
npm install
|
|
258
226
|
|
|
259
|
-
|
|
227
|
+
# Create Cloudflare D1 database and R2 storage bucket
|
|
228
|
+
npx wrangler d1 create edge-ota-db
|
|
229
|
+
npx wrangler r2 bucket create edge-ota-assets
|
|
260
230
|
|
|
261
|
-
|
|
231
|
+
# Deploy to Cloudflare Edge
|
|
232
|
+
npx wrangler deploy
|
|
233
|
+
```
|
|
262
234
|
|
|
263
|
-
|
|
235
|
+
### Option 2: Docker Compose (Node.js + SQLite / Postgres)
|
|
264
236
|
|
|
265
|
-
|
|
237
|
+
Deploy anywhere with a single container stack:
|
|
266
238
|
|
|
267
239
|
```bash
|
|
268
|
-
|
|
269
|
-
|
|
240
|
+
git clone https://github.com/renbostudios/edge-ota.git
|
|
241
|
+
cd edge-ota
|
|
242
|
+
docker compose up -d
|
|
270
243
|
```
|
|
271
244
|
|
|
272
|
-
|
|
245
|
+
The Node.js server will boot on port `3000` with automated database migrations and local asset storage.
|
|
273
246
|
|
|
274
|
-
|
|
275
|
-
git clone https://github.com/renbostudios/edge-ota-cli
|
|
276
|
-
cd edge-ota
|
|
277
|
-
docker-compose up -d
|
|
278
|
-
```
|
|
247
|
+
---
|
|
279
248
|
|
|
280
|
-
|
|
249
|
+
## Security Model
|
|
281
250
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
pnpm install
|
|
286
|
-
pnpm --filter @renbostudios/edge-ota-server-node dev
|
|
287
|
-
```
|
|
251
|
+
- **ECDSA P-256 Code Signing**: Every bundle is cryptographically verified before serving. Tampered bundles or unauthorized uploads are rejected by the backend.
|
|
252
|
+
- **Local Key Storage**: Private keys are stored on your local disk at `~/.config/edge-ota/keys/<projectId>.key` with restrictive `0600` file permissions. Private keys are never uploaded to EdgeOTA servers.
|
|
253
|
+
- **Structured Field Values**: Signatures are serialized according to RFC 8941 Structured Field Values (`expo-signature="sig=..."`).
|
|
288
254
|
|
|
289
255
|
---
|
|
290
256
|
|
|
291
257
|
## Troubleshooting
|
|
292
258
|
|
|
293
|
-
|
|
259
|
+
<details>
|
|
260
|
+
<summary><strong>"Failed to check for update" on app launch</strong></summary>
|
|
261
|
+
|
|
262
|
+
<br>
|
|
294
263
|
|
|
295
|
-
The `expo-updates` native module
|
|
264
|
+
The `expo-updates` native module cannot contact your update server because the server URL was not embedded in the native binary.
|
|
296
265
|
|
|
266
|
+
Run a clean native build:
|
|
297
267
|
```bash
|
|
298
268
|
npx expo prebuild --clean
|
|
299
269
|
npx expo run:ios
|
|
300
270
|
npx expo run:android
|
|
301
271
|
```
|
|
272
|
+
</details>
|
|
302
273
|
|
|
303
|
-
|
|
274
|
+
<details>
|
|
275
|
+
<summary><strong>Updates apply on iOS but not on Android</strong></summary>
|
|
304
276
|
|
|
305
|
-
|
|
277
|
+
<br>
|
|
306
278
|
|
|
279
|
+
Ensure `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:
|
|
280
|
+
|
|
281
|
+
```xml
|
|
282
|
+
android:usesCleartextTraffic="true"
|
|
283
|
+
```
|
|
284
|
+
And forward the port over ADB:
|
|
307
285
|
```bash
|
|
308
|
-
|
|
309
|
-
npx expo run:android
|
|
286
|
+
adb reverse tcp:3000 tcp:3000
|
|
310
287
|
```
|
|
288
|
+
</details>
|
|
289
|
+
|
|
290
|
+
<details>
|
|
291
|
+
<summary><strong>"No signing key found for project" during push</strong></summary>
|
|
311
292
|
|
|
312
|
-
|
|
293
|
+
<br>
|
|
313
294
|
|
|
314
|
-
|
|
295
|
+
Run `edge-ota init` in your project root to generate and register a key pair, or run `edge-ota keygen` to generate a key manually.
|
|
296
|
+
</details>
|
|
315
297
|
|
|
316
|
-
|
|
298
|
+
<details>
|
|
299
|
+
<summary><strong>Switching between Cloud and Self-Hosted</strong></summary>
|
|
317
300
|
|
|
318
|
-
|
|
301
|
+
<br>
|
|
319
302
|
|
|
320
|
-
|
|
303
|
+
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`.
|
|
304
|
+
</details>
|
|
321
305
|
|
|
322
306
|
---
|
|
323
307
|
|
|
324
|
-
##
|
|
308
|
+
## Monorepo Topology
|
|
325
309
|
|
|
326
|
-
|
|
310
|
+
```
|
|
311
|
+
edge-ota/
|
|
312
|
+
├── apps/
|
|
313
|
+
│ ├── cli/ → @renbostudios/edge-ota CLI (npm distribution)
|
|
314
|
+
│ ├── server-node/ → Self-hosted Express + SQLite/Postgres API
|
|
315
|
+
│ └── worker-oss/ → Serverless Cloudflare Worker + D1 + R2 engine
|
|
316
|
+
├── packages/
|
|
317
|
+
│ └── core/ → ECDSA P-256 crypto & Expo Updates Protocol v1 manifests
|
|
318
|
+
└── assets/ → Banners, terminal demos, and walkthrough previews
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
327
322
|
|
|
328
|
-
|
|
323
|
+
<p align="center">
|
|
324
|
+
Built with care by <a href="https://renbostudios.com">Renbo Studios</a> & open-source contributors.<br>
|
|
325
|
+
<sub>MIT License · Open Source</sub>
|
|
326
|
+
</p>
|