Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

kablo

Run Claude Code on your desktop from your phone, over a tunnel that costs nothing and a QR code you scan once. No server, no account, no hosting bill — the desktop does the work and the phone is a window onto it.

A turn running on a Galaxy A52

That screenshot is the real app on a real phone: prompt at the top behind a copper rule, the tool call it made, the answer, and what the turn cost.

Run it

Needs Node 23.6+ (the host runs TypeScript directly, no build step) and a working claude CLI — the same login you already use in your terminal.

cd host
npm install
npm start

That prints a QR code, and writes the same code to ~/.kablo/qr.png for terminals that mangle block characters. Scan it with the app and you are connected.

Building the app needs JDK 17+ and an Android SDK with platform 37:

cd android
./gradlew :app:assembleRelease      # app/build/outputs/apk/release/app-release.apk, 4.1 MB

The APK is signed with the debug key on purpose — it is sideloaded onto your own phone, and a real keystore committed to a public repo would be worse than none.

How the phone finds the desktop

The QR holds a token and one or more addresses. The phone dials all of them at once and keeps whichever answers first, so you never have to know where you are standing:

Transport Address When it wins
Cloudflare quick tunnel wss://<random>.trycloudflare.com Anywhere. No account, no card, no DNS. cloudflared is downloaded on first run (~55 MB, into ~/.kablo/bin).
Tailscale ws://100.x.y.z:7420 If you already run Tailscale. Stable address, so the QR never goes stale.
LAN ws://192.168.x.x:7420 Opt-in with --lan. Fastest at home.

The header says which one you got in on — live · lan, live · tunnel. Turning WiFi off mid-session moves you to the tunnel in about twenty seconds without touching anything.

The token is permanent; only the address rotates. Plain ws:// is accepted only for 100.64/10, 10/8, 172.16/12 and 192.168/16 — a QR pointing the phone at ws://some-public-host is refused rather than sending the token in the clear.

The agent keeps running when the phone disconnects. Every frame is numbered, the host keeps the last 2000, and a phone that reconnects asks for everything after the last number it saw. Going into a tunnel on the metro does not cancel a refactor.

Speaking instead of typing

mic next to the composer dictates into the text box and stops there. It never sends. The far end runs shell commands without asking, so a misheard word has to stay editable — dictation appends to whatever you had already typed, and you press send yourself. Uses the phone's own recogniser in the phone's own language.

This gives away full control of your machine

kablo runs Claude Code with bypassPermissions. It will not ask before writing a file, deleting one, or running a shell command. Whoever holds the token has that.

What protects it: a 256-bit token that never crosses the wire in cleartext, every HTTP path answering 404 so a stranger who finds the tunnel URL sees nothing, and a delay that doubles on each wrong token — 250 ms, 500 ms, 1 s, up to 5 s. Deliberately not a lockout: behind a tunnel every connection arrives from 127.0.0.1, so locking an address out would let a stranger lock out your own phone.

What does not protect it: anything, if you photograph the QR and post it. Rotate with npm start -- --rotate, list paired phones with --who.

Flags

--port 7420        listen somewhere else
--model <id>       override the model the agent uses
--scan a,b         directories to look for git repos in
                   (default: ~/projects and ~/Desktop)
--lan              add the LAN address to the QR
--no-tunnel        skip Cloudflare entirely
--rotate           mint a new token; every paired phone must scan again
--who              list phones that have authenticated

What is missing or broken

  • No diffs. A file edit shows as src/agent.ts · 12→18, not as changed lines. You can see that something was written, not what.
  • Text only. Images the model produces, and anything a tool renders visually, do not reach the phone.
  • One phone at a time. A second connection displaces the first.
  • Sleep kills it. If the desktop suspends, the tunnel dies and the run dies with it. Nothing here keeps the machine awake.
  • A quick tunnel URL changes on every launch. Without Tailscale, that means rescanning the QR each time you restart the host. The token survives; only the address is stale.
  • The pairing was lost once, on a phone that had taken six sideloaded upgrades in an hour, and it has not reproduced. The Keystore key that decrypts the stored token can be dropped by the OS; that path now logs to kablo in logcat instead of silently showing the scan screen, but the cause is unconfirmed.
  • macOS downloads nothing. cloudflared is fetched automatically for Windows and Linux only; on macOS run brew install cloudflared first.

What was actually tested

On a Galaxy A52 (Android 14) against Windows 11, Node 24.18, JDK 21:

  • Pairing by camera, then a prompt typed on the phone that created a file on the desktop and ran git status.
  • The same session over the Cloudflare tunnel from mobile data with WiFi switched off — 415 ms to connect, reconnect and transport switch handled without intervention.
  • Wrong tokens, empty tokens, an old protocol number, and a prompt sent before auth: all refused, and the owner's phone still admitted in 4 ms during the attack.
  • Host restarted underneath a live phone: it reconnected and reopened the project by itself.
  • 20 host tests and 16 Android tests, the latter decoding real frames captured off a running host.

Not tested: Tailscale (not installed here), macOS, tablets, Android below 14, and any turn long enough to exercise compaction.

Changing the protocol

host/src/protocol.ts and android/.../Wire.kt describe the same wire format twice and nothing enforces that but tests. After editing either, recapture the fixtures:

cd host && npm start -- --no-tunnel --lan     # leave running
node scripts/capture.mjs && node scripts/capture.mjs --fault
cp frames.json ../android/app/src/test/resources/frames.json
cd ../android && ./gradlew :app:testDebugUnitTest

host/scripts/probe.mjs drives a full turn from the command line without the phone, which is the fastest way to tell whether a problem is in the host or the app.

About

Run Claude Code on your desktop from your phone. QR pairing, free Cloudflare tunnel, no server.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages