unify/gateway/)
is a single FastAPI application assembled by create_app() in
app.py
and launched with python -m unify.gateway serve (the CLI in
__main__.py
also provides doctor, urls, setup, smoke, and wizard
subcommands). It runs as a separate process from the
ConversationManager.
Channels vs adapters
The two sub-packages split along an ingress/egress line — but precisely:channels/hosts the outbound and admin APIs: sending messages, placing calls, provisioning numbers, managing watches and installs. Nearly all routes require the admin bearer token (admin_auth_dependencyincommon/auth.py). A few unauthenticated routes live here too where the provider calls back mid-flow (Twilio’s TwiML and conference-status callbacks).adapters/hosts inbound normalization: provider webhooks and internal Console routes, each validated (Twilio signatures, Slack signing secrets, OAuth state), resolved to a target assistant, and reduced to a{thread, event}envelope.
GatewayConnection in
channels/discord/gateway.py,
supervised by bot_manager.py) rather than a webhook — DMs always route,
guild messages only on @mention. And social verification
(channels/social/views.py)
is a synchronous Twilio send that returns the code to the caller — it never
publishes an envelope.
Channel modules
Each channel is a sub-package underchannels/ with a views.py exposing
its router(s):
Adapter modules
The shared inbound pipeline lives in
adapters/common.py:
resolve the assistant (get_assistant), resolve the channel route where
pools are involved (resolve_phone_route, resolve_whatsapp_route — this
is how shared numbers map (pool number, sender) to the right assistant),
activate the runtime, then publish_runtime_event().
Credentials
unify/gateway/credentials/
holds operator infrastructure credentials — Twilio account, Slack
signing secret, OAuth client IDs, LiveKit keys — behind the
CredentialStore protocol (base.py), with EnvCredentialStore (env.py)
as the OSS implementation reading environment variables. The package
docstring draws the line explicitly: this is distinct from
unify.secret_manager, which holds assistant-owned user secrets
(per-assistant OAuth tokens like GOOGLE_ACCESS_TOKEN live in Orchestra
assistant secrets, not here).
Adding a channel: the seams
The gateway is designed so a new channel touches a predictable set of files:- Outbound — a new sub-package under
channels/with an admin-authenticated router; mount it via the prefix table increate_app(). - Inbound — a webhook handler under
adapters/that validates, resolves the assistant, and publishes an envelope with a newthreadkey (register it inKNOWN_THREADSinenvelopes.py). - Runtime — a typed event class in
events.py, an entry inCommsManager.events_map, and an@EventHandler.registerhandler — covered in the conversation runtime. - Send path — a method on
CommsPrimitivesplus its brain-tool wrapper.