Build native Android apps locally from plain HTML, CSS, and JavaScript.
You provide: an app name, a description, an icon, and your code. WebShell Forge stamps everything into a vendored copy of s17labs/webshell β a minimal Android WebView wrapper kit β and runs Gradle locally to produce an installable debug APK.
No cloud build services. No accounts. The generated apps are real Android apps with the INTERNET permission already declared, so they can make API calls and load remote content like any other app.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Dashboard (browser) Β· CLI wizard β
β name Β· description Β· icon Β· HTML / CSS / JS β
β + live preview before you build β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β forge-core β
β 1. copies the vendored webshell template β
β 2. stamps applicationId / version into gradle β
β 3. writes app_name (+ escaped strings.xml) β
β 4. icon β adaptive launcher icons (all densities) β
β 5. writes assets/www/ and injects bridge.js β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ./gradlew assembleDebug β dist/*.apk β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Item | Version |
|---|---|
| Node.js | 20+ |
| Java | 17+ (AGP 8.7.3 requirement) |
| Android SDK | platforms;android-35 |
Check your setup anytime:
npm run forge -- doctor # or: forge doctorScaffolding works without Java/SDK β only the final Gradle build needs them.
npm install
# Option A β web dashboard (form + live preview + streamed builds)
npm start # β http://localhost:4321
# Option B β CLI wizard
npm run forgeBoth frontends share the same core: they scaffold a project under projects/, stream the Gradle build log, and drop the finished APK into dist/ ready to adb install or sideload.
forge create
# β answers prompts for name/description/icon
# β points it at ./index.html ./style.css ./app.js
# β optionally builds immediately| File | Change |
|---|---|
app/build.gradle.kts |
applicationId, versionCode, versionName |
app/src/main/res/values/strings.xml |
app_name (quotes/apostrophes escaped safely) |
res/mipmap-* |
replaced with adaptive icons generated from your image |
app/src/main/assets/www/ |
your index.html, style.css, app.js + app.json metadata |
| (untouched) | namespace = com.yourapp and all Kotlin sources β see note below |
Why no source rewriting: webshell's docs explicitly allow namespace β applicationId. The Kotlin package stays com.yourapp while each generated app gets its own unique applicationId, so side-by-side installs just work and we never touch fragile code.
The description is stored in assets/www/app.json (Android has no native "description" slot) and used for the APK filename.
- Full-screen WebView loading
assets/www/index.html - JS β native bridge via
bridge.jsβ auto-injected if missing (NativeBridge.toast(...), vibrate, etc.) INTERNET+VIBRATEpermissions pre-declared by the template- Debug-signed APK β installs on any device (API 26+)
See the upstream docs for the full bridge API: bridge-api Β· gotchas-and-tips
Forge itself is pure Node.js and runs fine on ARM64 phones (Termux / proot environments). Whether the Gradle build works there depends on your JVM:
java -version # if this fails inside proot, try PROOT_NO_SECCOMP=1 java -versionIf Java runs, npm run forge works end-to-end on-device β expect slow first builds (~5β15 min while Gradle + dependencies download; cached afterwards). If your container blocks JIT memory mapping, builds must run on a desktop until we ship a JVM-free pipeline.
The repo's own CI proves the full pipeline on every push: unit tests, then it scaffolds the sample app from the vendored template and runs ./gradlew assembleDebug on GitHub-hosted runners. The resulting APK is uploaded as the forge-sample-debug-apk artifact.
- Download the latest sample APK: Actions β CI β latest run β Artifacts
- Rebuild on demand via the workflow's Run workflow button (
workflow_dispatch)
You can also build your generated app in CI: scaffold locally (npm start or forge create), then use export and push:
Turns any scaffolded project into a ready-to-push repo with its own CI baked in:
forge create # β¦answer prompts; say yes to "Export a CI-ready repo?"
# or later, for an existing scaffolded project:
forge export # point it at ./projects/<id>The exported folder contains the full Android project plus:
.github/workflows/build.ymlβ builds a debug APK on every push (and manual dispatch) and uploads it as an artifactREADME.mdβ your name/description, build instructions, badge placeholder- clean
.gitignore; no build outputs, no forge metadata
Push it anywhere with Git and the APK appears under Actions artifacts:
cd <exported-folder>
git init -b main && git add -A && git commit -m "Initial import from WebShell Forge"
gh repo create my-app --public --source . --pushIn the dashboard, the same thing is the β¬ CI-ready repo (.zip) button after scaffolding.
npm test # core unit tests (no Java needed)
npm start # dashboard
npm run forge # CLILayout:
webshell-forge/
βββ .github/workflows/ci.yml CI: unit tests + E2E sample APK build (artifact upload)
βββ templates/
β βββ .template-version pinned s17labs/webshell SHA + vendored date
β βββ webshell/ full vendored template (the build substrate)
βββ packages/
β βββ core/src/
β β βββ scaffold.js copy template β staging dir
β β βββ stamp.js applicationId/version/app_name rewriting
β β βββ icons.js image β adaptive launcher icons (sharp)
β β βββ www.js assets/www assembly + bridge.js injection
β β βββ build.js gradlew runner + APK collection
β β βββ export.js CI-ready repo generator
β β βββ zip.js dependency-free zip writer/reader
β β βββ doctor.js JDK/SDK environment checks
β β βββ index.js public API (createApp, buildApp, β¦)
β βββ dashboard/ Express server + zero-dependency browser UI
β βββ cli/ @clack/prompts wizard (bin: forge)
βββ scripts/e2e-scaffold.mjs sample app scaffolder (npm run sample)
βββ test/run.js node:test suite (no Java needed)
βββ projects/ dist/ local outputs (gitignored)
git -C templates/webshell pull # or re-copy from a fresh clone
git rev-parse HEAD > new sha # update templates/.template-version
npm test # stamping patterns are drift-checked by testsMIT Β© s17 Labs. The vendored templates/webshell retains its own MIT license.