Skip to content
Inferect
DocsArchitecture

System Architecture

The data plane, the control plane, and everything a request touches in between.

9 min read

Three surfaces, one database

Inferect Labs isn't three separate products glued together — it's one Postgres database shared by three cooperating surfaces, each with a different job and a different auth model.

LayerJobAuth
Data planeChat completions, model discovery, routing explainAPI key
Control planeOrgs, members, keys, credentials, providers, policies, dashboards, billingSession or API key, via the BFF
Web appThe operator dashboard — Next.js App RouterBrowser session

How the backend is organized

The Go backend is split into domain packages with hexagonal boundaries — nothing reaches into another package's internals. Grouped by concern:

GroupPackagesRole
Data planegateway, dispatch, routing, cache, classification, ingestionRequest handling, provider abstraction, routing, caching
Discovery / catalogdiscovery, catalogModel sync and the per-tenant model catalogue
Tenancy / identitytenancy, account, organization, user, auth, apikey, authz, role, permissionMulti-tenant foundation, auth, RBAC
Credentialscredential, secretsBYOK envelope encryption
Billingbilling, entitlement, quota, usage, ratelimitPlans, subscriptions, entitlements, metering, limits
Control / experimentscontrol, experimentDashboard aggregation, shadow mode, recommendations
Cross-cuttingconfig, lifecycle, logging, metrics, observability, health, requestid, versionRuntime foundation

What happens on a request

1

Authenticate

The API key is validated and resolved to an organization.

2

Classify

The request is classified — model family, capability needs — to feed the router.

3

Route

The engine scores candidates and produces a full ranking, not just a single pick.

4

Check cache

Exact match, then semantic. A hit returns immediately, no provider call made.

5

Dispatch

The top-ranked provider is called using your BYOK credential.

6

Stream or return

The response is returned in the OpenAI-compatible shape.

7

Fail over if needed

A retryable, pre-commit failure walks to the next-ranked candidate. A committed stream never fails over — that would corrupt what the client is already reading.

8

Record usage

Latency, tokens, retries, and outcome are written for the dashboard and billing.

Note

A per-provider circuit breaker tracks health and pulls unhealthy providers out of the ranking until they recover — so a struggling provider doesn't keep eating retries.

Multi-tenancy is a database property, not a code convention

Every tenant table carries a Postgres Row-Level Security policy, and the application connects as a non-owner role. A query bug in application code can't leak another organization's rows — the database refuses it at the policy level, not because someone remembered to add a WHERE org_id = ? clause.

Deployment topology

One Go binary, one managed Postgres with pgvector enabled. No Kubernetes cluster, no message queue, no fleet of microservices to keep in sync. A multi-stage Docker build produces the service binary, a migration runner, and a small utility that provisions the RLS application role; the entrypoint runs migrations to completion, then execs the service.

How architectural change works here

Architecture decisions are recorded as ADRs — dated, binding documents covering the stack, provider abstraction, credential handling, observability, auth, routing, caching, shadow mode, and billing. v1.0 is frozen: a change to it requires a new ADR, not a quiet patch.