I want to make an Android version of River. I've prototyped it on the salvatoret/android branch, and it runs on a physical device today. Here are the five changes that mattered most.
1. Run a Freenet node on the device
On the web, River is WASM in a browser talking to a remote node. A phone has no browser to host that node, so River now starts a full Network-mode Freenet node in-process when the app launches. Everything else depends on this one.
2. Build the UI as a native Android app, not just WASM
The Dioxus UI now also compiles to a native arm64 binary that renders inside an Android WebView (wry/tao). Browser-only code (web_sys, clipboard, notifications, the browser WebSocket) is gated off the native target so it doesn't crash on startup.
3. Vendor freenet and align the stdlib
Linking freenet as a library meant moving the workspace to freenet-stdlib 0.8 and vendoring freenet with its Windows and macOS GUI dependencies removed, because they clash with dioxus's wry/tao versions. The bundled freenet version also has to track the live network's gateway version, or peers reject the handshake.
4. Connect the UI to the on-device node
The synchronizer talks to the local node over a native WebSocket at 127.0.0.1. Storage lives in the app's private directory, resolved through JNI. A synthetic auth token is registered at startup so the chat delegate accepts the app's own messages.
5. Keep the node alive in the background
A foreground service keeps the node running when the app is backgrounded. It posts an ongoing notification and hands a clean shutdown back to the node's runtime when the user stops it.
Still open: a coordinated web republish so Android and existing web users share the same contract namespace. That one is blocked on the web-container signing key.
📸 Screenshots

I want to make an Android version of River. I've prototyped it on the
salvatoret/androidbranch, and it runs on a physical device today. Here are the five changes that mattered most.1. Run a Freenet node on the device
On the web, River is WASM in a browser talking to a remote node. A phone has no browser to host that node, so River now starts a full Network-mode Freenet node in-process when the app launches. Everything else depends on this one.
2. Build the UI as a native Android app, not just WASM
The Dioxus UI now also compiles to a native arm64 binary that renders inside an Android WebView (wry/tao). Browser-only code (web_sys, clipboard, notifications, the browser WebSocket) is gated off the native target so it doesn't crash on startup.
3. Vendor freenet and align the stdlib
Linking freenet as a library meant moving the workspace to freenet-stdlib 0.8 and vendoring freenet with its Windows and macOS GUI dependencies removed, because they clash with dioxus's wry/tao versions. The bundled freenet version also has to track the live network's gateway version, or peers reject the handshake.
4. Connect the UI to the on-device node
The synchronizer talks to the local node over a native WebSocket at 127.0.0.1. Storage lives in the app's private directory, resolved through JNI. A synthetic auth token is registered at startup so the chat delegate accepts the app's own messages.
5. Keep the node alive in the background
A foreground service keeps the node running when the app is backgrounded. It posts an ongoing notification and hands a clean shutdown back to the node's runtime when the user stops it.
Still open: a coordinated web republish so Android and existing web users share the same contract namespace. That one is blocked on the web-container signing key.
📸 Screenshots