Skip to content

Repository files navigation

Orvexa

The AI-native contact center OS — open infrastructure for customer conversations that remember, route, and resolve.

Orvexa is a four-plane, event-driven Go core where customers, conversations, AI agents and business workflows form one continuous system — with every carrier, LLM and datastore behind tested, swappable ports.

Go License: MIT Merged PRs Verification gate Security posture Release verdict


Why Orvexa

The contact-center industry still runs on 1990s CRUD: banks of agents with no memory of the customer, per-seat pricing with per-minute carrier lock-in, and bolt-on AI that never touches the actual workflow. Orvexa rebuilds the stack as infrastructure:

  • One conversation, many channels. A phone call is not the customer relationship — it is one interaction inside a continuous conversation. WhatsApp → voice → email on the same conversation keeps one context; identity normalization means a phone number can never fork into two customers (asserted by tests: internal/customers).
  • Four planes, one critical path. Interaction, Intelligence, Execution and Control communicate only through the event bus, defined interfaces or the API — nothing outside telephony → routing → agent may block a live call.
  • Carrier-agnostic by construction. Twilio, WhatsApp Cloud, Africa's Talking, FreeSWITCH, Asterisk and the built-in simulator sit behind two ports (internal/telephony/ports.go, internal/messaging/ports.go) and must pass the same conformance kit to merge. Switching carriers is an env var, not a rewrite.
  • AI with a safety boundary. AI suggestions are metered; every AI→side-effect call passes a deny-by-default tool gateway that allowlists, schema-validates, rate-limits and audits — refusals included.

Architecture

Four planes around a transactional event backbone; three stateless deployables; carriers behind a hexagon:

flowchart TB
    CH["Channels — voice · WhatsApp · SMS · USSD"] --> API
    IDP["OIDC identity provider"] --> API

    API["cmd/api<br/>REST v1 · dual auth · capability RBAC · signed webhook ingress"]
    WK["cmd/worker<br/>outbox dispatcher · audit · facts · search indexer · workflow executor"]
    RT["cmd/realtime<br/>WebSocket fan-out"]

    API --> INT
    INT --> BUS
    BUS --> WK
    BUS --> RT

    subgraph PLANES["Domain planes — bounded contexts"]
        direction LR
        INT["INTERACTION — critical path<br/>telephony · messaging · routing · presence · agents · queues"]
        EXE["EXECUTION<br/>durable workflows · callbacks · notifications"]
        NTL["INTELLIGENCE<br/>AI gateway · tool gateway · analytics facts · search"]
    end

    CTL["CONTROL — everywhere<br/>tenancy · identity · capability RBAC · audit · metering"]
    CTL -. fast checks on every request .-> API

    BUS["EVENT BUS — transactional outbox → at-least-once<br/>inproc default · NATS JetStream driver"]

    INT -. "VoiceProvider / MessagingProvider ports" .-> CAR
    subgraph CAR["Conformance-tested carrier adapters"]
        direction LR
        TW["Twilio"] --- WA["WhatsApp Cloud"] --- ATC["Africa's Talking"] --- FSW["FreeSWITCH"] --- AST["Asterisk"] --- SIM["simulator (default)"]
    end
Loading
Plane Owns Critical path?
Interaction Realtime customer interactions: telephony, messaging, routing, agent assignment Yes — nothing else may block a call
Intelligence AI gateway, tool gateway, transcription, analytics facts, search No — consumes events, fails independently
Execution Durable workflows, callbacks, notifications No — event-driven consumers
Control Tenancy, identity, authorization, metering, audit Yes — but only as fast checks

Core domain model: Customer → Conversation → Interaction → Case → Workflow. Decisions live in the ADRs — start with ADR-0001 (planes), ADR-0004 (outbox), ADR-0005 (hexagon).

Engineering quality — every claim links to evidence

