Skip to content

CloudBridge URL unification — single resolver for chat and submit

CloudBridge URL unification — single resolver for chat and submit

Status: Delivered CAS: CAS-2537 Delivered: 2026-05-12 PRs: #651 (B1 — shared resolver), #652 (B2 — wire both paths)

What’s new

Chat and submit now resolve the CloudBridge URL through a single async cloudbridge_base_url() function. The previous two-code-path design — chat reading from the AI_CLOUDBRIDGE_URL env var, submit reading from iOS Keychain cloudbridge.worker_url — could silently diverge when the device paired before a backend architecture change.

How it works

src-tauri/src/services/ai/cloudbridge.rs
async fn cloudbridge_base_url() -> Result<String> {
// 1. Env override (dev / CI)
if let Ok(url) = std::env::var("AI_CLOUDBRIDGE_BASE_URL") { return Ok(url); }
// 2. Keychain (written at pair time, canonical in prod)
keychain::read("cloudbridge.worker_url")
}

Both start_stream (chat) and cloud_bridge_report_issue (submit) call this resolver. The Keychain read is now authoritative in production; the env var is the escape hatch for dev and CI runs that don’t pair.

The pair_with_pin command retains a hardcoded fallback (https://ai-bridge.casaconomy.com) because it bootstraps the Keychain entry — it cannot read from it before writing it.

What changed

  • src-tauri/src/services/ai/cloudbridge.rs — new cloudbridge_base_url() async fn; CloudBridgeService::new() is now async; the inline worker_url field removed.
  • src-tauri/src/services/ai/manager.rsAIManager::new() is now async to await the base-URL resolution.
  • src-tauri/src/commands/ai_commands.rs — inline Keychain read in cloud_bridge_report_issue removed; routes through shared resolver.
  • src-tauri/src/main.rs — async init for the CloudBridge service path.

Why we built it

On 2026-05-12 the regent’s iPhone (0.2.2 build 15) could chat (AI bridge worked) but submit failed with “Mac bridge is offline”. The daemon was healthy — curl confirmed 200 from the public hostname. The failure was that the Keychain stored a stale URL from before the CAS-2196 architecture rewire. Chat ignored Keychain and used the env var; submit read Keychain and got the stale value.

Root cause: two independent URL sources of truth for the same daemon. This is the two-source-of-truth rule (Rule 5 in CAS-2550’s quality skills extension): if two functions resolve the same logical value, they must share an implementation.