Skip to main content

Solution architecture

Yabanin is a customer-operated AI gateway. Compatible applications and agents send model requests to it; the gateway applies configured controls, selects an upstream, forwards the request and records operational evidence. The customer's development tools remain responsible for editing code, running tests and accepting changes.

This document describes the implementation inspected on 24 September 2026. Diagrams are logical views, not a claim that every feature is enabled in every installation. Optional components require configuration and their dependencies. Source paths below are relative to the repository root. Mermaid blocks are editable diagram sources and render in Markdown viewers with Mermaid support.

1. System context and ownership​

flowchart LR
Developer["Developer / application"] --> Agent["Compatible agent or SDK"]
Operator["Platform operator"] --> UI["Dashboard / operator CLI"]

subgraph Customer["Customer-operated environment"]
Gateway["Yabanin gateway"]
Admin["Admin API"]
Stores[("PostgreSQL and Redis")]
Local["Optional local inference"]
Tools["Optional internal tool servers"]
Gateway --> Local
Gateway --> Stores
Admin --> Stores
Gateway -. "Configured MCP invocation" .-> Tools
end

Agent -->|"Model requests"| Gateway
UI --> Admin
Gateway -->|"Approved external route"| Cloud["External model provider"]
Gateway -. "Configured MCP invocation" .-> ExternalTools["External tool server"]
Agent --> Checks["Customer sandbox, tests and code review"]

The gateway is an intermediary for traffic sent through its configured endpoints. It is not an agent runtime, an operating-system sandbox or a replacement for code review. An agent's direct network calls bypass this boundary unless the deployment separately restricts them. Self-hosted gateway and local inference are distinct: an external model route sends request data outside the customer environment.

2. Runtime components​

flowchart TB
Client["Agent / SDK"] --> GW["Gateway :8082"]
Dashboard["React dashboard / yb CLI"] --> Admin["Admin API :8086"]
Bridge["Optional MCP partner bridge"] --> GW
Bridge --> Admin

subgraph Data["Request processing"]
GW --> Resolve["Model resolver and routing"]
Resolve --> Adapter["Provider adapter / upstream client"]
GW --> Cache["Optional cache service"]
Resolve -. "Optional complexity classification" .-> Classifier["Classifier"]
Cache -. "Semantic lookup" .-> Embedder["Embedding endpoint"]
end

Adapter --> Inference["Local or external inference"]
Cache --> Redis[("Redis: exact cache and auth state")]
Cache --> PG[("PostgreSQL: configuration, logs, lineage, pgvector")]
Resolve --> PG
GW -->|"Async request events"| Stream[("Redis Stream: events:queue")]
Stream --> Logger["Loggerd :8085"]
Logger -->|"Request logs and accounting"| PG
GW -. "Optional async lineage writer" .-> PG
Admin --> PG
Admin --> Redis

Redis cache and Redis Stream are shown separately by responsibility; they can use the same Redis deployment. The gateway also reads identity, budget and policy data; the diagram groups those reads with configuration rather than drawing every query.

ComponentResponsibilityImplementation entry points
GatewayHTTP middleware, authentication, chat API, routing, streaming, optional MCP invoke APIservices/gateway/cmd/main.go, pkg/gateway/gateway.go
Request pipelineValidation, cache branch, model selection, upstream execution and eventspkg/gateway/chat.go, pkg/gateway/stream.go
AdminConfiguration and identity management, analytics, lineage verification, dashboard backendservices/admin/handlers/router.go, pkg/adminstore
LoggerdRedis consumer groups, request-log persistence, accounting, pending-message recovery and dead-letter handlingservices/run/loggerd.go, services/loggerd/consumer/stream.go
MCP partner bridgeExposes Yabanin tools to MCP clients; calls gateway/admin APIsservices/run/mcp.go, pkg/mcppartner
PostgreSQLDurable configuration and operational records; optional semantic-cache vectorsmigrations, pkg/migrate/migrations
RedisCache, identity-resolution cache, event stream and other configured runtime statepkg/cache, pkg/auth, pkg/eventpublisher
Classifier / embedderOptional routing classification and semantic-cache embeddingspkg/classifier, pkg/embeddings, deploy/docker-compose.yml

The gateway's separate operational listener (default :9090) is not the Admin control-plane service. In the development Compose stack, Admin container port :8086 is published as host port :9091. The MCP bridge supports stdio and an optional HTTP-SSE listener on :8787; it is not required for ordinary chat calls.

3. Chat request path​

The principal API is POST /v1/chat/completions, with JSON and SSE responses. The ordering below follows chatCompletions; disabled or unwired features are skipped. Rejection branches terminate the request before the depicted next stage.