Claim Evidence
32 test packages green under the race detector; build, vet, gofmt, migration and boot gates pass verified per ADR-0003, recorded in qa/QA_REPORT.md; re-run on this PR
Provider conformance kit — every carrier adapter must pass the same lifecycle contract, audited by negative controls internal/comms/conformance/README.md
Transactional outbox: state + events in one SQL transaction, lease-based dispatcher, at-least-once with idempotent consumers, failures parked — never deleted internal/platform/outbox, ADR-0004
Tool gateway: the only path from AI to side-effects — allowlists, schemas, rate windows, audited refusals; AI holds no credentials internal/tools, architecture § AI boundary
Capability RBAC: IdP owns identity, Orvexa owns authorization; role→capability matrix pinned by tests; revocation lands within the TTL; 10-attack JWT forgery matrix docs/rbac.md, internal/identity, ADR-0008
Tenant isolation: tenant resolved only from the authenticated key or verified token — never the wire; cross-tenant ids read as 404; hard isolation on realtime fan-out; server-side tenant filter on search internal/tenancy, internal/realtime, QA §4
Fail-closed webhook ingress: per-provider HMAC (Twilio / WhatsApp Cloud / Africa's Talking / platform), tamper + replay + empty-secret rejection, no unsigned path exists internal/webhooks, QA §2
Rate limiting on all 24 mutation routes with server-side keying and memory-bounded eviction security posture §4, internal/platform/httpx
OpenAPI contract tests: router ⇄ spec bidirectional conformance, uniform {data,meta} / error envelope tests/contract, api/openapi/orvexa-v1.yaml (40 paths)
Security sweep verdict: PUBLIC-READY — secret scan clean (canary-validated), headers complete, govulncheck 0 reachable vulnerabilities docs/security/security-posture.md, scripts/security-scan.sh
Truthful health: /readyz reports component truth (503 when degraded), workers fail fast without storage, degradation is typed (never silent) QA §1, operations runbook

Full-loop integration suites (webhook dedup against provider_events, outbox lease/ack) run against real PostgreSQL via the devstack: make integration.

Quickstart

Verified end to end on a fresh clone (full transcript attached to the README pull request): no cloud accounts, no docker, no sudo — Go 1.26+, bash, curl, jq.

# 1. dev stack: userland PostgreSQL 16 + migrations (idempotent; docker compose also available)
make devstack-up

# 2. run the platform (two terminals)
export ORVEXA_DATABASE_URL='postgres://postgres:postgres@127.0.0.1:55432/orvexa?sslmode=disable'
export ORVEXA_WEBHOOK_HMAC_SECRET='dev-secret-change-me'   # signs/validates every provider webhook
go run ./cmd/api      # REST + webhook gateway on :8080
go run ./cmd/worker   # outbox dispatcher, audit + facts consumers, workflow executor

# 3. bootstrap org + tenant + API key (SQL until the admin API lands — see runbook)
psql "$ORVEXA_DATABASE_URL" <<'SQL'
INSERT INTO organizations (id, name, slug) VALUES (gen_random_uuid(), 'Acme', 'acme');
INSERT INTO tenants (id, organization_id, name) VALUES (gen_random_uuid(), (SELECT id FROM organizations), 'Acme Support');
INSERT INTO api_keys (id, tenant_id, name, key_hash, scopes)
VALUES (gen_random_uuid(), (SELECT id FROM tenants), 'bootstrap',
        encode(sha256('orvx_dev-bootstrap-key'::bytea), 'hex'), '{api}');
SELECT id AS tenant_id FROM tenants;
SQL

# 4. drive the loop through the public contract
export ORVEXA_KEY='orvx_dev-bootstrap-key'
curl -s http://localhost:8080/readyz | jq -c .        # {"status":"ready"}

# a customer with two channel identities — one identity, not two silos
CUST=$(curl -s -X POST -H "X-API-Key: $ORVEXA_KEY" -H 'Content-Type: application/json' \
  -d '{"display_name":"Jane Wanjiku","identifiers":[{"type":"phone","value":"+254 712 345 678"},{"type":"whatsapp","value":"+254712345678","is_primary":true}]}' \
  http://localhost:8080/api/v1/customers | jq -r .data.id)

# place an outbound call: the simulator carrier reports progress via SIGNED webhooks
# through the same public gateway a real carrier uses — the hexagon, live
CALL=$(curl -s -X POST -H "X-API-Key: $ORVEXA_KEY" -H 'Content-Type: application/json' \
  -d "{\"customer_id\":\"$CUST\",\"to\":\"+254712345678\"}" \
  http://localhost:8080/api/v1/calls | jq -r .data.ID)

# prove the ingress is fail-closed and idempotent: sign, replay, tamper
BODY=$(jq -nc --arg i "$CALL" --arg n "qs-$(date +%s)" '{event:"call.connected",interaction_id:$i,nonce:$n,tenant_id:"<tenant_id from step 3>"}')
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac 'dev-secret-change-me' -hex | awk '{print $2}')
curl -s -X POST -H 'Content-Type: application/json' -H "X-Orvexa-Signature: $SIG" -d "$BODY" http://localhost:8080/api/v1/webhooks/simulator | jq -c .   # processed:true
curl -s -X POST -H 'Content-Type: application/json' -H "X-Orvexa-Signature: $SIG" -d "$BODY" http://localhost:8080/api/v1/webhooks/simulator | jq -c .   # duplicate:true — single effect
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/json' -H "X-Orvexa-Signature: deadbeef" -d "$BODY" http://localhost:8080/api/v1/webhooks/simulator   # 401 — tamper rejected

# finish the interaction, open a case, schedule a callback workflow
curl -s -X POST -H "X-API-Key: $ORVEXA_KEY" -H 'Content-Type: application/json' -d '{"action":"hangup"}'   http://localhost:8080/api/v1/calls/$CALL/actions | jq -r .data.Status
curl -s -X POST -H "X-API-Key: $ORVEXA_KEY" -H 'Content-Type: application/json' -d '{"action":"complete"}' http://localhost:8080/api/v1/calls/$CALL/actions | jq -r .data.Status
CASE_ID=$(curl -s -X POST -H "X-API-Key: $ORVEXA_KEY" -H 'Content-Type: application/json' \
  -d "{\"customer_id\":\"$CUST\",\"subject\":\"Billing dispute\"}" \
  http://localhost:8080/api/v1/cases | jq -r .data.id)
curl -s -X POST -H "X-API-Key: $ORVEXA_KEY" "http://localhost:8080/api/v1/cases/$CASE_ID/interactions/$CALL/link" -o /dev/null -w 'linked: %{http_code}\n'
curl -s -X POST -H "X-API-Key: $ORVEXA_KEY" -H 'Content-Type: application/json' \
  -d "{\"customer_id\":\"$CUST\",\"phone\":\"+254712345678\",\"notes\":\"dispute follow-up\"}" \
  http://localhost:8080/api/v1/workflows/callbacks | jq -c '{type:.data.type,status:.data.status}'   # waiting_timer

# stop: make devstack-down   ·   full loop incl. analytics: scripts/e2e-demo.sh (see limitations)

Analytics facts are asynchronous (worker consumes the outbox): give it a beat, then GET /api/v1/analytics/summary. Configuration reference: operations runbook.

Repository map

Path What it is
cmd/ Deployables: api (REST + ingress), worker (dispatcher + consumers), realtime (WebSocket fan-out)
internal/ Domain modules — customers · conversations · interactions · cases · queues · agents · routing · telephony · messaging · comms (+ conformance kit) · webhooks · realtime · ai · tools · workflows · analytics · search · identity · tenancy · httpserver · platform (bus, outbox, httpx, db)
pkg/ Shared kernels: events (closed topic registry), errors, idempotency, pagination, config, logging
migrations/ 12 ordered, forward-only SQL migrations (11 PostgreSQL + 1 ClickHouse engine-specific)
api/openapi/ Orvexa API v1 — 40 paths, envelope + stable machine error codes, contract-tested
docs/ Architecture · domain map · RBAC · runbook · devstack · ADRs · security
qa/ Release-gate report: QA_REPORT.md — the evidence-backed verdict
scripts/ devstack.sh · e2e-demo.sh · security-scan.sh
tests/ contract (OpenAPI ⇄ router) · integration (DB-backed, tag-gated)
Makefile make race · make integration · make devstack-up · make lint-todos

For investors

  • Market. Contact centers are a large, sticky category still sold as seats-and-minutes CRUD. Orvexa is the platform layer underneath: conversations as first-class infrastructure with an event backbone every AI capability can safely plug into. The leverage is agent time — metered AI suggestions, audited tool calls and durable workflows that complete follow-ups without a human holding the thread.
  • Defensible architecture. The moat is the contract set: hexagonal carrier ports + conformance kits make new carriers cheap and safe; the transactional outbox makes every state change auditable and replayable; capability RBAC and tenant isolation are enforced by construction, not by review; OpenAPI contract tests keep the surface stable for integrators. Competitors must rebuild discipline that is already pinned by tests here.
  • Pilot readiness. Quoting qa/QA_REPORT.md: "GO for pilot onboarding (single-tenant pilots, sandbox/demo posture)" — "the full interaction loop … is implemented end to end with zero external dependencies in the default profile, and clean extraction paths to production infrastructure. Known limitations are documented below and tracked as issues; none are silent."
  • Honest limitations (same report, §5): no hosted CI on this account (the local ADR-0003 matrix is the authoritative gate; CI workflow is armed), production carriers/NATS/Redis wired as tested library swap points pending binary glue (roadmap), SQL-first tenant bootstrap, single-region posture. Trust is the product — the ledger is public.

For engineers — six things worth stealing

  1. Hexagonal carriers with a self-auditing conformance kit. Adapters embed a lifecycle contract; broken adapters are proven to fail it (negative controls).
  2. Transactional outbox done properly. One SQL transaction for state + events, SKIP LOCKED leasing, at-least-once delivery, idempotent consumers, a closed topic registry that makes unregistered topics impossible.
  3. A real AI security boundary. The tool gateway is deny-by-default with schema validation, per-agent rate isolation and audited refusals — AI never holds credentials (internal/tools).
  4. Capability RBAC without cookie-soup. RS256-only OIDC verification with kid rotation + amplification floors, static role catalog pinned Go↔SQL, revocation inside the token's cryptographically-valid lifetime (docs/rbac.md).
  5. Durable workflows as data. Deterministic steps, timer rows, immutable step trace, crash-safe resume — with an optional Temporal driver behind a build tag (ADR-0006, ADR-0009).
  6. Truthful operations. /readyz that lies is debt: health reports component truth, workers fail fast when they cannot do their job, degradation is typed (search.not_configured, 429 + Retry-After), and the runbook documents every env var the code reads.

Roadmap

Shipped: foundation → domain core → event backbone → communications → routing → realtime → intelligence → execution → release gate → devstack → carriers → search/Redis/Temporal → OIDC RBAC → security sweep → contract tests (issues #1–#41, all merged via PR — see QA §6).

Next, per umbrella #10: wire the JetStream bus driver and Redis presence/limiter drivers into the binaries (both shipped and integration-tested as library swap points) · LLM adapter wiring behind the AI gateway · real-carrier production runs per-adapter acceptance criteria · admin/onboarding API to replace SQL bootstrap (#56) · multi-region readiness per ADR-0007.

Honest limitations

From qa/QA_REPORT.md §5 and this PR's verification: the default profile runs the in-process bus and simulator carrier (production drivers are swap points, not yet wired into cmd) · no hosted CI — the local matrix is the gate (ADR-0003) · tenant bootstrap is SQL (no admin API yet) · single-region, single-Postgres posture · scripts/e2e-demo.sh needs a payload refresh to match the strict processor vocabulary (#89) and the second outbound call per tenant is blocked by an empty-provider_ref dedupe collision (#90) — both verified while validating the quickstart above, which stays inside the proven path.

License

MIT — © 2026 Orvexa contributors.

About

Orvexa — the AI-native Contact Center OS. Interaction, Intelligence, Execution and Control planes in one event-driven platform: omnichannel conversations (voice, WhatsApp, SMS, email, chat), human + AI agents, routing, workflows, tool gateway and analytics.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages