Motivation
IPFS/IPNS currently comes from an embedded Kubo node: mobile.aar (built in solardev-xyz/freedom-node-mobile, gomobile) wrapped by swarmnode/src/main/java/baby/freedom/swarm/IpfsNode.kt. iOS instead embeds solardev-xyz/freedom-ipfs, a read-only Rust IPFS reader with a loopback gateway. Moving Android to freedom-ipfs:
- drops the Go runtime + full Kubo daemon (APK size, memory, battery),
- since the bee→ant swap we only use the Kubo half of
mobile.aar, so this removes the freedom-node-mobile dependency entirely,
- unifies retrieval behaviour with iOS (verified blocks, delegated routing + light-DHT fallback, bounded cache),
- gains real mobile lifecycle hooks (background/foreground, low-memory, network-change) that Kubo never exposed,
- deletes the Kubo config-JSON DNS patching hack in
IpfsNode.kt (ensureDnsConfigPatched) — freedom-ipfs speaks DoH natively.
Blocked on: solardev-xyz/freedom-ipfs#16 (freedom-ipfs has no Android export yet — iOS xcframework only).
Integration plan (mirrors the ant integration)
1. Vendor the native lib. Build libfreedom_ipfs_mobile.so per ABI in the freedom-ipfs repo (cargo xtask build-android-arm64 / -x86_64, per #16) → copy into swarmnode/src/main/jniLibs/{arm64-v8a,x86_64}/. Vendor freedom_ipfs.h into swarmnode/src/main/cpp/ next to ant.h.
2. JNI shim. New swarmnode/src/main/cpp/freedom_ipfs_jni.c (same pattern as ant_jni.c), added to CMakeLists.txt. Minimum surface:
freedom_ipfs_node_new_with_data_dir / freedom_ipfs_node_free
freedom_ipfs_node_start_gateway_online_with_config_v3 with addr = "127.0.0.1:0" (ephemeral port, same trick as today) — params: delegated router URL(s), routing mode, max concurrent requests, DHT query timeout, DHT max providers, request queue timeout
freedom_ipfs_node_gateway_url (replaces Kubo's gatewayAddr() for IpfsInfo.gatewayUrl)
freedom_ipfs_node_stop_gateway
freedom_ipfs_node_diagnostics (+ retrieval_stats / routing_stats) for the status UI
- lifecycle:
enter_background, enter_foreground, handle_low_memory, handle_network_change, trim_cache / clear_cache
freedom_ipfs_version for logging
- string/buffer frees (
freedom_ipfs_string_free, freedom_ipfs_buffer_free)
The native gateway request API (freedom_ipfs_gateway_request_*) is not needed — we consume the HTTP loopback gateway, as the browser already does with ant and Kubo.
3. Rewrite IpfsNode.kt, keep its public shape. Same state: StateFlow<IpfsInfo> + start()/stop()/dispose() contract so NodeService (maybeStartIpfs/maybeStopIpfs), the AIDL broadcast, and Settings UI stay untouched. Changes inside:
- construct node with
dataDir = filesDir/ipfs-reader (new dir; delete the old Kubo repo at filesDir/ipfs on first successful start — it's just a cache + keys, nothing user-owned)
- delete
ensureDnsConfigPatched and the offline-init dance
- peer count: freedom-ipfs has no
connectedPeerCount. Repoll diagnostics instead and surface cache hits / provider blocks / bitswap blocks (or drop the peers row from the IPFS status card). Touches IpfsInfo.kt / Settings status UI.
4. Settings mapping (NodeSettings.kt, Settings screen):
ipfs_routing_mode (Kubo "autoclient"/"dht") → freedom-ipfs routing-mode enum (auto / delegated-only / light-DHT / offline). Pick new default = auto; migrate stored values.
ipfs_low_power (Kubo lowpower profile) has no direct equivalent → either drop the toggle or map it to conservative config-v3 values (lower max concurrent requests / DHT fanout / shorter timeouts).
- Bonus over Kubo: config changes can apply via
restart_gateway_online_with_config_v3 instead of requiring an off/on cycle.
5. Wire the lifecycle hooks (new capability, do it in this pass): handle_network_change from a ConnectivityManager callback in NodeService, handle_low_memory/trim_cache from onTrimMemory, background/foreground from the service's foreground state.
6. Verify ENS/DNSLink .eth coverage. The Kubo integration patched eth. → https://dns.eth.limo/dns-query for .eth DNSLink. freedom-ipfs defaults to Cloudflare DoH for DNSLink; confirm the in-app EnsResolver path covers all ens:// flows, otherwise we need per-TLD resolver config upstream (would extend #16).
7. Cleanup. Delete swarmnode/libs/mobile.aar + its Gradle dep, drop the gomobile classes from consumer-rules.pro if referenced, update README build recipe (freedom-node-mobile section → freedom-ipfs section).
Testing
Motivation
IPFS/IPNS currently comes from an embedded Kubo node:
mobile.aar(built insolardev-xyz/freedom-node-mobile, gomobile) wrapped byswarmnode/src/main/java/baby/freedom/swarm/IpfsNode.kt. iOS instead embedssolardev-xyz/freedom-ipfs, a read-only Rust IPFS reader with a loopback gateway. Moving Android to freedom-ipfs:mobile.aar, so this removes the freedom-node-mobile dependency entirely,IpfsNode.kt(ensureDnsConfigPatched) — freedom-ipfs speaks DoH natively.Blocked on: solardev-xyz/freedom-ipfs#16 (freedom-ipfs has no Android export yet — iOS xcframework only).
Integration plan (mirrors the ant integration)
1. Vendor the native lib. Build
libfreedom_ipfs_mobile.soper ABI in the freedom-ipfs repo (cargo xtask build-android-arm64/-x86_64, per #16) → copy intoswarmnode/src/main/jniLibs/{arm64-v8a,x86_64}/. Vendorfreedom_ipfs.hintoswarmnode/src/main/cpp/next toant.h.2. JNI shim. New
swarmnode/src/main/cpp/freedom_ipfs_jni.c(same pattern asant_jni.c), added toCMakeLists.txt. Minimum surface:freedom_ipfs_node_new_with_data_dir/freedom_ipfs_node_freefreedom_ipfs_node_start_gateway_online_with_config_v3withaddr = "127.0.0.1:0"(ephemeral port, same trick as today) — params: delegated router URL(s), routing mode, max concurrent requests, DHT query timeout, DHT max providers, request queue timeoutfreedom_ipfs_node_gateway_url(replaces Kubo'sgatewayAddr()forIpfsInfo.gatewayUrl)freedom_ipfs_node_stop_gatewayfreedom_ipfs_node_diagnostics(+retrieval_stats/routing_stats) for the status UIenter_background,enter_foreground,handle_low_memory,handle_network_change,trim_cache/clear_cachefreedom_ipfs_versionfor loggingfreedom_ipfs_string_free,freedom_ipfs_buffer_free)The native gateway request API (
freedom_ipfs_gateway_request_*) is not needed — we consume the HTTP loopback gateway, as the browser already does with ant and Kubo.3. Rewrite
IpfsNode.kt, keep its public shape. Samestate: StateFlow<IpfsInfo>+start()/stop()/dispose()contract soNodeService(maybeStartIpfs/maybeStopIpfs), the AIDL broadcast, and Settings UI stay untouched. Changes inside:dataDir = filesDir/ipfs-reader(new dir; delete the old Kubo repo atfilesDir/ipfson first successful start — it's just a cache + keys, nothing user-owned)ensureDnsConfigPatchedand the offline-init danceconnectedPeerCount. Repolldiagnosticsinstead and surface cache hits / provider blocks / bitswap blocks (or drop the peers row from the IPFS status card). TouchesIpfsInfo.kt/ Settings status UI.4. Settings mapping (
NodeSettings.kt, Settings screen):ipfs_routing_mode(Kubo"autoclient"/"dht") → freedom-ipfs routing-mode enum (auto / delegated-only / light-DHT / offline). Pick new default = auto; migrate stored values.ipfs_low_power(Kubolowpowerprofile) has no direct equivalent → either drop the toggle or map it to conservative config-v3 values (lower max concurrent requests / DHT fanout / shorter timeouts).restart_gateway_online_with_config_v3instead of requiring an off/on cycle.5. Wire the lifecycle hooks (new capability, do it in this pass):
handle_network_changefrom aConnectivityManagercallback inNodeService,handle_low_memory/trim_cachefromonTrimMemory, background/foreground from the service's foreground state.6. Verify ENS/DNSLink
.ethcoverage. The Kubo integration patchedeth.→https://dns.eth.limo/dns-queryfor.ethDNSLink. freedom-ipfs defaults to Cloudflare DoH for DNSLink; confirm the in-appEnsResolverpath covers allens://flows, otherwise we need per-TLD resolver config upstream (would extend #16).7. Cleanup. Delete
swarmnode/libs/mobile.aar+ its Gradle dep, drop the gomobile classes fromconsumer-rules.proif referenced, update README build recipe (freedom-node-mobile section → freedom-ipfs section).Testing
ipfs://<cid>file + directory (index.html fallback, listing) on emulator (x86_64) and arm64 deviceipns://<key>and DNSLink (ipns://en.wikipedia-on-ipfs.org)ens://site that resolves to an IPFS contenthashContent-Range:nodeprocess and restart; airplane-mode → network-change recoveryErrorPage.kt