Hot reload over Wi-Fi on Android and iOS, with an optional Gossip bot for remote control.
npm run cap:dev:android -- 10.26.239.15:5555npm run cap:dev:ios
# → Opens Xcode. Select your iPhone → Cmd+R
# (Vite starts automatically, hot reload is active)npm run cap:dev:all -- "10.26.239.15:5555"
# → Android deploys via ADB, iOS opens Xcode for manual run- Phone(s) and Mac on the same Wi-Fi network
- Android: ADB Wi-Fi connected (
adb connect <ip>:5555) - iOS: Wi-Fi debugging enabled in Xcode (Window → Devices and Simulators → Connect via network)
-
Install the mkcert root CA on your iPhone:
- AirDrop
~/.vite-plugin-mkcert/rootCA.pemto your phone - Settings → General → VPN & Device Management → install the profile
- Settings → General → About → Certificate Trust Settings → enable full trust
- AirDrop
-
Ensure Xcode has your device set up for Wi-Fi debugging
adb tcpip 5555
adb connect <phone-ip>:5555All scripts follow the same flow:
- Detect local IP address
- Build the SDK (
npm run build:sdk) - Set
DEV_SERVER_URL=https://<local-ip>:5173 - Run
cap sync(writes the dev server URL into the Capacitor config) - Deploy to device (or open Xcode for iOS)
- Start Vite HTTPS dev server (
npx vite --host)
The WebView loads from the Vite server instead of bundled files → any code change triggers hot reload.
The Vite server uses HTTPS (required for crypto.subtle). Self-signed certs need bypass on both platforms:
Android (already in repo):
MainActivity.java:BridgeWebViewClientsubclass that accepts all certs in debugnetwork_security_config.xml: trusts user-installed CAs in debug
iOS (3 layers, belt-and-suspenders):
patches/@capacitor+ios+8.0.0.patch— patchesWebViewDelegationHandler.swiftto accept self-signed certs (persisted viapatch-package, applied onnpm install)ios/App/App/SSLBypassPlugin.swift— Capacitor plugin registered in debug builds onlyios/App/App/Info.plist—WKAppBoundDomainsremoved (blocks localStorage on non-listed domains)
| File | Purpose |
|---|---|
scripts/dev-android.sh |
Android hot reload script |
scripts/dev-ios.sh |
iOS hot reload script |
scripts/dev-all.sh |
Both platforms simultaneously |
capacitor.config.ts |
Reads DEV_SERVER_URL env var |
patches/@capacitor+ios+8.0.0.patch |
SSL bypass patch for Capacitor iOS |
ios/App/App/SSLBypassPlugin.swift |
SSL bypass Capacitor plugin (debug only) |
ios/App/App/MyViewController.swift |
Plugin registration + WebView inspectable |
android/app/src/main/java/.../MainActivity.java |
SSL bypass for Android (debug only) |
This means the WebView can't load from the dev server (SSL rejection).
- Verify the patch is applied: check
node_modules/@capacitor/ios/Capacitor/Capacitor/WebViewDelegationHandler.swiftfor "patched by gossip-app" comment - If not applied:
npx patch-packageornpm install(postinstall hook runs it) - After patching:
npx cap sync ios, then clean build in Xcode (Cmd+Shift+K → Cmd+R) - Nuclear option: delete
~/Library/Developer/Xcode/DerivedDataand rebuild
Never run cap sync without DEV_SERVER_URL set. Always use the dev scripts which set it automatically.
- Ensure
bridge?.webView?.isInspectable = trueis set inMyViewController.swift(under#if DEBUG) - The app must actually load (no black screen) for it to appear
- Safari → Settings → Advanced → Show features for web developers
adb devices # check connection
adb disconnect
adb connect <ip>:5555For remote control — send instructions from your phone via Gossip:
cd bot.local && npm startThen set up a cron in Claude Code:
/loop 1m Check bot.local/inbox.jsonl for unprocessed messages...
Bot config is in bot.local/.env. See memory/dev-setup.md for full details.