flowchart TD
In["POST /v1/chat/completions"] --> HTTP["Body limit, timeout, rate limit, request ID"]
HTTP --> Auth["Authentication and resolved identity"]
Auth --> Admission["Optional credit guard and regional failover middleware"]
Admission --> Parse["Parse JSON and run configured input guards"]
Parse --> Access["Resolve requested model; check model access and budgets"]
Access --> Validate["Validate request; optional vertical transforms"]
Validate --> Cache{"Eligible cache hit?"}
Cache -->|"Yes"| Replay["Return cached JSON or replay as SSE"]
Cache -->|"No / bypass / lookup error"| Tier["Optional classifier and tier selection"]
Tier --> Rules["Organization routing-rule overrides"]
Rules --> Time["Optional time-window or batch downgrade"]
Time --> Residency["Configured residency enforcement / model remap"]
Residency --> Execute["Provider adapter, circuit breaker, retry and fallback"]
Execute --> Result["Return JSON or stream SSE"]
Result -. "Eligible successful response" .-> Write["Cache write"]
Replay -.-> Evidence["Request event and optional signed lineage"]
Result -.-> Evidence

Identity and admission​

Authentication removes client-supplied organization, key, team and agent identity headers before populating them from the resolved principal. Static-key and database-resolver behavior depends on the configured auth mode; a static key is not a substitute for an organization/agent identity. See pkg/gateway/auth.go.

When wired, model-access and budget checks can reject a request; datastore errors in these checks do not authorize spending. Team/agent budget checks and later accounting are separate operations, so this diagram does not imply an atomic reservation of every concurrent request's eventual cost.

Cache and routing order​

Cache lookup precedes classifier/rule routing and the later residency-enforcement block in the current handler. A hit returns early. Do not interpret the diagram as evidence that every post-cache policy is re-evaluated on cached responses. Cache scope, invalidation and policy changes must be assessed together.

Cache-Control: no-store bypasses the gateway cache for the request. An eligible lookup tries Redis exact matching, optionally PostgreSQL exact matching, and then semantic matching when enabled by organization policy. Semantic matching uses an embedding endpoint and pgvector. It is not proof that a reused answer is suitable for every task. Details: cache architecture.

On a miss, classification may select a tier, organization rules may override the selection, and configured time-based routing may change it again before residency enforcement. These are distinct decisions. Routing-rule read failure currently logs an error and continues without those rules; it is not a universal fail-closed policy layer. The model that ultimately responds can also differ after fallback.

Streaming and retries​

sequenceDiagram
participant C as Client
participant G as Gateway
participant A as Selected upstream
participant B as Configured fallback
C->>G: Chat request with stream=true
G->>G: Admission, cache miss, routing
G->>A: Open stream through provider adapter
alt Connection setup fails
G->>A: Retry when policy permits
opt Selected upstream remains unavailable
G->>B: Try configured fallback
B-->>G: Accepted stream
end
else Selected upstream accepts
A-->>G: Accepted stream
end
Note over G,B: Successful setup commits to one upstream stream
G-->>C: Translated SSE chunks from winning upstream
Note over C,G: A mid-stream failure cannot be transparently replayed
G->>G: Record outcome and observed usage

This sequence depicts a successful setup through either route. If all setup attempts fail, the gateway returns an error. Streaming retry/fallback applies only to connection setup: after accepting the upstream response, it drains that stream without silently restarting it. Provider-specific raw-body streaming disables cross-provider fallback. See pkg/gateway/stream.go and provider integration.

4. Events, storage and evidence​

flowchart LR
GW["Gateway request outcome"] --> Pub["Bounded async event publisher"]
Pub --> Stream[("Redis Stream")]
Stream --> Consumer["Loggerd consumer group"]
Consumer --> DB[("PostgreSQL request_logs and accounting")]
Consumer -->|"Successful processing"| Ack["Acknowledge stream entry"]
Consumer -->|"Processing failure"| Pending["Pending entry / reclaim"]
Pending --> Consumer
Pending -->|"Poison-message handling"| DLQ[("Dead-letter stream")]
GW -. "When lineage enabled" .-> Sign["Sign attestation with organization key"]
Sign --> Writer["Async lineage writer"]
Writer --> Ledger[("PostgreSQL request_lineage")]
UI["Dashboard / operator"] --> Admin["Admin analytics and verification API"]
Admin --> DB
Admin --> Ledger

Request logging and lineage have different persistence paths. Loggerd consumes request events; the gateway's lineage writer appends attestations separately. The client response does not wait for loggerd to persist its event, and analytics can lag behind request completion.

Event publication is bounded and best effort: the gateway retries publication, but can drop an event when its backlog is full or attempts are exhausted. Lineage creation is also best effort and does not block a request on failure. Consumer reclaim, deduplication and dead-letter handling do not turn the whole pipeline into an exactly-once, lossless transaction.

RecordMeaningInterpretation boundary
model_requested, model_used, routing decisionRequested label, recorded serving model and selection contextA requested model name alone is insufficient to price or compare the executed workload
Tokens, duration, first-byte timing and attemptsObservations available on the executed pathMissing fields or interrupted streams are not evidence of zero work or a complete answer
Estimated and counterfactual costUsage-based price estimate and a modeled alternativeNeither is a provider invoice or guaranteed realized savings
Lineage signature and region evidenceIntegrity of an attestation and its stated evidence sourceA valid signature does not independently prove physical inference location
Task / session / agent identifiersAttribution and correlationA task tag does not demonstrate that its output passed tests or review

