Android: run coding agents on the phone (stacked on #609) - #639
Draft
katulevskiy wants to merge 1 commit into
Draft
katulevskiy wants to merge 1 commit into
katulevskiy wants to merge 1 commit into
Conversation
Author
|
HEADS UP, THIS IS HEAVILY VIBECODED |
Adds on-device mode to the Material 3 Expressive app: the phone runs its own `zeron headless` plus the real agent CLIs (Claude Code, Codex, OpenCode, Grok…) in a proot Alpine guest, next to zeronsh#609's Account and Demo modes. Engine side - crates/localedge: single-tenant Rust port of the edge (chat2 rooms, registry room, DeviceRoom relay, nudges, checkpoints) on loopback, so the unmodified engine and zeron-client sync over production protocols with no cloud. `zeron headless` hosts it via ZERON_LOCAL_EDGE_PORT/TOKEN. - ZERON_IPC_TOKEN gates the IPC socket (loopback is shared by every app on a phone); ZERON_DEVICE_PLATFORM; a `ui` cargo feature for an engine-only static musl build. - Client: Credentials::Local; execution hosts recognised by advertised capabilities rather than platform; long host RPCs (installs, clones) get the engine's forward deadlines instead of 30s. Mobile: host_call. Android - :runtime module: bootstrap, foreground RuntimeService, health, logs, exec(), phantom-kill detection, in-place guest upgrades. - App: "Run agents on this phone" on the first-run screen and a mode switch in Settings; On-device engine and Coding agents screens (install, sign in, sign out, for any engine device); new sessions default to an installed harness; clone repositories on the phone; local notifications. - scripts/android: fetch/patch proot (x86_64 rebuilt with a fork→clone patch), fetch Alpine, build the musl engine — run by Gradle when missing. Design and contracts: docs/android.md.
katulevskiy
force-pushed
the
android-on-device
branch
from
September 29, 2026 11:17
e80f73a to
d330c07
Compare
katulevskiy
changed the base branch from
main
to
zeron/android-app-implementation
September 29, 2026 11:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds on-device mode to #609's Android app: the phone runs its own
zeron headlessengine and the real agent CLIs (Claude Code, Codex, OpenCode, Grok, …) inside a small Linux guest on the device. #609's Account (remote control) and Demo modes are unchanged, and on-device mode sits next to them.Tested on a real arm64 phone: Claude Code (Max) and Codex (ChatGPT) signed in and ran sessions entirely on the device, including a Claude session with image attachments.
On a real phone (runtime and engine from this PR, shown in an earlier standalone UI):
In #609's UI (Android 16 emulator):
How it works
execveof app-writable files (targetSdk ≥ 29), and every vendor installer writes executables. proot's loader maps guest programs into memory instead of exec'ing them. The app ships proot and the engine aslib*.soinnativeLibraryDir, the only place apps may exec from. The engine runs inside the guest, so it believes it is on a Linux VPS: zero engine changes for spawning, installing, or discovering harnesses. UserLAnd uses the same approach on Play.zeron-clientonly speaks the edge protocols.crates/localedgeis a single-tenant Rust port ofedge/src(chat2 rooms, registry room, DeviceRoom relay, nudges, checkpoints; SQLite-backed), hosted byzeron headlesson loopback. The unmodified engine and client sync over production protocols with no cloud. It's also a first step toward the self-hosting contract ARCHITECTURE.md defers.?token=, compared in constant time). The IPC socket is gated byZERON_IPC_TOKEN, which is passed explicitly to thezeron mcpserver that agents get.What's in the PR (on top of #609)
Rust
crates/localedge(new): the local edge, with unit, edge-protocol, and e2e tests. The e2e test drives a real in-process engine (mock harness) ⇄ local edge ⇄zeron-client, and covers restart persistence and auth rejection.apps/zeron:uifeature, so--no-default-featuresbuilds an engine-only static musl binaryZERON_LOCAL_EDGE_PORT/TOKENembeddingzeron statusreports the local edgecrates/rpc:ZERON_IPC_TOKENgate on the server; the client presents it, and a rejected handshake gets a clear error.crates/engine:ZERON_DEVICE_PLATFORM; the token is injected into the agent MCP server.crates/proto/crates/client:Credentials::LocalInstallHarness/CloneRepo/FetchAllget the engine's 15-minute forward deadline instead of 30 scrates/mobile: ahost_callpassthrough; a demo host for installs and clones; clippy fixes.Android
:runtime(new module):exec()~/.claude/CLAUDE.md,~/.codex/AGENTS.md, …) and asudoshim;apk addworks unprivileged in the guest:app, in Android app on the shared Rust mobile core (Material 3 Expressive) #609's design (Material 3 Expressive, Zeron icons):useLegacyPackaging = true, so the runtime executables are extracted tonativeLibraryDirlibtalloc)fetch-proot.sh,fetch-rootfs.shandbuild-engine.shwhen their outputs are missingscripts/android: fetch and patch proot (the x86_64 build is rebuilt with a fork→clone patch, because Android's x86_64 seccomp policy rejects musl's fork), fetch Alpine, build the musl engine.Full design and contracts:
docs/android.md.Testing
cargo check --workspace --all-targetscargo testfor localedge (16), client (43), rpc (30), proto (47), mobile (20)/home/zeron/projectshello.py/fib.pyin the guestKnown gaps
system_servercrashes whenever a Custom Tab opens; seedocs/android.md)libproot-loader32.soare still open.Try it
Toolchain: rustup targets
{aarch64,x86_64}-linux-androidand{aarch64,x86_64}-unknown-linux-musl, cargo-ndk, cargo-zigbuild + zig, Android SDK 37 + NDK 29, JDK 21.