| … | … | @@ -4,34 +4,44 @@ A **full favela node on a phone** — React Native over a [bare-kit](https://git |
| 4 | 4 | worklet. The phone holds its own DID key, authors and signs its own atproto repo, replicates over |
| 5 | 5 | Hyperswarm and indexes its own appview. |
| 6 | 6 | |
| 7 | | This is **not** favela-web in a WebView, and not a client of a home server. Those are different, |
| 8 | | easier products. See `spec/architecture.md` and `favela-web/spec/mobile-node.md`. |
| 7 | It **is** favela-web in a WebView — but not a client of a home server, which is the distinction that |
| 8 | matters. The phone runs the server. favela-web is embedded in the worklet, serving its pages to |
| 9 | loopback from an hpp that holds the DID key on this device; nothing renders from a remote host. |
| 10 | |
| 11 | ⚠ This reverses the original design, which rendered a hand-written React Native UI over favela-web's |
| 12 | data. That was rebuilt here on 2026-08-11 because the native surface looked nothing like favela and |
| 13 | could only be brought closer by rebuilding every screen a second time, by hand, to a lower standard. |
| 14 | Rendering the real pages means every screen favela-web gains arrives on the phone for free — the |
| 15 | reason to embed a whole appview in an app in the first place. See `spec/architecture.md` and |
| 16 | `favela-web/spec/mobile-node.md`; the WebView decision post-dates both. |
| 9 | 17 | |
| 10 | 18 | ## Status — honest |
| 11 | 19 | |
| 12 | | The APK **builds and is signed. It has never been launched.** Everything below is measured; nothing |
| 13 | | here claims the app runs on a device yet. |
| 20 | The APK builds, is signed, and **runs on hardware** (2026-08-11: boots, serves, indexes, signs in). |
| 21 | Everything below is measured. |
| 14 | 22 | |
| 15 | 23 | | | | |
| 16 | 24 | |---|---| |
| 17 | 25 | | worklet bundle | 19 MB — hpp + appview + swarm, packed by `bare-pack --preset android` | |
| 18 | | | APK | 136 MB, arm64-v8a, minSdk 31, signed | |
| 26 | | APK | 145 MB, arm64-v8a, minSdk 29, signed | |
| 27 | | on-device boot | ~40 s cold: corestore → swarm → appview → favela-web answering `/status` 200 | |
| 19 | 28 | | pairing | 17/17 over a real testnet | |
| 20 | 29 | | IPC contract | 14 assertions against the real worklet, not a mock | |
| 21 | 30 | | storage in a worklet thread | 8/8 on Linux, incl. lock release on exit (the Doze-restart case) | |
| 22 | 31 | | storage on device | `rocksdb dlopen` + `Corestore.ready` on API 34, app-private | |
| 23 | 32 | |
| 24 | | Untested: bare-kit's own worklet lifecycle on hardware, foreground-service survival, and |
| 25 | | hyperdht hole-punching on cellular/CGNAT. |
| 33 | Untested: the WebView surface itself under real use, foreground-service survival, and hyperdht |
| 34 | hole-punching on cellular/CGNAT. |
| 26 | 35 | |
| 27 | 36 | ## Architecture in one paragraph |
| 28 | 37 | |
| 29 | | `lib/node.js` is the worklet entry — a headless full node speaking line-delimited JSON over |
| 30 | | bare-kit's IPC pipe. It binds no HTTP port: React Native does not need one, so objects cross the |
| 31 | | boundary instead of HTML. **The worklet owns all state** — no CAR parsing, no MST, no signing on the |
| 32 | | RN side, so the signing key never leaves the thread that owns it. The appview was always |
| 33 | | render-agnostic (`getTimeline`/`_hydrate` return plain objects), which is why this is a re-render of |
| 34 | | favela-web rather than a rewrite. |
| 38 | `lib/node.js` is the worklet entry — a full node that also brings up **favela-web in-process**, bound |
| 39 | to `127.0.0.1:4041`, and signs in to it with a sub-key it delegates to itself. React Native renders |
| 40 | that server in a WebView; the remaining IPC verbs over bare-kit's pipe are lifecycle and pairing, not |
| 41 | content. **The worklet owns all state** — no CAR parsing, no MST, no signing on the RN side, so the |
| 42 | signing key never leaves the thread that owns it, and writes reach hpp over its local control socket |
| 43 | rather than any network. Because the pages are favela-web's own, this is favela on a phone rather |
| 44 | than a second implementation of it. |
| 35 | 45 | |
| 36 | 46 | ## Two bundlers, two artifacts |
| 37 | 47 | |
| … | … | @@ -80,8 +90,10 @@ The APK is published as a release artifact and served with |
| 80 | 90 | https://7rnx.net/git/releases/favela-android/<version>/app-release.apk |
| 81 | 91 | ``` |
| 82 | 92 | |
| 83 | | Requires **Android 12+** (bare-kit's AAR declares minSdk 31). Sideloading needs "install unknown |
| 84 | | apps" for your browser. |
| 93 | Requires **Android 10+** — `android/build.gradle` sets `minSdkVersion = 29`, matching what |
| 94 | react-native-bare-kit 0.14.5 declares. (Upstream bare-kit has since moved to 0.15.0 / minSdk 31; the |
| 95 | 0.14.5 pin is a deliberate divergence to keep the API-30 handset, not a wait for a fix.) Sideloading |
| 96 | needs "install unknown apps" for your browser. |
| 85 | 97 | |
| 86 | 98 | ⛔ The signing key is durable and kept. Android refuses to upgrade an APK signed with a different |
| 87 | 99 | key, and the only way out is uninstall — which deletes app-private storage, i.e. the node's identity, |