Skip to content

Mobile AI Bridge

Saga the Watchful — Seneschal

By the end of this chapter you’ll understand:

  • How a one-time pairing flow binds an iOS device to the local macOS AI daemon using a short-lived PIN
  • How every subsequent request is authenticated and forwarded through the Cloudflare Worker tunnel
  • Why the versioned command envelope makes the bridge extensible without breaking existing clients

The core idea

The macOS AI daemon runs claude locally — there is no cloud AI subscription billed per request, and no transcript leaves the household. The problem is that the iPhone cannot reach a process running on a LAN Mac directly. The solution is a Cloudflare Worker sitting at ai-bridge.casaconomy.com that acts as a secure relay. The daemon opens an outbound tunnel to Cloudflare (via cloudflared). When iOS sends a request to the Worker, the Worker authenticates it and forwards it down the tunnel.

macOS

Cloudflare Worker

ai-bridge.casaconomy.com

iOS App

HTTPS POST /v1/chat

Authorization: Bearer

tunnel

streaming response

stream

response

Bug report sheet

Keychain

bearer token

Token auth

Forward

cloudflared tunnel

AI daemon

claude CLI

The Worker is stateless — it verifies the bearer token and proxies. It never stores prompt content or response text.

Pairing

Before iOS can send a request, it needs a bearer token. The pairing flow issues one.

Cloudflare WorkeriOS AppmacOS DaemonCloudflare WorkeriOS AppmacOS Daemongenerate token + PIN (5-minute TTL)POST /v1/pair/confirm {"pin":"NNNNNN"}forward via tunnelverify PIN, issue bearer token{"token": "...", "server_public_key": "...", "worker_url": "..."}same responsestore token + server_public_key in Keychain

The PIN is a six-digit code the regent reads from the Mac and types into the iPhone. It expires in five minutes. A successful pairing stores two items in iOS Keychain: the bearer token (used for authentication) and the daemon’s X25519 public key (reserved for future end-to-end encryption). Re-pairing clears and replaces both items.

Command envelope

All bridge calls use a versioned command envelope. The version field lets the daemon evolve its command set without breaking older iOS clients.

{
"command": "ai_chat",
"version": 1,
"payload": {
"conversation_id": "uuid-or-client-id",
"message": "user prompt"
}
}

If the daemon does not recognise a command, it returns HTTP 400 with a typed error that names the unsupported command and lists what it does support. iOS shows an informative banner and does not crash or spin.

Dispatcher pattern

The daemon handles commands through a match-based dispatcher. Adding a new command means adding one match arm and one handler — existing commands are untouched and their contracts do not change. This keeps the bridge extensible: a new capability (for example, an image-analysis command) is one CAS, not an architectural change.

Security model

SurfaceControl
Bearer tokenIssued at pairing; stored in iOS Keychain; verified by Worker on every request
cloudflared tunnelOutbound-only from Mac; Cloudflare terminates TLS at the edge
Prompt contentNever logged by the Worker or stored on Cloudflare
Server public keyPinned in Keychain at pairing; reserved for future E2E encryption layer
Re-pairClears and replaces Keychain items; old token is invalidated

The Worker and daemon logs must not include prompt body or secret tokens. This is verified during incident review when wrangler tail is used for diagnostics.

Worked example

  1. Regent opens the bug-report sheet on iPhone.
  2. The sheet calls POST https://ai-bridge.casaconomy.com/v1/chat with Authorization: Bearer <token> and a body of {"command":"ai_chat","version":1,"payload":{"conversation_id":"...","message":"the bug description"}}.
  3. The Cloudflare Worker validates the bearer token. On success, it forwards the request body down the cloudflared tunnel to the Mac daemon.
  4. The Mac daemon dispatches on "ai_chat", invokes the claude CLI with the prompt, and streams the response back through the tunnel.
  5. The Worker streams the response back to iOS.
  6. The bug-report sheet displays the streamed response in the chat UI.
  7. If the Mac is offline, the Worker receives a connection error from the tunnel and returns HTTP 503 {"code":"bridge_offline"}. iOS shows an offline banner immediately — no spinner hang.

iPhone log capture quick path

For incident reports from TestFlight/iPhone, collect diagnostics in this order:

  1. About path (copy only): SettingsAboutCopy diagnostic logs.
  2. Bug-report path (submit): open bug-report sheet and submit; the same recent diagnostic log lines are now attached automatically in the GitHub issue diagnostics comments.
  3. Escalation artifact: paste the copied logs into the CAS if the app cannot submit the report itself.

Recap

  • The bridge is a Cloudflare Worker that authenticates bearer tokens and proxies requests through a cloudflared tunnel — the Mac never opens a public port.
  • Pairing issues a bearer token via a six-digit PIN with a five-minute TTL; re-pairing invalidates the old token and replaces both Keychain items.
  • The versioned command envelope (command + version + payload) lets the daemon add new capabilities without breaking existing iOS clients.

What changed {#what-changed}

This chapter was introduced in CAS-3637 Phase 3 (The Casaconomy Book) as the canonical reference for the Mobile AI Bridge.

See: CHANGELOG → 2026-05-18