Repository navigation
Aster Droid 0.1.0
Aster Droid 0.1.0
Aster runs on an Android phone. The full agent, on the device, driven from Telegram.
Experimental. arm64-v8a only. The fast path is four steps and no compiler: install the APK, create a bot, paste two values, send a message. Everything else here is detail.
1. Install the APK
Download asterdroid-0.1.0.apk from the assets below (or the Releases page) and open it on an Android 8+ phone. Android asks once to allow installs from your browser; allow it.
Open asterdroid and grant what it asks for: accessibility, notification access, Do Not Disturb. The agent needs the accessibility service to read and control the screen, so confirm it is on.
Prefer adb? Connect the phone and run:
adb install -r asterdroid-0.1.0.apkBuilding from source instead is covered in the appendix.
2. Connect your bot
The phone runs aster remote telegram as a child of the foreground service. That bridge is the whole interface: you never touch the phone, you message it.
- Create a bot with @BotFather: send
/newbot, follow the prompts, copy the token. - Get an API key from any provider. Groq has a free tier; a local server needs no key at all.
- In asterdroid, tap the settings icon in the top right. Paste the token into Telegram token, pick the provider, paste its key. Save. Saving writes the agent's
.envand restarts the agent. - Press start and wait for the notification to say Connected to Telegram.
- Send
helloto your own bot in Telegram, not to BotFather. With no allowed users configured it replies with your numeric user id. - Paste that id into Allowed ids and save. The bot now ignores every account outside the list.
Then send a real instruction:
Open settings and turn on Do Not Disturb.The agent reads the screen, performs the action and replies with the result.
From your computer instead. Push a .env rather than typing in the settings sheet:
cat > .env <<'EOF'
ASTER_TELEGRAM_TOKEN=YOUR_BOT_TOKEN
<PROVIDER>_API_KEY=YOUR_PROVIDER_KEY
ASTER_REMOTE_USERS=YOUR_TELEGRAM_USER_ID
EOF
adb push .env /sdcard/Android/data/dev.aster.probe/files/.envThe external files dir is the one place adb can write without run-as. Install.env moves the file inside and deletes the outside copy. Pushed values merge with what the phone already has; keys absent from the pushed file survive. If you do not know your user id yet, steps 4 and 5 above get it.
3. Providers, models, keys
The provider catalog is the same providers.json the CLI and desktop read, synced from the Aster repo at build time. The settings sheet lists it, and model lists come from the selected provider's /models endpoint, so the phone offers what the terminal offers.
Pick provider, model and effort in the app's pickers, or in chat with /model and /effort. Either way the agent restarts to apply it.
Keys are plain environment variables named in the catalog's key_env field for whichever provider you selected. The README's Providers table links every provider's key page. Fast picks:
- Groq (
GROQ_API_KEY, free tier) - Google Gemini (
GEMINI_API_KEY) - OpenAI (
OPENAI_API_KEY) - Anthropic (
ANTHROPIC_API_KEY) - OpenRouter (
OPEN_ROUTER_API_KEY, one key for hundreds of models)
A local OpenAI-compatible endpoint also works, with no key: point ASTER_BASE_URL at Ollama, LM Studio, vLLM or a llama.cpp server.
4. Modes and commands
The agent starts in yolo mode because a phone has no terminal for approval prompts. It announces outbound actions rather than gating them. Change it per chat with /mode.
/help lists the chat commands: /new, /stop, /retry, /queue, /mirror, /mode, /model, /effort, /status, /sessions, /resume, /memory, /remember, /skills, /learn, /checkins, /cron. /mirror returns a browser URL that shows the phone and accepts input.
5. Push to production
release.sh builds a signed release into dist/. It rebuilds both native binaries, runs the Gradle release task, verifies the signature with apksigner and prints a sha256.
./release.sh apk # signed release APK
./release.sh aab # signed release App Bundle
./release.sh apk -v 0.2.0 -c 2 # set the version, then build
./release.sh apk -s -i # reuse jniLibs, install over adbSigning reads keystore.properties and the key it names, both outside git. ./release.sh check reports the SDK, NDK, build-tools, Aster checkout and signing setup without building anything. Install the artifact the same way as the debug build:
adb install -r dist/asterdroid-<version>.apkFrom CI
.github/workflows/release.yml runs the same release.sh on GitHub's runners. It checks out Aster beside this repository, installs the NDK, platform 35 and build-tools, cross-compiles both native binaries, then builds and uploads the signed artifacts. A tag push publishes a GitHub Release, using docs/release-notes/vX.Y.Z.md as the body when that file exists.
git tag v0.1.0
git push origin v0.1.0Set these repository secrets first:
| secret | what it is |
|---|---|
ANDROID_KEYSTORE_BASE64 |
base64 -i aster-release.jks |
ANDROID_KEYSTORE_PASSWORD |
storePassword |
ANDROID_KEY_ALIAS |
keyAlias |
ANDROID_KEY_PASSWORD |
keyPassword |
The keystore is written on the runner and removed with it, so the key never leaves the secrets store. The native build is cached on target/android-tls and target/android-client, which is what keeps a run under the timeout after the first one.
What shipped
- The agent, not a client.
build-agent.shcross-compilesaster-cliforaarch64-linux-androidand packages it aslibaster.so. The process on the phone has the same tools, skills, prompts and review path as the terminal version. Sessions, memory and skills live on the device. - Hands and eyes. An accessibility service reads and drives the screen: numbered element maps, swipes, drags, pinches, held touches, typing through
ACTION_SET_TEXTor a windowless IME, with OCR, screenshot grids and colour-region targeting where the tree is blind. - Receipts. Every action returns a dispatch receipt and the observed screen change. An empty diff returns
warning: nothing on screen changed; treat as not done. - Screen mirror.
/mirrorreturns a browser URL that shows the phone and accepts input. MediaProjection feeds a hardware encoder directly; the wire format follows scrcpy and the Rust server converts to AVCC for WebCodecs.
Known limits
arm64-v8aonly.- Broad permissions: accessibility, notification access, Do Not Disturb, battery exemption.
- Starts in
yolomode, since a phone has no terminal for approval prompts. Change it per chat with/mode. typecannot reach a view that handles input without opening an input connection, such as some terminals. Read the field back to verify.- A reboot means opening the app once before the agent is reachable again; boot start is declared but not wired yet.
Appendix: build from source
What you need
- An Android 8+ phone or emulator (
minSdk 26) connected throughadb. - An Aster checkout beside this repository, or
ASTER_REPOpointing to one. ANDROID_HOMEwith build-tools and platform 35, an NDK, andkotlinc.- A Rust toolchain that can build
stdfor a custom target, withrust-srcinstalled (rustup component add rust-src).
Build and install
./build-agent.sh # the agent, into app/src/main/jniLibs/arm64-v8a/libaster.so
./build-client.sh # asterctl and the mirror, as libclient.so
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
./grant.shThe first build is the slow one: it builds std from source and a static libffi for the embedded Python. Both land in jniLibs because nativeLibraryDir is the one place Android executes from.
grant.sh grants runtime permissions, app-op access, notification access, Do Not Disturb access and battery exemptions, enables the accessibility service and selects the keyboard, then reports any denied grants. Check that the accessibility service is on. The agent needs it to read and control the screen.