A self-contained Android APK that bundles an entire Linux environment, installs the OpenAI Codex CLI, and gives you a full coding agent UI right on your phone. No root. No Termux. One APK.
βββββββββββββββββββββββββββββββββββββββββββββββββ
β βββββββ βββββββ βββββββ βββββββββββ βββ β
β βββββββββββββββββββββββββββββββββββββββββ β
β βββ βββ ββββββ βββββββββ ββββββ β
β βββ βββ ββββββ βββββββββ ββββββ β
β βββββββββββββββββββββββββββββββββββββ βββ β
β βββββββ βββββββ βββββββ βββββββββββ βββ β
β A N D R O I D Β· M O B I L E Β· A P K β
βββββββββββββββββββββββββββββββββββββββββββββββββ
OpenAI shipped Codex CLI β a terminal-based AI coding agent that reads your codebase, writes code, runs commands, and iterates. It's incredible. But it only runs on macOS and Linux.
We put it on Android. Yes, the full native Rust binary. Yes, with a real web UI. Yes, on your phone.
This project packages a complete Linux userland (borrowed from Termux's bootstrap), installs Node.js 24, the Codex CLI v0.104.0 with its native aarch64 binary, wires up a Vue.js web frontend, and serves it all through an Android WebView β in a single APK that installs like any other app.
One APK. Zero dependencies. Full AI coding agent in your pocket. π§
| Use Case | Description | |
|---|---|---|
| π¬ | Chat with Codex | Full conversational coding agent with streaming responses |
| π | Write code on the go | Generate, refactor, and debug code from your phone |
| π§ | Execute commands | Codex runs shell commands in the embedded Linux environment |
| π§ | Reasoning visibility | Watch the model think in real-time with reasoning summaries |
| π | Multi-thread sessions | Multiple parallel conversations, each with its own context |
| π | Full auto-approval | No permission popups β danger-full-access mode by default |
| π | Background execution | Foreground service keeps Codex alive when you switch apps |
| π | OAuth login | Authenticate with your OpenAI account via browser β no API key pasting |
| π | DNS proxy bridge | Native musl binary routes through Node.js CONNECT proxy for DNS/TLS |
| π± | Offline-ready bootstrap | Linux environment extracted from APK β works without internet after setup |
# π¨ Clone the repo
git clone https://github.com/friuns2/codex-app-android.git
cd codex-app-android
# π¦ Install deps & build frontend
npm install && npm run build
# π§ Download Termux bootstrap (one-time, ~50MB)
cd android && bash scripts/download-bootstrap.sh
# π± Bundle, build APK, install, launch
bash scripts/build-server-bundle.sh && ./gradlew assembleDebug \
&& adb install -r app/build/outputs/apk/debug/app-debug.apk \
&& adb shell am start -n com.codex.mobile/.MainActivity
# π You're flying!Four layers. One APK. Zero compromises.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β π± Android APK β
β β
β ββββββββββββββ βββββββββββββββββββββββββββββββββββββββ β
β β π₯οΈ WebView β β π¦ APK Assets β β
β β (Vue.js) β β bootstrap-aarch64.zip β β
β βββββββ¬βββββββ β server-bundle/ (Vue + Express) β β
β β β proxy.js (CONNECT proxy) β β
β β βββββββββββββββββββββββββββββββββββββββ β
β βββββββΌβββββββββββββββββββββββββββββββββββββββββββββββ β
β β π§ CodexServerManager β β
β β Bootstrap β Node.js β Codex CLI β Platform Binary β β
β β Config β Proxy β Auth β Health Check β Server β β
β βββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββΌβββββββββββββββββββββββββββββββββββββββββββββββ β
β β π§ Embedded Linux ($PREFIX) β β
β β β β
β β node (v24) βββ Express server (:18923) β β
β β β β β
β β βββ JSON-RPC over stdio β β
β β βΌ β β
β β codex app-server (native Rust/musl aarch64) β β
β β β β β
β β βββ HTTPS_PROXY βββΆ proxy.js (:18924) β β
β β β β β β
β β β βΌ β β
β β β api.openai.com β β
β β β β β
β β βββ SSE notifications βββΆ WebView β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
User taps Send
β π₯οΈ Vue ThreadComposer β useDesktopState β codexGateway
β π‘ POST /codex-api/rpc { method: "turn/start" }
β π Express bridge β JSON-RPC stdin β codex app-server
β π¦ Native Rust binary β HTTPS via proxy β OpenAI API
β π€ stdout JSON-RPC notifications (streaming)
β πΊ SSE EventSource β live typing in WebView
π€ codex-app-android/
βββ π± android/
β βββ π§ app/build.gradle.kts # targetSdk=28 (W^X bypass)
β βββ π src/main/
β βββ π AndroidManifest.xml # Permissions & service declaration
β βββ π¦ assets/
β β βββ π proxy.js # Node.js CONNECT proxy
β β βββ π server-bundle/ # Pre-built Vue + Express + deps
β βββ β java/com/codex/mobile/
β βββ π§ BootstrapInstaller.kt # Linux environment setup
β βββ π CodexForegroundService.kt # Background persistence
β βββ βοΈ CodexServerManager.kt # Install, auth, proxy, server
β βββ π₯οΈ MainActivity.kt # WebView + setup orchestration
βββ π src/ # codex-web-local (TypeScript + Vue)
β βββ π‘ api/ # RPC client, gateway, SSE
β βββ π§© components/ # Vue components (composer, threads)
β βββ π composables/ # useDesktopState (reactive state)
β βββ π server/ # Express + codex app-server bridge
β βββ π cli/ # CLI entry point
βββ π§ android/scripts/
β βββ π₯ download-bootstrap.sh # Fetch Termux bootstrap
β βββ π¦ build-server-bundle.sh # Bundle frontend into APK
βββ π PROJECT_SPEC.md # Full technical specification
They said you can't run a statically-linked Rust binary on Android without root. We did it anyway.
The APK bundles Termux's bootstrap-aarch64.zip β a minimal Linux userland with sh, apt-get, dpkg-deb, SSL certificates, and core libraries. On first launch, it's extracted to the app's private storage. All hardcoded /data/data/com.termux/ paths are rewritten to our package path.
The Codex CLI ships a 73MB native Rust binary compiled for aarch64-unknown-linux-musl. npm refuses to install it on Android (os: "linux" vs process.platform: "android"), so we download the tarball directly from the npm registry using Node.js and extract it manually.
The musl-linked binary reads /etc/resolv.conf for DNS β which doesn't exist on Android. Our Node.js CONNECT proxy on port 18924 solves this: Node.js uses Android's native Bionic DNS resolver, and the native binary routes all HTTPS through HTTPS_PROXY.
The Express server spawns codex app-server and communicates via newline-delimited JSON-RPC 2.0 over stdin/stdout. Notifications stream back via Server-Sent Events to the Vue frontend, enabling real-time typing, reasoning visibility, and turn progress.
| Permission | Why |
|---|---|
π INTERNET |
API calls to OpenAI |
π FOREGROUND_SERVICE |
Keep server alive in background |
π REQUEST_IGNORE_BATTERY_OPTIMIZATIONS |
Prevent Doze from killing processes |
π WAKE_LOCK |
Maintain CPU during long tasks |
The app runs with targetSdk = 28 to bypass Android 10+'s W^X (Write XOR Execute) SELinux policy β same approach used by Termux on F-Droid.
- π± Android 7.0+ (API 24) β ARM64 device
- π Internet connection β for first-run setup + API calls
- π OpenAI account β authenticated via OAuth browser flow
- πΎ ~500MB storage β for Linux environment + Node.js + Codex binary
| Problem | Solution |
|---|---|
| π« App crashes on launch | Check adb logcat for CodexServerManager errors |
| π "Permission denied" executing binaries | Ensure targetSdk = 28 in build.gradle.kts |
| π "No address associated with hostname" | Check device has internet; proxy may not be running |
| π Login page doesn't open | Ensure a default browser is set on the device |
| π¦ Old UI after APK update | Server bundle re-extracts every launch β force-stop and reopen |
| π App killed in background | Grant battery optimization exemption in Android settings |
π₯ codex exec fails with "not inside trusted directory" |
Uses --skip-git-repo-check flag automatically |
| Layer | Technology | Version |
|---|---|---|
| π€ AI Engine | OpenAI Codex CLI (@openai/codex) |
0.104.0 |
| π¦ Native Binary | Rust (musl, aarch64) | - |
| π’ Runtime | Node.js (via Termux) | 24.13.0 |
| π Frontend | Vue.js 3 + Vite + TailwindCSS | 3.x |
| π Backend | Express.js + JSON-RPC bridge | - |
| π± Android | Kotlin + WebView | 2.1.0 |
| π§ Linux | Termux bootstrap (aarch64) | - |
If you believe an AI coding agent should run in your pocket β not just on a laptop with a terminal β smash that star button. β
This is what happens when you refuse to accept "it's desktop-only."
Built by shoving an entire Linux distro into an APK and refusing to give up π¬
They said "just use SSH to your server." We said "no." π