GitHub Docs

VibeMobile is the Flutter companion for VibeCody — a full phone/tablet/desktop client that pairs with a running VibeCLI or VibeCoder instance, streams AI sessions in real time, and hands the session back and forth between devices.

As of v0.5.5 VibeMobile gained URL-only pairing, zero-config mDNS / Tailscale / ngrok discovery, Apple-Handoff-style continuity with desktop and watch clients, and Google-Docs-style full-content sync (no more 80/512-char truncation).

Platforms: iOS, Android, macOS, Linux, Windows, Web — one Flutter codebase.


What’s new in 0.5.19

Area Improvement
Daemon Document RAG and Office-engine changes are daemon-side; nothing the app calls changed shape
Downloads Release links go through vibecody.online/dl/

Added in 0.5.15:

Area Improvement
Daemon tool_choice support and encrypted-store config are daemon-side; nothing the app calls changed shape
Downloads Every platform artifact is verified on R2 before the release is advertised

Added in 0.5.14: Flutter pinned to 3.47.1 (iOS deployment floor 15.0); the daemon gained GET/PUT /voice/settings, which the app does not yet call.

Added in 0.5.10: no mobile-facing change — that release was desktop security panels and settings.

Added in 0.5.9: every daemon route the app calls covered by a test that fails when the route stops existing, agent runs bounded from outside their own loops, and credential files redacted before the model sees them.

Added in 0.5.8: mic in the chat and watch-chat composers (on-device recogniser first, record + upload fallback), a read-only SkillForge catalogue and training-job status, and compileSdk raised to 37 (see the note under Platform requirements).

Added in 0.5.6: the + New Goal flow (AppBar action and empty-state button, machine picker when ≥ 2 are paired, 409-aware “already exists” handling) plus getGoalTree / getCurrentGoal / pinGoal / unpinGoal on ApiClient.

Added in 0.5.5: URL-only / URL + Bearer pairing (works on emulators), P-256 ECDSA device keys replacing Ed25519, mDNS / Tailscale Funnel / ngrok discovery raced in parallel, the Handoff banner, and Google-Docs-style full-content sync.


Platform requirements

Target Minimum
iOS 15.0+ (raised from 13.0 by the Flutter 3.47.1 pin; 13.0 dated to v0.5.5, itself up from 12.0)
Android 7.0 / API 24+
macOS 12.0+
Linux / Windows GTK 3 / Edge WebView2
Build toolchain Flutter 3.47.1 (CI-pinned, floor ≥ 3.8.0), Xcode 26 (App Store submissions after 2026-04-28)

Android build SDK — compileSdk = 37 from v0.5.8. permission_handler_android, pulled in by the voice-input microphone permission, declares an AAR metadata minimum of API 37, and :app:checkReleaseAarMetadata fails the release build against anything lower. compileSdk only selects which APIs are available at compile time — minSdk (API 24) and targetSdk are unchanged, so the supported device range is the same. VibeCodyWear is already on 37 for a comparable reason (see Wear OS).


Install

From release artifacts (fastest)

See the Releases page for the current build. For v0.5.15:

Platform Artifact
iOS VibeCody-Mobile-v0.5.15-ios.ipa (unsigned — sideload via AltStore / Sideloadly)
Android APK VibeCody-Mobile-v0.5.15-android.apk
Android AAB VibeCody-Mobile-v0.5.15-android.aab

From source

# Prerequisites: Flutter ≥ 3.2.0, Xcode (iOS/macOS), Android Studio + SDK (Android)
cd vibemobile
flutter pub get
flutter run            # launches on the currently-selected device

# Release builds
make -C vibemobile ios-ipa         # signed .ipa (needs APPLE_TEAM_ID)
flutter build apk --release        # Android APK
flutter build appbundle --release  # Android AAB for Play Store
flutter build macos --release      # macOS .app
flutter build linux --release      # Linux bundle
flutter build windows --release    # Windows .exe
flutter build web --release        # static web bundle

Pair with VibeCody in 30 seconds

Pairing produces a short-lived challenge, the phone signs it with its Keystore/Secure-Enclave P-256 key, and the daemon returns a JWT.

Path A — QR code (desktop nearby)

  1. Desktop: start the daemon.
    vibecli --serve --port 7879
    
  2. Desktop: in the REPL or VibeCoder Governance → Watch Devices, click Pair Device.
  3. Phone: open VibeMobile → Pair → scan the QR code. Done.

Path B — URL only (new in 0.5.5, works on emulators)

  1. Desktop: run /pair --url-only in VibeCLI (or click Show URL in VibeCoder).
  2. Phone: VibeMobile → Pair → Paste URL → confirm.

