Skip to content

Where the brain runs

An agent’s brain is one setting: deployment.brain_url, a single WebSocket URL. When a call starts, the voice runtime dials {brain_url}?session_id={session_id} — one connection per session, opened just-in-time and torn down when the call ends.

Neither the runtime nor the control plane interprets where that URL points. Your brain code is identical regardless. The only choice is who dials whom.

Inbound serverCortex relay
Who dialsThe runtime dials into your routeYour brain dials out to Cortex
You exposeOne authenticated wss:// routeNothing inbound
Best forAny backend that can accept connectionsServerless, laptops, egress-only / air-gapped networks
ScalingYour own load balancerHand out a different Cortex URL
StatusPrimary — build toward thisFallback

Both paths run the same per-session engine and the same Vql* wire. A brain written for one runs on the other unchanged.

Default to inbound. If you already run a web or mobile backend, exposing one more authenticated WebSocket route is trivial, and it keeps the runtime dialing you directly with no relay in the path. See Inbound server.

Reach for Cortex only when your brain genuinely can’t accept inbound connections — a serverless function, a process on a laptop behind NAT, or a network that only allows egress. Your brain dials out to Cortex, which splices the two legs on a scope it derives from the credential each leg presents — never from the URL, so nothing an attacker can type decides who gets your sessions. See Cortex relay.

Point an agent at your brain with the MCP server:

update_agent(tenant="acme", agent_id="06a2…", brain_url="wss://brain.example.com")

There is no set_brain_url tool — brain_url is a field on the agent, set with create_agent up front or update_agent later.

Rules:

  • It must be wss:// (plain ws:// is allowed only for localhost).
  • Give the URL of your route, path and all — the runtime uses it verbatim and appends only ?session_id=. For Cortex, it’s the Cortex origin exactly as create_agent_credentials returned it.
  • Changing brain_url never touches the agent’s STT/TTS config.

An empty brain_url falls back to a hosted welcome brain, so a freshly created agent still greets while you build the real one.

However the connection is made, the runtime presents the same short-lived RS256 JWT: iss=pygato, aud="brain", sub=session_id, plus tenant_id / agent_id. Both SDKs verify it against Voqalize’s public key for you. Details in The wire.