@renbostudios/edge-ota 0.3.1 → 0.4.2

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 CHANGED
@@ -1,26 +1,522 @@
1
- # @renbostudios/edge-ota CLI
1
+ <p align="center">
2
+ <img src="https://ota.renbo.site/og.png" alt="Edge OTA" width="100%">
3
+ </p>
2
4
 
3
- Zero-SDK, self-hostable OTA updates for Expo.
5
+ <h1 align="center">
6
+ <code>edge-ota</code>
7
+ </h1>
4
8
 
5
- See the [main README](../../README.md) for full documentation.
9
+ <p align="center">
10
+ <strong>Zero-SDK, self-hostable OTA update CLI for Expo React Native</strong><br>
11
+ Push signed JavaScript bundles to any EdgeOTA server.
12
+ </p>
6
13
 
7
- ## Commands
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=81C784" alt="npm version"></a>
16
+ <a href="https://github.com/renbostudios/edge-ota-cli/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@renbostudios/edge-ota?style=flat-square&color=FFAA00" alt="license"></a>
17
+ <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=81C784" alt="downloads"></a>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <a href="https://github.com/renbostudios/edge-ota-cli">GitHub</a> · <a href="https://ota.renbo.site">Dashboard</a> · <a href="https://ota.renbo.site/docs">Docs</a> · <a href="https://pd1mvpk7lt.ufs.sh/f/6uDGWrRxa3ipmhByNjc2TlHph0oA4dsWRFUuqZMan18Gwb7g">Video Tutorial</a>
22
+ </p>
23
+
24
+ ---
25
+
26
+ <br>
8
27
 
9
28
  ```
10
- edge-ota login # Log in globally
11
- edge-ota logout # Log out
12
- edge-ota init # Set up a new project
13
- edge-ota push # Publish an OTA update
14
- edge-ota status # View recent deployments
15
- edge-ota keygen # Generate a new key pair
29
+ ╔══════════════════════════════════════════════════════════════════╗
30
+ ║ ║
31
+ ║ ┌─┐┬┌─┬┌┬┐┌─┐┌─┐┬ ┬┌─┐┌┐┌┌┬┐┌─┐ ║
32
+ ║ └─┐││ ┤│││├┤ │ ┬├─┤├─┤│││ │ ┤ └─┐ ║
33
+ ║ └─┘┴└─┴┴ ┴└ └─┘┴ ┴┴ ┴┘└┘─┴┘└─┘└─┘ ║
34
+ ║ ║
35
+ ║ Push signed OTA bundles to the edge. ║
36
+ ║ Zero SDK. Zero egress. Full control. ║
37
+ ║ ║
38
+ ╚══════════════════════════════════════════════════════════════════╝
16
39
  ```
17
40
 
18
- ## Install
41
+ <br>
42
+
43
+ ## ⚡ Quick Start
44
+
45
+ ```
46
+ ┌──────────────────────────────────────────────────────────────┐
47
+ │ STEP 01 │
48
+ │ Install the CLI globally │
49
+ └──────────────────────────────────────────────────────────────┘
50
+ ```
19
51
 
20
52
  ```bash
21
53
  npm install -g @renbostudios/edge-ota
22
54
  ```
23
55
 
