1. What runs where
Five core domains behind one front door. The gateway is the only world-facing process: it resolvesx-api-key fail-closed, enforces per-route scopes, and proxies the REST surface plus a
/ws multiplex. Everything else binds loopback or the internal network.
- core/meetings — joins the call, captures audio, emits a speaker-attributed
transcript.v1. - core/gateway — the edge; the one authenticated boundary.
- core/runtime — the kernel that spawns and supervises isolated workloads (one browser container per meeting, one agent container per dispatch).
- core/identity — authN/authZ and accounts, default-deny.
- core/agent — the knowledge layer: transcripts into a git Markdown workspace via sandboxed agents. Self-hosted only. The hosted service runs meetings and transcription; it does not run the agent plane.
2. Where the audio goes
This is usually the first question, so here is the whole path with nothing elided:
At rest: Postgres holds users, tokens, meetings, transcripts. S3/MinIO holds recordings, agent
workspaces, and authenticated-bot browser profiles. Redis holds the streams and the bot command bus.
The egress answer, precisely
The repository models its own egress as machine-checkable architecture. Inarchitecture.calm.json every
node sits inside the tenant boundary except the transcription service, and exactly one edge
crosses out — bot → transcription, carrying audio. A CI gate (pnpm gate:dataflow, gate:calm)
fails the build if that changes. The rendered view is
docs/views/egress.mmd.
So the honest statement is conditional, and the condition is yours to set:
- Point
TRANSCRIPTION_SERVICE_URLat your own STT — the bundled GPU unit underdeploy/transcription, or any OpenAI-compatible endpoint — and audio never leaves your perimeter. This is the no-egress posture Security & compliance describes. - Leave it unset and follow the quickstart and you are using hosted STT: the variable ships
empty in
deploy/compose/.env.example, and the quickstart hands you a hosted token to get going. That is a fine way to evaluate and the wrong way to run a sovereignty requirement. Self-hosting the control plane does not by itself make the deployment zero-egress.
3. What running it costs
Everything in this section is the cost of running it yourself. The
hosted service removes all of it — one authenticated
POST /bots, nothing to
install, nothing to operate. Self-host when the audio must not leave your perimeter, or when you
want the agent plane, which the hosted service does not run.
Every count in that table is a command, not a claim. On a clean clone, before you install anything:
deploy/transcription, which ships a CPU variant too).
Helm’s shipped resource requests, which is the number a capacity plan wants:
So 1.55 vCPU and 4 GiB requested, and a burst ceiling of 13 vCPU / 13.75 GiB, before a single
meeting runs. Point the chart at an external Postgres and object store and you schedule the
application tier only — 1.15 vCPU / 3 GiB. Meetings and agent dispatches are ephemeral Pods on top,
and are not in either figure.
Configuration surface.
deploy/compose/.env.example declares 77 variables. make all seeds
.env from it and the stack comes up on the defaults for all but two credential blocks you supply
yourself: transcription (TRANSCRIPTION_SERVICE_URL + _TOKEN — without it bots join and record but
produce no transcript) and one model credential for the agent plane (any one of five accepted names —
without it agent chat refuses). make all warns loudly about both rather than failing quietly.
Is it working? make probe runs a full-journey smoke in nine stages — S0 ready, spawn,
schedule, boot, join, transcribe, live-view, stop, S8 log sweep — each printing
Expected / Actual / Verdict. It runs against all three surfaces (make probe,
make probe SURFACE=lite, make probe SURFACE=helm). Every long-running service answers
GET /health; compose gates bring-up on service_healthy rather than sleeping.
Upgrades are a tag bump and a re-run — IMAGE_TAG for Lite and compose,
--set global.imageTag=… for Helm. Schema migrations converge in-process on service start; there
is no separate migration step.
What operability does not include yet
Say this part out loud, because an architecture review will find it anyway:- No backup, restore, or DR procedure is documented. Postgres and S3/MinIO are yours to back up by your own standard. Nothing in the tree does it for you.
- No automated upgrade path — no operator, no version-check service. Upgrades are the manual step above.
- No published hardware minimum for Lite or Kubernetes. The one hardware figure in the docs (8 vCPU / 16 GB) is stated for the full stack, and the README and quickstart disagree about whether it applies to running published images or only to building from source. Size from the table above, not from that line.
What we do not measure
Everything above is an inventory of what there is to run, taken from the tree. That is a smaller claim than it may look: it does not tell you how many hours you will spend running it. We have not run the study that would, so we publish no operator-hours figure, no total cost of ownership, and no setup-time comparison against any other product. Operability is the row this field most often argues with a percentage. Three such figures are published today. We went looking for the source of each and did not find one:
We are not answering any of them with a number of our own. The question worth asking any vendor —
this one included — is whether they will tell you how much before you commit, and in a form you can
check without asking them. Ours is the three commands above, the requests table, and the list of
what we do not ship.
4. How it fails
Failure reasons are not prose. They are a sealed contract —lifecycle.v1,
one of the 16 frozen in contracts.seal.json, with a sha256 that CI refuses to let drift, and
golden fixtures beside it.
BotStatus: joining · awaiting_admission · active · needs_help · completed · failed.
CompletionReason — the schema’s own word for it is operator-facing: stopped · left_alone · startup_alone · evicted · awaiting_admission_timeout · awaiting_admission_rejected · join_failure · auth_session_missing · validation_error · max_bot_time_exceeded.
FailureStage: requested · joining · awaiting_admission · active — which is what separates a
broken node from a refused join. Events also carry exit_code, a bot_logs ring buffer, an
error_details object, and a cgroup resource snapshot.
The schema is deliberately liberal (additionalProperties: true), so producers can add detail
(infra_fault: control_plane_unreachable, for one) without a reseal.
The enum is fully public and machine-readable in the tree, and it is now enumerated end to end in
these docs: Completion reasons carries all ten values, whose
side each points at, and the four timeouts you control.
Troubleshooting is the symptom-shaped companion.
5. Security posture — and what is not claimed
What is real and checkable:- One authenticated front door. Services bind loopback; the gateway resolves identity server-side, fail-closed, with per-route scopes and default-deny ownership checks.
- A pre-auth edge guard on by default on compose and Helm — per-IP throttle (600 req/min),
auto-ban, allow/deny lists — with an explicit kill switch (
GUARD_ENABLED,gateway.guard.enabled). - Agents are untrusted by design: ephemeral isolated container, no egress except brokered tools, propose-only on untrusted input, irreversible effects gated.
- Procurement artifacts resolve, and they are files, not assertions:
architecture.calm.json(FINOS CALM),SECURITY.md,security-insights.yml(OpenSSF),license-exceptions.jsonandTHIRD_PARTY_LICENSES.md, and the CI gates atscripts/gates.mjs.
- No third-party certification. No SOC 2, no ISO 27001, no HIPAA BAA, no published penetration test, no external auditor. The posture here is self-attestation plus machine-checkable artifacts you can run yourself. If your process requires a certificate, this does not have one.
- Encryption at rest is planned, not shipped — for workspaces, transcripts, and stored tokens. Today you bring your own disk or volume encryption.
- Mid-call bot control endpoints are not wired in the open-core stack.
6. What you own, and how you leave
- Apache-2.0, verbatim, no addenda, no dual licence, no enterprise edition, no feature gates. Nothing in the tree is withheld from the licence.
- No CLA and no copyright assignment. Contributions come in under Apache-2.0 with a DCO sign-off; an individual CLA is not required. No assignment means no unilateral relicensing lever over what you have already received.
- Fork rights are the exit plan. There is no licence restriction on reselling or embedding it in a service you sell.
- The data leaves with you. Transcripts and derived knowledge are Markdown in a git repository you hold; recordings are objects in your bucket; everything else is rows in your Postgres. Leaving does not orphan the data.
7. Known gaps, in one place
Capability-by-capability status, kept against the tree:
Status. Where Vexa sits against the alternatives:
Comparison.