Skip to main content
The Postgres spine that backs every brain uses up to three inference legs. Only the first is required; the other two make answers better and their absence is a supported state, not a fault.
These are deployment-level, not per-tenant: a brain’s configuration is a property of the API that serves it, and GET /v1/brain/status is the read that tells you which state you are in. It is the one brain route that is not plan-metered.

How each leg resolves

Each leg walks its own ladder and stops at the first rung that answers. An explicit BRAIN_*_BASE always wins over the hosted default, so pointing a leg at your own endpoint does not require un-setting the hosted key. The rung that answered is reported per service as source:
The reranker deliberately has no hosted rung. The only target it is built for is a self-hosted TEI/BGE cross-encoder, so on a deployment that runs none, reranker reports configured: false, source: null permanently and correctly. It carries optional: true for exactly that reason, and it is not part of ready. Do not treat it as an incident.
Explicit overrides: BRAIN_EMBED_KEY / BRAIN_EMBED_AUTH_HEADER (authorization, api-key, or x-api-key — for Azure-style endpoints), BRAIN_ANSWER_KEY, and BRAIN_RERANK_KEY apply to the explicit rung. The auth-header knob is ignored on the hosted embedder rung: api.openai.com accepts nothing but a bearer token. Embedding width is BRAIN_EMBED_DIM (default 1536), it is the fixed width of the stored vector column, and it is enforced — a vector of the wrong length is rejected loudly rather than truncated or padded. On the hosted rung a text-embedding-3-* model is asked for that width explicitly, so a non-default BRAIN_EMBED_DIM still produces correctly-sized vectors — but only up to the model’s native width (1536 for text-embedding-3-small, 3072 for -large). Above it, or on a BRAIN_EMBED_MODEL that takes no dimensions field and whose native width is not BRAIN_EMBED_DIM, the hosted rung declines rather than resolving: source is null and configured: false even with OPENAI_API_KEY set, ready is false, and ingest 501s. The rung refuses up front instead of accepting documents that would fail the dim check minutes later. The explicit rung is exempt — an operator who sets BRAIN_EMBED_BASE sets BRAIN_EMBED_DIM to match it.

GET /v1/brain/status

Model configuration, per-service health, and knowledge-base / document / projection counts. Present at both prefixes (/v1/brain/status and /v1/users/{user_id}/brain/status), and nested as runtime inside GET /v1/brain/ops-status. Every service line that resolved to an HTTP endpoint is probed live on this callGET <base>/models for the embedder and for an explicit answerer base, GET https://openrouter.ai/api/v1/key on the hosted answerer rung (it 401s on a revoked key, where /models answers 200 with no key at all), GET <base>/health for the reranker, each authenticated with that leg’s key and bounded by a 2.5 s timeout — so ok reflects the endpoint now, not at boot. latency_ms is that probe. Mock and unresolved legs issue no request and carry no latency_ms.

Fields

The three inference services share one shape:

ready

ready is true when the embedding service is up, the projection queue has no failures, and — only if a semantic engine is configured — that engine is healthy. The answerer and the reranker are excluded on purpose: both are optional, and a query still answers without them. A brain can be ready: true while services.reranker reports configured: false.
semantic_engine reports the configured preference, not what is running. Whether the semantic engine is actually in the read path is services.gbrain.configured / services.gbrain.ok, which require both the preference and a base URL. When it is not configured, document RAG is served from the Postgres spine — the canonical store either way.

Degraded, but still serving

Most of these are normal operating states, not incidents.
Synthesized answers are bounded by BRAIN_ANSWER_MAX_TOKENS. Unset, it resolves per rung: 512 output tokens on the hosted OpenRouter rung, 128 on an explicit BRAIN_ANSWER_BASE. If the model stops because it hit the cap, the response falls back to answer_mode: "grounded" rather than returning a truncated paraphrase — so answer is always complete, in one form or the other. /status does not report this value.

When nothing is configured

With no embedder on any rung, the two document paths refuse with 501 feature_not_configured and a hint naming every way to satisfy it. Ingest refuses at the door, before the document is accepted or any credit is charged:
501
POST /v1/brain/query fails the same way when it tries to embed the question. The credit check runs before retrieval and the deduction after it, so a query that fails this way is not charged. GET /v1/brain/status on such a deployment reports the following — abridged to the fields that differ from the example above; the rest of the object is unchanged and no probe is issued:
200
As the hint says, semantic memory does not need the embedder. POST /remember, /recall, /graph and /timeline keep answering; recall and think simply return no document cards, and POST /think is still charged its 0.08 credits because the recall and synthesis legs still run.

Local development

BRAIN_MOCK=true (or BLOCKBRAIN_MOCK=true) swaps the whole brain provider for a deterministic in-memory mock — not just the three legs. No Postgres spine, no network, no keys. It is a development and test mode, not a degraded production one: query is answered out of the mock store, and its response carries no answer_mode field at all. /status under mock reports source: "mock" on all three legs, with ok: true for embedding and reranker and configured: false for answerer — the mock rung resolves to no answerer, which is why it is the one leg whose mock arm is not “configured”. Every models.* entry is null except embedding_dim, and ready is true.