Full phone control for Claude Code.
Flowhook gives any authorized caller — your CLI, a Claude Code session, a shell script — full adb shell-level control over an Android phone over the open internet. Install APKs, run shell commands, simulate taps, grab screenshots, read logcat. No USB cable. No Wi-Fi. Works over cell data from anywhere.
Live site & APK download: https://flowhook.dustforge.com
A small FastAPI server + an Android companion app. The phone dials out to your server and keeps a WebSocket open. The server exposes a REST API. When you (or an agent) hit the API, the command travels down the socket, executes on the phone via Shizuku's shell-user Binder IPC, and the result comes back. Everything a wired adb shell can do, Flowhook can do — from any network.
Because letting your agent drive your phone while you're on a run is a reasonable thing to want, and USB isn't going to reach you.
flowhook/
├── app/ # Android companion app (Kotlin)
├── server/ # FastAPI backend (server.py + requirements.txt)
├── cli/ # Python CLI (flowhook)
├── build.gradle.kts # Top-level Gradle config
├── settings.gradle.kts
└── README.md (you are here)
- Install Shizuku on the phone. Pair it via Wireless Debugging. Confirm "Shizuku is running."
- Download the APK: https://flowhook.dustforge.com/app.apk
- Register and enroll at the server:
export FLOW=https://flowhook.dustforge.com USER_TOKEN=$(curl -s -X POST $FLOW/auth/register \ -H 'Content-Type: application/json' \ -d '{"username":"you","password":"pw"}' \ | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])") curl -s -X POST $FLOW/devices/enroll \ -H "Authorization: Bearer $USER_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"name":"my phone"}' # → returns {device_id, agent_token, enroll_token}
- Open Flowhook on the phone. Paste server URL
wss://flowhook.dustforge.com/agentand theagent_tokenfrom step 3. Save. Grant Shizuku permission. Hit all three "KEEP ALIVE" buttons. - Run a command:
curl -s -X POST $FLOW/exec \ -H "Authorization: Bearer $USER_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"cmd":"id; getprop ro.product.model"}'
cd server
python3 -m venv venv
venv/bin/pip install -r requirements.txt
FLOWHOOK_JWT_SECRET=$(openssl rand -hex 32) venv/bin/python server.py
# Listens on :8700. Put nginx in front for TLS.See server/server.py top comment for endpoints.
cp cli/flowhook ~/bin/flowhook
chmod +x ~/bin/flowhook
mkdir -p ~/.flowhook
echo '<your user_token>' > ~/.flowhook/user_token
flowhook health
flowhook devices
flowhook exec 'uptime'
flowhook install https://example.com/yourapp.apk
flowhook screencap phone.png
flowhook logcat MyTagRequires Android SDK 35 + Gradle 8.7+ + JDK 17+.
./gradlew assembleDebug
# → app/build/outputs/apk/debug/app-debug.apkSee the landing page for the full API reference.
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/auth/register |
— | create user |
POST |
/auth/login |
— | reissue user token |
POST |
/devices/enroll |
user | create device + agent token |
GET |
/devices |
user | list enrolled devices |
WS |
/agent |
(first frame) | phone dials in here |
POST |
/exec |
user | run shell command on phone |
POST |
/install |
user | silent APK install via Shizuku |
POST |
/uninstall |
user | pm uninstall |
POST |
/tap | /text | /key |
user | synthetic input |
GET |
/logcat |
user | recent logcat |
GET |
/screencap |
user | base64 PNG |
GET |
/audit |
user | command history |
GET |
/health |
— | server + online-device count |
- Server issues a JWT on registration.
- User enrolls a phone → server mints a long-lived agent token bound to that phone.
- Phone opens a WebSocket to
/agent, sends the agent token as the first frame. - Server holds the socket. Outbound-only from phone — no inbound ports, carrier NAT irrelevant.
- Caller hits
/exec. Server marshals command → phone → Shizuku → uid 2000 (shell) exec → response. - Every command logged to audit.
- Shizuku dies on reboot. Re-pair via Wireless Debugging (any Wi-Fi, including a temporary hotspot). 30-second ritual. If your phone rarely reboots, this is a non-issue.
- Shell user, not root. Writing system partitions or modifying other apps' private data still requires root. Flowhook won't help with that.
- OEM battery aggression. Samsung (OneUI) and some Chinese OEMs will kill background services even with Android's standard exemptions. Flowhook's app prompts for all three: battery-optimization ignore, Samsung "Never sleeping apps," and App-info → Unrestricted. Grant all three.
- Transport is
wss://(WebSocket over TLS) — same encryption as HTTPS, same Let's Encrypt cert. - Tokens are bearer JWTs. Treat them like SSH keys. Rotate via uninstall/re-enroll or by changing
FLOWHOOK_JWT_SECRET(invalidates all). - Shizuku privileges =
shelluser. Not root. - Dual-use. Don't install Flowhook on a phone you don't own. Don't hand tokens to anyone you wouldn't hand your unlocked phone to.
Sightless — voice-driven remote control for your agent army. Separate product, separate APK, no Shizuku dependency (commercial subscription).
MIT. See LICENSE.
Built the morning of 2026-04-14 from blank slate to running-over-cell-data in one session.