Skip to content

Commit 89b7b32

Browse files
Initial scaffold: README, ARCHITECTURE, LICENSE, .gitignore
Tracks JSS#366. v1 scope is a Flutter Android wrapper that boots JSS via nodejs-mobile, points a WebView at localhost:4443/, and keeps the pod alive with a foreground-service notification. Feasibility was confirmed by the spike: zero native modules in JSS deps, runtime already proven on Android via Termux, only --git and --terminal shell out via child_process (both optional, off by default in v1). Flutter project bones (pubspec.yaml, lib/, android/) are intentionally absent from this commit — they should come from a real `flutter create` run on a dev box rather than hand-rolled here.
0 parents  commit 89b7b32

4 files changed

Lines changed: 909 additions & 0 deletions

File tree

.gitignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Flutter / Dart
2+
.dart_tool/
3+
.flutter-plugins
4+
.flutter-plugins-dependencies
5+
.packages
6+
.pub-cache/
7+
.pub/
8+
build/
9+
.metadata
10+
11+
# IDE
12+
.idea/
13+
.vscode/
14+
*.iml
15+
*.ipr
16+
*.iws
17+
.DS_Store
18+
19+
# Android
20+
android/.gradle/
21+
android/captures/
22+
android/local.properties
23+
android/app/release/
24+
android/app/debug/
25+
*.keystore
26+
*.jks
27+
android/key.properties
28+
29+
# iOS (kept in case we ever scope iOS in)
30+
ios/Pods/
31+
ios/.symlinks/
32+
ios/Flutter/Flutter.framework
33+
ios/Flutter/Flutter.podspec
34+
35+
# Bundled JSS — copied in by scripts/bundle-jss.sh, not tracked
36+
assets/nodejs-project/
37+
38+
# Node (only present transiently while bundling)
39+
node_modules/
40+
npm-debug.log
41+
yarn-debug.log
42+
yarn-error.log
43+
44+
# Generated
45+
*.lock
46+
.flutter-version

ARCHITECTURE.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Architecture
2+
3+
## Goal
4+
5+
Ship a single-tap Android install that runs a personal Solid pod on the phone. The user opens the app and a JSS instance is running at `http://localhost:4443/` inside the app's process; closing the app stops the pod (or, with the foreground service, keeps it alive in the background).
6+
7+
## Components
8+
9+
### 1. Bundled JSS (the asset)
10+
11+
- Path inside APK: `assets/nodejs-project/`
12+
- Contents: full JSS tree as published to npm — `bin/`, `src/`, `package.json`, `node_modules/`
13+
- Why the full `node_modules/`: it's pure JS, so copying it once at build-time is cheaper than `npm install` on first launch (and avoids needing a network on first run)
14+
- Size estimate: ~1.4 MB unpacked (per `npm publish` output for `javascript-solid-server@0.0.169`) + node_modules ≈ 30–50 MB
15+
16+
### 2. nodejs-mobile runtime
17+
18+
- Provides a patched Node binary cross-compiled for Android (arm64-v8a, armeabi-v7a, x86_64)
19+
- Runs as a dedicated thread inside the Android process
20+
- Restrictions vs upstream Node: no `child_process`, no `cluster`, no `worker_threads` until recent versions, no native addons unless explicitly built for Android
21+
22+
### 3. Flutter plugin (`nodejs-mobile-flutter`)
23+
24+
- Boots the Node runtime, hands it the entry script (`assets/nodejs-project/bin/jss.js`) plus startup args
25+
- Provides a Dart channel for bidirectional message passing (used post-MVP for native UI ↔ JSS commands; v1 doesn't need it)
26+
27+
### 4. Flutter UI
28+
29+
- **v1**: a single `WebView` widget pointed at `http://localhost:4443/`
30+
- Initial route depends on JSS config; default lands on the IDP landing or the `/{pod}/` root
31+
- v2: native screens for sign-in, file browse, sharing — replacing the WebView screen by screen
32+
33+
### 5. Android foreground service
34+
35+
- Standard pattern: when JSS is "running" the app posts a persistent notification ("JSS is running on port 4443")
36+
- Required to avoid the OS killing the Node thread when the app goes to background
37+
- User can stop the pod by tapping the notification's Stop action
38+
39+
## Boot sequence
40+
41+
1. App `main()` → run Flutter app
42+
2. Flutter `initState` → call `NodejsMobile.startNodeProject()` with:
43+
- script: `bin/jss.js`
44+
- args: `['start', '--single-user', '--port', '4443', '--data-root', <android-files-dir>/data]`
45+
3. Node thread starts; JSS imports its modules; Fastify binds to `127.0.0.1:4443`
46+
4. Flutter listens for the "ready" message on the channel (or polls `GET /` until 200) and shows the WebView
47+
48+
## Data layout on device
49+
50+
- `<android-files-dir>/data/` — JSS pod data (passed via `--data-root` and `DATA_ROOT` env)
51+
- `<android-files-dir>/data/.idp/` — IDP credentials, keys
52+
- `<android-files-dir>/.well-known/` — token store, etc.
53+
- `<cache-dir>/` — JSS notification queues, ephemeral state
54+
55+
The pod's filesystem layout is unchanged from desktop JSS — we just point `--data-root` at app-private storage.
56+
57+
## Auth model on a single-user phone pod
58+
59+
- `--single-user` mode: one pod, one WebID, no registration UI
60+
- Default: pod owner is the device user; the IDP login screen still works for cross-app sign-in flows (xlogin, did:nostr signers)
61+
- E2EE composes cleanly: the device-local Nostr key is the pod's WebID and the encryption key (see [JSS#365](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/issues/365))
62+
63+
## Public exposure (optional)
64+
65+
- v1: pod is loopback-only, accessible only inside the app's WebView
66+
- For sharing: `--tunnel` connects to a remote JSS that proxies a public hostname back to the local pod (decentralized ngrok)
67+
- Long-term: a "share via tunnel" toggle in the app settings, with a paired remote JSS service
68+
69+
## What we're not doing
70+
71+
| Choice | Why |
72+
|--------|-----|
73+
| Run Node via Termux instead of nodejs-mobile | Termux requires a separate app + manual setup; defeats "one-click install" |
74+
| Cross-compile native deps for Android | Not needed — JSS already uses pure-JS substitutes everywhere |
75+
| Custom Node fork for additional capabilities | nodejs-mobile is maintained; forking is a long-term burden |
76+
| iOS in v1 | App Store rule 2.5.2 (no executable code download) and embedded-interpreter policy is fuzzy; needs separate research |
77+
| Native Flutter UI in v1 | WebView is faster to ship and reuses JSS's existing IDP / mashlib UIs |
78+
79+
## Open questions
80+
81+
- Does `nodejs-mobile`'s most recent release ship Node ≥ 18? (verify before scaffold, but `engines.node: ">=18.0.0"` is JSS's floor)
82+
- Memory ceiling on cheap Android devices (1 GB RAM)? `oidc-provider` is the heaviest dep; might need `--idp` off in a "lite" mode
83+
- How do we surface the WebID/pod URL outside the app? (Deep link? Share sheet? A "copy URL" button?)
84+
- Does the foreground-service notification need a stop action wired to a graceful Fastify shutdown? (yes — need to expose this via the plugin channel)
85+
86+
## Build & release
87+
88+
- Debug builds: `flutter run` from a dev box with a connected device
89+
- Release: `flutter build apk --release` produces a signed APK; `flutter build appbundle` for Play Store
90+
- CI (later): GitHub Actions matrix with `subosito/flutter-action`, building both `--debug` and `--release` artifacts on each tag

0 commit comments

Comments
 (0)