56
+ ```
57
+ ┌──────────────────────────────────────────────────────────────┐
58
+ │ STEP 02 │
59
+ │ Log in to your EdgeOTA account │
60
+ └──────────────────────────────────────────────────────────────┘
61
+ ```
62
+
63
+ ```bash
64
+ edge-ota login
65
+ ```
66
+
67
+ ```
68
+ ┌──────────────────────────────────────────────────────────────┐
69
+ │ STEP 03 │
70
+ │ Register your project & generate signing keys │
71
+ └──────────────────────────────────────────────────────────────┘
72
+ ```
73
+
74
+ ```bash
75
+ edge-ota init
76
+ ```
77
+
78
+ ```
79
+ ┌──────────────────────────────────────────────────────────────┐
80
+ │ STEP 04 │
81
+ │ ⚠ REBUILD YOUR NATIVE APP (required!) │
82
+ └──────────────────────────────────────────────────────────────┘
83
+ ```
84
+
85
+ ```bash
86
+ npx expo prebuild --clean
87
+ npx expo run:ios
88
+ npx expo run:android
89
+ ```
90
+
91
+ ```
92
+ ┌──────────────────────────────────────────────────────────────┐
93
+ │ STEP 05 │
94
+ │ Push an OTA update 🚀 │
95
+ └──────────────────────────────────────────────────────────────┘
96
+ ```
97
+
98
+ ```bash
99
+ edge-ota push
100
+ ```
101
+
102
+ > **📹 Prefer video?** Watch the full setup tutorial below:
103
+
104
+ <p align="center">
105
+ <video src="https://pd1mvpk7lt.ufs.sh/f/6uDGWrRxa3ipmhByNjc2TlHph0oA4dsWRFUuqZMan18Gwb7g" controls width="100%" style="max-width: 640px; border: 1px solid #333; border-radius: 4px; background: #000;">
106
+ Your browser does not support the video tag.
107
+ </video>
108
+ </p>
109
+
110
+ <br>
111
+
112
+ ---
113
+
114
+ <br>
115
+
116
+ ## 📦 Commands
117
+
118
+ ### `edge-ota login`
119
+
120
+ Authenticate with your EdgeOTA account. Credentials are stored globally — no per-project `.env` files needed.
121
+
122
+ ```bash
123
+ edge-ota login # default server
124
+ edge-ota login --server https://my-server.com # custom server
125
+ ```
126
+
127
+ ```
128
+ ┌──────────────────────────────────────────────┐
129
+ │ 📁 Stored at: │
130
+ │ ~/.config/edge-ota/config.json │
131
+ │ │
132
+ │ Contains: auth token, email, server URL │
133
+ └──────────────────────────────────────────────┘
134
+ ```
135
+
136
+ ---
137
+
138
+ ### `edge-ota logout`
139
+
140
+ Remove stored credentials from this machine.
141
+
142
+ ```bash
143
+ edge-ota logout
144
+ ```
145
+
146
+ ---
147
+
148
+ ### `edge-ota init`
149
+
150
+ The all-in-one setup command. Registers your project, generates ECDSA P-256 signing keys, and configures `app.json`.
151
+
152
+ ```bash
153
+ edge-ota init # interactive setup
154
+ edge-ota init --server https://my-server.com # override server
155
+ ```
156
+
157
+ **What happens under the hood:**
158
+
159
+ ```
160
+ ① Fetches your existing projects from the server
161
+ ② Lets you select an existing project or create a new one
162
+ ③ Generates an ECDSA P-256 key pair
163
+ ④ Registers the project with your public key
164
+ ⑤ Saves private key → ~/.config/edge-ota/keys/<projectId>.key
165
+ ⑥ Updates app.json with server URL, public key, and headers
166
+ ```
167
+
168
+ > ⚠ **After `init`, you MUST rebuild your native app.** See [Rebuild Required](#-rebuild-required) below.
169
+
170
+ ---
171
+
172
+ ### `edge-ota push`
173
+
174
+ Export your Expo bundle and publish an OTA update.
175
+
176
+ ```bash
177
+ edge-ota push # all platforms, production channel
178
+ edge-ota push --channel staging # deploy to staging
179
+ edge-ota push --runtime 1.0.0 # target specific runtime version
180
+ edge-ota push --runtime 1.0.5,1.0.6,1.0.7 # target multiple runtime versions (matrix deployment)
181
+ edge-ota push --platform ios # iOS only
182
+ edge-ota push --platform android # Android only
183
+ edge-ota push --skip-export # use existing ./dist
184
+ edge-ota push --dry-run # build & sign, don't upload
185
+ ```
186
+
187
+ **What happens under the hood:**
188
+
189
+ ```
190
+ ① Runs `npx expo export` to create the JS bundle
191
+ ② Collects all assets from ./dist
192
+ ③ For each platform (ios/android):
193
+ → Locates the platform-specific bundle
194
+ → Signs the payload with your ECDSA private key
195
+ → Uploads bundle + signature to the server
196
+ ④ Server verifies the signature before storing
197
+ ```
198
+
199
+ **How it reads your config:**
200
+
201
+ | Source | What it reads |
202
+ |---|---|
203
+ | `expo.updates.url` | Server URL + Project ID |
204
+ | `expo.runtimeVersion` | Runtime version (string or policy) |
205
+ | `~/.config/edge-ota/keys/<id>.key` | ECDSA private key |
206
+ | `expo.extra.edgeOtaServer` | Server URL override |
207
+
24
208
  ---
25
209
 
26
- by **[Renbo Studios](https://renbostudios.com)** — Mobile App Development Studio
210
+ ### `edge-ota status`
211
+
212
+ List recent deployments for the current project.
213
+
214
+ ```bash
215
+ edge-ota status
216
+ edge-ota status --limit 20 # show more releases
217
+ ```
218
+
219
+ ---
220
+
221
+ ### `edge-ota keygen`
222
+
223
+ Generate a new ECDSA P-256 key pair and print it to stdout. Does not write any files.
224
+
225
+ ```bash
226
+ edge-ota keygen
227
+ ```
228
+
229
+ Useful for manual key rotation or generating keys for server-side signing.
230
+
231
+ <br>
232
+
233
+ ---
234
+
235
+ <br>
236
+
237
+ ## 🔄 Rebuild Required
238
+
239
+ After running `edge-ota init`, you **must** rebuild your native app. The `expo-updates` native module reads its config from the **native binary**, not from `app.json` at runtime.
240
+
241
+ ```bash
242
+ npx expo prebuild --clean
243
+ npx expo run:ios
244
+ npx expo run:android
245
+
246
+ # or via EAS
247
+ eas build --profile production
248
+ ```
249
+
250
+ ```
251
+ ╔══════════════════════════════════════════════════════════════════╗
252
+ ║ ⚠ SKIP THIS STEP AND YOU'LL SEE: ║
253
+ ║ ║
254
+ ║ "Failed to check for update" ║
255
+ ║ ║
256
+ ║ The native module has no valid server URL to reach because ║
257
+ ║ it was never embedded in the binary. ║
258
+ ╚══════════════════════════════════════════════════════════════════╝
259
+ ```
260
+
261
+ You must also rebuild after any change to `expo.updates.url` or `expo.runtimeVersion` in `app.json`.
262
+
263
+ <br>
264
+
265
+ ---
266
+
267
+ <br>
268
+
269
+ ## 🔁 How It Works
270
+
271
+ ```
272
+ Developer Machine EdgeOTA Server User's Phone
273
+ ───────────────── ────────────── ────────────
274
+
275
+ edge-ota push ──────────────► POST /api/updates
276
+ (ECDSA verified ✓)
277
+ (stores bundle)
278
+ ◄──────────── GET /api/updates
279
+ (expo-updates SDK)
280
+ ────────────► signed manifest
281
+ ◄──────────── GET /api/assets/:hash
282
+ (downloads bundle)
283
+ (reloads app ✓)
284
+ ```
285
+
286
+ 1. `edge-ota push` runs `expo export` and uploads the Hermes bundle + ECDSA signature
287
+ 2. The server **verifies** the ECDSA P-256 signature before storing anything
288
+ 3. When users open the app, `expo-updates` checks for a new manifest
289
+ 4. The server responds with a signed multipart manifest (Expo Updates Protocol v1)
290
+ 5. `expo-updates` downloads and applies the update automatically
291
+
292
+ <br>
293
+
294
+ ---
295
+
296
+ <br>
297
+
298
+ ## ⚙ What `init` Writes to `app.json`
299
+
300
+ ```json
301
+ {
302
+ "expo": {
303
+ "updates": {
304
+ "url": "https://your-server.com/api/projects/<projectId>/updates",
305
+ "checkAutomatically": "ON_LOAD",
306
+ "fallbackToCacheTimeout": 30000,
307
+ "requestHeaders": {
308
+ "expo-channel-name": "production"
309
+ }
310
+ },
311
+ "runtimeVersion": "1.0.0",
312
+ "extra": {
313
+ "edgeOtaServer": "https://your-server.com",
314
+ "edgeOtaPublicKey": "-----BEGIN PUBLIC KEY-----\n..."
315
+ }
316
+ }
317
+ }
318
+ ```
319
+
320
+ > `runtimeVersion` is **not modified** by the CLI — it must already exist in your `app.json`. Supports both static strings and policy objects:
321
+
322
+ ```json
323
+ "runtimeVersion": "1.0.0"
324
+ ```
325
+ ```json
326
+ "runtimeVersion": { "policy": "appVersion" }
327
+ ```
328
+
329
+ <br>
330
+
331
+ ---
332
+
333
+ <br>
334
+
335
+ ## 🗂 Global Storage
336
+
337
+ All credentials and keys are stored **outside** your project directory:
338
+
339
+ ```
340
+ ~/.config/edge-ota/
341
+ ├── config.json ← auth token, email, server URL
342
+ ├── keys/
343
+ │ └── <projectId>.key ← ECDSA private key (per-project)
344
+ └── update-check.json ← auto-update check timestamp
345
+ ```
346
+
347
+ ```
348
+ ┌──────────────────────────────────────────────────────────────┐
349
+ │ 🔒 Nothing sensitive is ever stored in your project repo. │
350
+ └──────────────────────────────────────────────────────────────┘
351
+ ```
352
+
353
+ <br>
354
+
355
+ ---
356
+
357
+ <br>
358
+
359
+ ## 🧪 Environment Variables
360
+
361
+ | Variable | Purpose |
362
+ |---|---|
363
+ | `EDGE_OTA_TOKEN` | Override the auth token (for CI/CD pipelines) |
364
+ | `EDGE_OTA_UPDATING` | Internal — prevents recursive auto-updates |
365
+
366
+ <br>
367
+
368
+ ---
369
+
370
+ <br>
371
+
372
+ ## 🔄 Auto-Update
373
+
374
+ The CLI checks for newer versions on npm every 15 minutes. If a newer version is found, it auto-installs globally. This happens silently in the background and never blocks command execution.
375
+
376
+ To disable: set `EDGE_OTA_UPDATING=true` in your environment.
377
+
378
+ <br>
379
+
380
+ ---
381
+
382
+ <br>
383
+
384
+ ## 🔐 Security
385
+
386
+ ```
387
+ ┌──────────────────────────────────────────────────────────────┐
388
+ │ │
389
+ │ ✅ ECDSA P-256 signing on every bundle upload │
390
+ │ ✅ Server rejects invalid or missing signatures │
391
+ │ ✅ Private keys stored with 0600 permissions │
392
+ │ ✅ Session tokens expire after 24 hours │
393
+ │ ✅ Private keys never leave your machine │
394
+ │ │
395
+ └──────────────────────────────────────────────────────────────┘
396
+ ```
397
+
398
+ <br>
399
+
400
+ ---
401
+
402
+ <br>
403
+
404
+ ## 🖥 Server Setup
405
+
406
+ The EdgeOTA server is a standard Node.js/Express app. Self-host it or use the managed cloud service at [ota.renbo.site](https://ota.renbo.site).
407
+
408
+ ### Docker
409
+
410
+ ```bash
411
+ docker pull ghcr.io/itskodaaa/edge-ota-backend:latest
412
+ docker run -p 3000:3000 -e DATABASE_URL=... ghcr.io/itskodaaa/edge-ota-backend
413
+ ```
414
+
415
+ ### Docker Compose
416
+
417
+ ```bash
418
+ git clone https://github.com/renbostudios/edge-ota-cli
419
+ cd edge-ota
420
+ docker-compose up -d
421
+ ```
422
+
423
+ ### Manual
424
+
425
+ ```bash
426
+ git clone https://github.com/renbostudios/edge-ota-cli
427
+ cd edge-ota
428
+ pnpm install
429
+ pnpm --filter @renbostudios/edge-ota-server-node dev
430
+ ```
431
+
432
+ <br>
433
+
434
+ ---
435
+
436
+ <br>
437
+
438
+ ## 🐛 Troubleshooting
439
+
440
+ <details>
441
+ <summary><strong>"Failed to check for update" on app launch</strong></summary>
442
+
443
+ <br>
444
+
445
+ The `expo-updates` native module can't reach your server because the URL was never embedded in the binary.
446
+
447
+ ```bash
448
+ npx expo prebuild --clean
449
+ npx expo run:ios
450
+ npx expo run:android
451
+ ```
452
+
453
+ </details>
454
+
455
+ <details>
456
+ <summary><strong>Updates work on iOS but not Android</strong></summary>
457
+
458
+ <br>
459
+
460
+ The Android native project may still have the default Expo Updates URL. Regenerate and rebuild:
461
+
462
+ ```bash
463
+ npx expo prebuild --clean
464
+ npx expo run:android
465
+ ```
466
+
467
+ Verify by checking `android/app/src/main/AndroidManifest.xml` for the `EXPO_UPDATE_URL` meta-data entry.
468
+
469
+ </details>
470
+
471
+ <details>
472
+ <summary><strong>Updates stopped working after a version bump</strong></summary>
473
+
474
+ <br>
475
+
476
+ If `runtimeVersion` uses the `appVersion` policy, every version bump changes the runtime version. Push a new update after bumping, or switch to a static string.
477
+
478
+ </details>
479
+
480
+ <details>
481
+ <summary><strong>"No signing key found" error on push</strong></summary>
482
+
483
+ <br>
484
+
485
+ Run `edge-ota init` to generate keys, or use `edge-ota keygen` to generate a new pair and register it manually.
486
+
487
+ </details>
488
+
489
+ <br>
490
+
491
+ ---
492
+
493
+ <br>
494
+
495
+ ## 📂 Project Structure
496
+
497
+ ```
498
+ edge-ota-cli/
499
+ ├── apps/
500
+ │ ├── cli/ ← CLI tool (you are here)
501
+ │ ├── server-node/ ← Express + TypeScript API
502
+ │ ├── worker-oss/ ← Cloudflare Worker (open source)
503
+ │ ├── dashboard/ ← SvelteKit frontend
504
+ │ └── core/ ← Shared crypto/manifest logic
505
+ └── packages/
506
+ └── core/ ← @renbostudios/edge-ota-core
507
+ ```
508
+
509
+ <br>
510
+
511
+ ---
512
+
513
+ <br>
514
+
515
+ <p align="center">
516
+ <strong>Built by</strong><br>
517
+ <a href="https://renbostudios.com">Renbo Studios</a> — Mobile App Development Studio
518
+ </p>
519
+
520
+ <p align="center">
521
+ <sub>MIT License · <code>@renbostudios/edge-ota</code></sub>
522
+ </p>