The URL encodes host:port + a one-time bearer token. No clipboard JSON is needed.

Path C — URL + Bearer (manual, air-gapped)

  1. Desktop: /pair --show-bearer prints two fields.
  2. Phone: VibeMobile → Pair → Manual → paste URL and bearer.

All three paths produce the same JWT. Sessions authenticate with Authorization: Bearer … afterwards.


Home tabs

Tab Purpose
Machines All paired desktops/laptops. Health, CPU/memory, active provider/model, session count
Sessions Live agent + chat sessions across all machines; tap to stream
Sandbox Auto-focused when a paired watch or desktop opens a sandbox session
Chat Direct remote chat with any provider configured on the paired host
Settings Pairing, notifications, mDNS toggle, Tailscale / ngrok preferences

The Handoff banner appears at the top of any tab when another paired device is active in the same session — tap to pull the session onto the phone.


Connectivity paths

VibeMobile races all reachable paths on every request and picks the fastest one:

  1. mDNS LAN — discovers _vibecli._tcp.local. on any IP range; no setup.
  2. Tailscale — if the phone is on your tailnet, the 100.x address is used; if the host has Funnel enabled, a public HTTPS URL is shared.
  3. ngrok — auto-detected if already running on the host; opt-in auto-start requires an auth token in VibeCLI settings.
  4. Manual host:port — always available as a fallback under Settings → Manual Connection.

See the full Connectivity guide for firewall / NAT / corporate-network notes.


Chat & sessions

  • Streaming. Every message streams token-by-token over Server-Sent Events. Reconnects automatically on flaky networks.
  • No truncation. The 0.5.5 sync model reconciles by message ID and keeps the full transcript — even long code blocks survive the round trip.
  • Markdown. Fenced code blocks render with syntax highlighting; copy button per block.
  • Voice. Tap the microphone on the chat input to dictate. The on-device recogniser runs first (partial text as you speak, nothing leaves the phone); if it is unavailable the clip is recorded and uploaded to the daemon’s POST /voice/transcribe, which prefers a locally downloaded whisper model and falls back to Groq. Needs microphone permission on both platforms.
  • Provider picker. Switch providers or models mid-conversation from the session header.

Handoff continuity

When you’re actively using a session on another paired device (desktop, watch, or another phone), VibeMobile surfaces a Handoff chip at the top of the current screen:

↻ ravi's iMac — Session #1287 · "refactor axum routes"
   [ Pull session ]  [ Mirror ]  [ Dismiss ]
  • Pull — transfers stream ownership. The other device goes read-only.
  • Mirror — keeps both devices writing; the Google-Docs-style sync reconciles edits.
  • Dismiss — ignores, but the chip reappears if the other device is still active.

Secure storage

Credentials never touch plaintext on disk:

Platform Backing store
iOS Keychain Services (kSecClassGenericPassword, access group per-app)
Android EncryptedSharedPreferences, key material in Android Keystore / StrongBox where available
macOS Keychain
Linux Secret Service (libsecret)
Windows DPAPI via flutter_secure_storage
Web IndexedDB + WebCrypto; clears on cache wipe

Notifications

Trigger Default How to change
Agent task complete On Settings → Notifications
Security alert (policy violation) On —
Machine goes offline On —
New message from another device (Handoff) Off Toggle on if you want push-style Handoff

iOS notifications require granting permission on first launch. Android 13+ requires POST_NOTIFICATIONS runtime permission.


Troubleshooting

Pairing QR code won’t scan

  • Dim your display, then try again — QR codes need contrast.
  • Switch to Path B (URL only) — paste the URL instead.
  • Check the phone can reach host:port (try a browser).

mDNS discovery doesn’t find the host

  • Some corporate networks block multicast. Fall back to manual host:port.
  • Verify vibecli --serve is running and bound to 0.0.0.0 (not 127.0.0.1).

“Session desynced” banner

This is the reconciliation catch-up signaling a missed event. Tap Reload — the full transcript is fetched and merged.

Wear OS / watchOS continuity not working

The watch must be paired to the same host as the phone. Check Governance → Watch Devices on the desktop.


API endpoints

All calls require Authorization: Bearer <jwt> after pairing. See VibeCLI Server Mode for the full reference.

Endpoint Method Purpose
/health GET Liveness
/status GET Active provider/model
/pair/challenge POST Issue pairing nonce
/pair/confirm POST Submit signed attestation, receive JWT
/mobile/beacon POST Heartbeat + presence for Handoff routing
/sessions GET List sessions across all machines
/sessions/{id}/stream GET (SSE) Stream messages
/sessions/{id}/reply POST Append a user reply
/chat POST One-shot chat