Body:
Summary
When the bundled vkt-client triggers manual captcha mode, it exposes a captcha proxy at http://localhost:8765. On macOS / Windows / Android the user can open this
URL in any browser and solve the captcha. On iOS this is impossible — Safari can reach localhost but the captcha session token from the NE process is not
shareable, and the user has no way to know a captcha is needed in the first place.
As a result, every TurnBridge connection attempt currently fails silently with the same pattern:
[APP] User requested connect with profile "..."
[APP] VPN status: Connecting
[APP] (12 seconds pass)
[APP] VPN status: Disconnecting
[APP] VPN status: Disconnected
…and PacketTunnel logs show:
[TP] Captcha required, opening proxy on localhost:8765
[TP] (no UI to surface this to user)
[TP] DTLS connection timeout!
[TP] FATAL: 0 connected streams
This has become a hard blocker since VK rolled out the new slider/sound captcha format — the auto-solver fails on every modern attempt, so manual captcha is the only
path forward. iOS is the only platform without a way to access it.
Proposed solution
Add an in-app captcha UI:
- When
PacketTunnelProvider detects captcha-needed state, write a flag to the App Group shared container (or send a Darwin notification).
- Main app observes the flag and:
- Shows a banner: "Captcha required — tap to solve"
- Optionally fires a local notification if app is backgrounded
- On tap, present a modal sheet with
WKWebView pointing at http://localhost:8765.
- WKWebView renders the captcha page exactly as Safari would on macOS.
- User solves the captcha → vkt-client receives the token → NE proceeds with TURN handshake → tunnel comes up.
Captcha needs to be re-solved roughly every 9 minutes (matches the VK credentials TTL of ~8m52s seen in [VK Auth] Using cached credentials (cache=0, expires in 8m52.852161667s)), so the same UX flow can re-trigger periodically.
Why localhost:8765 should work in WKWebView
NE and the main app run in separate processes but share the same localhost. WKWebView in the main app can reach the captcha proxy listening inside the NE without any
extra plumbing — no need for a custom URL scheme handler.
Implementation hints
- App Group is already required for any shared state; if not configured, add one (
group.com.nullcstring.turnbridge or similar) in entitlements for both targets.
- IPC:
UserDefaults(suiteName:) polling, or CFNotificationCenterGetDarwinNotifyCenter for instant signaling.
- The captcha proxy URL contains a session token (
?session_token=…) that NE should write to the shared container so the main app can construct the full URL.
Related upstream issue
The auto-captcha logic in cacggghp/vk-turn-proxy is currently broken because VK switched from checkbox to slider/sound captcha types — see
https://github.com/cacggghp/vk-turn-proxy issues. Even when that's fixed, manual captcha will likely remain a fallback path, so iOS UI for it is valuable
independently.
Environment
- TurnBridge build from latest
main
- iOS 26.4.1 (iPhone)
- Server: vk-turn-proxy v1.8.3 on Debian 13
- VK call link is valid (verified — call opens in browser, captcha appears on auth)
Happy to test patches and provide more logs. Thanks for the project!
Body:
Summary
When the bundled vkt-client triggers manual captcha mode, it exposes a captcha proxy at
http://localhost:8765. On macOS / Windows / Android the user can open thisURL in any browser and solve the captcha. On iOS this is impossible — Safari can reach
localhostbut the captcha session token from the NE process is notshareable, and the user has no way to know a captcha is needed in the first place.
As a result, every TurnBridge connection attempt currently fails silently with the same pattern:
[APP] User requested connect with profile "..."
[APP] VPN status: Connecting
[APP] (12 seconds pass)
[APP] VPN status: Disconnecting
[APP] VPN status: Disconnected
…and
PacketTunnellogs show:[TP] Captcha required, opening proxy on localhost:8765
[TP] (no UI to surface this to user)
[TP] DTLS connection timeout!
[TP] FATAL: 0 connected streams
This has become a hard blocker since VK rolled out the new slider/sound captcha format — the auto-solver fails on every modern attempt, so manual captcha is the only
path forward. iOS is the only platform without a way to access it.
Proposed solution
Add an in-app captcha UI:
PacketTunnelProviderdetects captcha-needed state, write a flag to the App Group shared container (or send a Darwin notification).WKWebViewpointing athttp://localhost:8765.Captcha needs to be re-solved roughly every 9 minutes (matches the VK credentials TTL of
~8m52sseen in[VK Auth] Using cached credentials (cache=0, expires in 8m52.852161667s)), so the same UX flow can re-trigger periodically.Why localhost:8765 should work in WKWebView
NE and the main app run in separate processes but share the same
localhost. WKWebView in the main app can reach the captcha proxy listening inside the NE without anyextra plumbing — no need for a custom URL scheme handler.
Implementation hints
group.com.nullcstring.turnbridgeor similar) in entitlements for both targets.UserDefaults(suiteName:)polling, orCFNotificationCenterGetDarwinNotifyCenterfor instant signaling.?session_token=…) that NE should write to the shared container so the main app can construct the full URL.Related upstream issue
The auto-captcha logic in cacggghp/vk-turn-proxy is currently broken because VK switched from
checkboxtoslider/soundcaptcha types — seehttps://github.com/cacggghp/vk-turn-proxy issues. Even when that's fixed, manual captcha will likely remain a fallback path, so iOS UI for it is valuable
independently.
Environment
mainHappy to test patches and provide more logs. Thanks for the project!