Repository map
The four Ori repositories, the control-plane / runtime-plane boundary they sit on, and the contracts that bind them together.
The platform is four cooperating services, and each one lives in its own git
repository under the oriserve1 Bitbucket workspace. This page maps every repo to
its service, shows which plane it sits on, and spells out the handful of contracts
that let the four pieces work as one system.
In prose we call the services Console, Backend / Backend API, Voice fleet, and Dialler. Those friendly names are what you use day to day. The git repositories have their own canonical names — vox-frontend, vox-backend, vox-agents, vox-dialler — and those are what you clone, branch, and deploy. The table below is the one place the two namings meet; everywhere else, the friendly names win.
The four repositories
All four live at git@bitbucket.org:oriserve1/<repo>.git.
| Repository | Service | Plane | Language / runtime | Package manager | What it owns |
|---|---|---|---|---|---|
vox-backend | Backend API | Control | Python 3.12 · FastAPI | uv | Auth, bots, campaigns, system settings, per-call runtime config, durable call records, CRM push, API keys, recordings access, fleet selection |
vox-frontend | Console | Operator surface | React 19 · Vite 8 (SPA) | npm | The operator dashboard: bot builder, campaign builder, call logs, reports, settings, knowledge bases |
vox-agents | Voice fleet | Runtime / media | Python 3.12 · FastAPI · Pipecat 1.3.0 | uv | Live call workers, transport adapters, the speech pipeline, recording upload, post-call packaging, result delivery |
vox-dialler | Dialler | Runtime / execution | Python 3.12 · asyncio worker | uv | Campaign leases, predictive pacing, retry scheduling, outbound SIP dialing, answering-machine screening, attaching answered calls to the fleet |
Internal development directories do not match the repo names — you may see a checkout sitting in a folder called something else locally. That mismatch is cosmetic and historical. For anything an operator or DevOps engineer touches — cloning, CI configuration, deploy jobs, runbooks — always refer to the repositories by their Bitbucket names (vox-backend, vox-frontend, vox-agents, vox-dialler). Never script against a local directory name.
Repo → service → plane
Control plane vs runtime plane
The defining rule of the platform is a clean split between the control plane, which owns durable business state, and the runtime plane, which runs the actual calls and holds no long-lived state.
Control plane — the source of truth
vox-backend (Backend API) plus its data layer — MongoDB, Redis, the vector database, and object storage — hold everything durable: bots, campaigns, system settings, and every call record. vox-frontend (Console) is the operator's window onto that truth. If a piece of state must survive a restart, it lives here.
Runtime plane — disposable muscle
vox-agents (Voice fleet) and vox-dialler (Dialler) do the heavy real-time work but keep no durable state. They re-fetch everything they need from the control plane at the start of each call, which is why you can scale them out, restart them, or replace a host freely — and why a crashed fleet worker costs you exactly one call.
This boundary is not just architectural tidiness; it drives the operational model. You scale the runtime plane horizontally (add fleet hosts, add workers) without touching the control plane, and you back up and protect the control plane carefully because it is the only thing that cannot be regenerated.
The contracts between services
Four narrow contracts connect the services. Everything else is internal. If you understand these four, you understand how a call gets configured, run, recorded, and recorded back.
Backend serves per-call runtime config — the fleet fetches it
At the start of every call, the Voice fleet calls the Backend's config endpoint and gets back the complete picture for that bot: prompts, voice settings, tools, and any CRM data for this contact. The fleet stores nothing between calls — this fetch is how a stateless worker becomes "the right bot" for the next 90 seconds.
The endpoint is GET /api/v1/config on the Backend. The fleet (vox-agents) knows where to find it via VOXBRIDGE_CONFIG_URL in its environment (for example http://localhost:8080/api/v1/config when the Backend is co-located).
# vox-agents .env
VOXBRIDGE_CONFIG_URL=http://localhost:8080/api/v1/configThe fleet posts results back to the Backend
When the call ends, the fleet hands the Backend a complete record — transcript, post-call analysis, quality-control findings, a disposition, and the recording reference. The Backend stores it as the durable call record and can push it onward to a CRM. The fleet's job is finished the moment the result is delivered.
The Dialler shares the SAME MongoDB database as the Backend
The Dialler and the Backend point at the same MongoDB database (MONGODB_DB=voxbridge in both). The Dialler reads running campaigns and leases call records directly from that shared database — there is no separate dialler API. The campaign records the Console writes through the Backend are the same records the Dialler paces and dials.
# both vox-backend and vox-dialler .env
MONGODB_DB=voxbridgeExactly one Dialler instance may run against a given database. Two diallers on one MongoDB over-dial, because each one paces as if it is the only dialler in the system. Run the single Dialler instance on the SIP/LiveKit host.
The Dialler attaches answered calls to the fleet via POST /attach
The Dialler places outbound SIP calls through LiveKit and, on answer, hands the live call to a free fleet worker with POST /attach. From that point the worker drives the conversation exactly as it would an inbound call — same pipeline, same config fetch, same post-call path. The Dialler authenticates the attach with a shared secret (VOXCORE_SECRET) and finds workers from its configured fleet list (FLEET_URLS).
The Console (vox-frontend) has only one upstream: the Backend. It talks to nothing else — not the fleet, not the dialler, not the database. The Backend's base URL is baked into the build at build time via VITE_API_URL. Every operator action, every list, every save goes through that one address.
# vox-frontend brand .env
VITE_API_URL=https://api-voice-agent.oriserve.comContract summary
| Contract | From → To | Mechanism | Notes |
|---|---|---|---|
| Runtime config | Voice fleet → Backend | GET /api/v1/config (fleet fetches per call) | Located via VOXBRIDGE_CONFIG_URL |
| Call results | Voice fleet → Backend | Fleet posts the finished record back | Backend stores it and may push to a CRM |
| Shared campaign state | Dialler ↔ Backend | Same MongoDB database (MONGODB_DB=voxbridge) | No dialler API; one dialler per database |
| Attach answered call | Dialler → Voice fleet | POST /attach (authenticated with VOXCORE_SECRET) | Fleet workers found via FLEET_URLS |
| Operator actions | Console → Backend | HTTPS to VITE_API_URL | Console's only upstream |
Where each repo runs
The repository boundaries line up with the deployment topology. A small deployment co-locates roles; a larger one spreads them across hosts.
vox-backend (Backend, uvicorn on :8080) and the built static output of vox-frontend (Console, served by nginx). Small deployments also co-locate MongoDB and Redis here.
vox-agents (Voice fleet) — N single-call worker processes as templated systemd instances, each on its own Unix socket behind nginx, plus local MinIO for recordings. Scale by adding more fleet hosts.
LiveKit and livekit-sip (Docker Compose), plus the single vox-dialler (Dialler) instance. This is the only host that runs the Dialler.
Package managers at a glance
The three Python repos all use uv — never pip. The Console uses npm.
uv sync # install dependencies
uv run uvicorn voxbridge.app:app --host 0.0.0.0 --port 8080 --app-dir srcuv sync # install dependencies
uv run uvicorn voxcore.app:app --host 0.0.0.0 --port 8000 --workers 4 --app-dir srcuv sync # install dependencies
uv run python -m voxdialler.mainnpm install # install dependencies
npm run dev # local dev server (vite)
npm run build # tsc -b && vite build → static dist/Tip
The module paths above (voxbridge.app, voxcore.app, voxdialler.main) are the importable Python package names inside each repo. They are the run targets, not the repo names — clone vox-backend, but launch voxbridge.app.
Where to go next
System architecture
The end-to-end picture: the four services, the data layer, the telephony plane, and the life of a single call.
Run it locally
Prerequisites, cloning the repos, and running the whole stack on your own machine.
Deploy it
The Bitbucket → Jenkins pipeline, host deployment, configuration, and the operations runbook.
System architecture
How the Ori voice platform fits together — the four services, the data layer, the telephony plane, and the journey of a single call from dial to disposition.
Local setup
Get a developer machine ready: install the toolchain, clone the four repos, bring up MongoDB and Redis locally, then do the first-time install for each service.