Prompts, response samples, cache entries, embeddings, event streams and backups are separate data surfaces. Do not infer that hashing one logged prompt removes content from all of them. Inspect capture, redaction, retention and endpoint settings for the actual deployment. See lineage, agent governance and shadow mode.

Shadow mode is optional, runs sampled comparisons against a candidate model and does not replace the primary answer. It makes additional inference calls and can incur additional cost; the candidate route is part of the data-egress boundary.

5. Two MCP integration paths​

flowchart LR
subgraph BridgePath["A. Clients invoke Yabanin tools"]
Client["MCP client"] -->|"stdio / HTTP-SSE"| Bridge["Partner bridge"]
Bridge -->|"Route prompt"| Chat["Gateway chat API"]
Bridge -->|"Spend, policy, lineage operations"| Admin["Admin API"]
end
subgraph InvokePath["B. Agents invoke registered external tools"]
Agent["Agent principal"] -->|"POST /api/mcp/invoke"| Invoke["Gateway invoke endpoint"]
Invoke --> Grants["Organization registry and agent grants"]
Grants --> Guards["Tool allow-list, input guards and SSRF checks"]
Guards --> Remote["Registered external MCP server"]
Guards -. "Audit event" .-> Events["Redis Stream / loggerd"]
end

The bridge is a separate integration process. The invoke endpoint is part of the gateway and is mounted only when the MCP proxy is configured (YABANIN_MCP_GATEWAY=1 and a database-backed setup). It requires an agent principal and enabled server grants; a regular API key is insufficient.

The external invoke path is a supported HTTP tool-call path, not a claim of universal MCP transport compatibility. It does not spawn upstream stdio servers. Forwarded agent identity headers are attribution, not upstream credentials. Input guards do not establish output redaction. Details: MCP gateway.

6. Deployment and operations​

flowchart TB
Users["Applications and operators"] --> Edge["Deployment-managed ingress / TLS"]
subgraph Boundary["Customer host, VPC or Kubernetes environment"]
Edge --> GW["Gateway replicas"]
Edge --> Admin["Admin API / dashboard"]
GW --> Redis[("Redis")]
Redis --> Logger["Loggerd workers"]
GW --> PG[("PostgreSQL / pgvector")]
Admin --> PG
Logger --> PG
Migration["Migration job"] --> PG
GW -. "Optional" .-> Local["Local model endpoints"]
GW -. "Optional" .-> Support["Classifier / embedder"]
GW -. "Metrics and traces" .-> Observe["Configured monitoring"]
end
GW -->|"Explicitly permitted egress"| Cloud["Approved cloud inference"]

This is a deployment pattern, not a prescribed replica count or an availability guarantee. Ingress, secrets, backups, resource limits and network policies belong to the installation. Protect operational listeners independently of client APIs.

DeploymentRepository assetsOperational meaning
Local developmentdeploy/docker-compose.ymlDatabase, Redis, migration job, services and optional inference/monitoring profiles; development exposure is not a production security configuration
Kubernetesdeploy/helm/yabaninConfigurable service deployment; optional regional topology requires additional configuration
On-premisesdeploy/helm/yabanin-onpremCustomer-operated components with local infrastructure options; air-gap readiness requires checking every dependency and egress path
Multi-regionpkg/regionrouter, pkg/regionrepl, tests/integration/multiregionOptional organization-region handling and replication; separate from retrying a model provider within a request

The regional gateway middleware can redirect a request with HTTP 307 to a peer or reject a pinned organization when its region is unavailable. Clients must handle the selected behavior; this is not an invisible cross-region proxy. See multi-region operation, deployment options and the air-gap checklist.

7. Source map and review boundaries​

QuestionCode to inspect
Which dependencies and optional features are wired?pkg/gateway/gateway.go, pkg/config, deploy/docker-compose.yml
In what order are chat checks and model choices made?pkg/gateway/chat.go
What can retry after streaming starts?pkg/gateway/stream.go
How are identities derived?pkg/gateway/auth.go, pkg/auth
How do cache scope and policy affect a hit?pkg/cache/service.go, pkg/cache/semantic.go
What happens when publishing or persistence fails?pkg/gateway/events.go, pkg/eventpublisher/redis_publisher.go, services/loggerd/consumer/stream.go
Where is lineage signed and stored?pkg/gateway/lineage.go, pkg/lineage
Which MCP requests are accepted?pkg/gateway/gateway.go, pkg/mcpagg/proxy.go, services/run/mcp.go
Where do operators change configuration?services/admin/handlers/router.go, pkg/adminstore, web/dashboard

Configuration changes may be observed through caches with different lifetimes; an Admin response is not a global, instantaneous configuration barrier. Verify propagation and invalidation for the setting being changed.

These diagrams document responsibilities and current code paths. They do not certify deployment security, model-output quality, customer traction or service availability. The first adoption step remains one supported workflow with explicit acceptance checks and observable operating results.