Architecture, laid bare.
Essarion's three products are built on services that compose, not services that compete. This page is the wiring diagram — where data lives, where control lives, and what actually happens between the moment a query arrives and the moment a citation comes back.
§ 01The chained gateway
From the outside the platform looks like one API. From the inside it is two services in a line: a Next.js gateway in front, and a FastAPI research engine behind it.
The gateway — essarion_api — owns identity. It holds the users table, the keys table, the usage ledger, and the plan logic. Every external request hits it first. It authenticates the caller, decides whether the call is allowed, records the usage, and proxies the work upstream.
The engine — research-agent-deploy — owns the work. It runs the phases, scores the sources, drafts the citations, and persists the run. It does not ask who you are; the gateway has already decided that. It only accepts traffic that arrives with a valid service token, and it trusts the user attribution headers the gateway adds.
browser / SDK
│
▼
essarion_api (Next.js) ← identity, keys, usage, plans
│ Authorization: Bearer <service-token>
│ X-Essarion-User-Id: <uuid>
│ X-Essarion-Key-Id: <uuid>
▼
research-agent-deploy (FastAPI) ← phases, sources, citations, runs
That separation is the load-bearing decision. It means the engine never has to know about pricing, sessions, or tenancy logic; the gateway never has to know about search providers, model routing, or the twelve-phase pipeline. Each side stays opinionated about exactly one job.
§ 02The research engine
The engine is a FastAPI service in Python. It owns the deep-research pipeline — up to twelve phases per run — and persists everything it produces.
State lives in Postgres: runs, steps, sources, citations, reasoning chunks. Web search is delegated to the You.com Smart Search API, fronted by a knowledge store and a response cache that answer from the shared corpus before any metered call is made. LLM calls are routed through OpenRouter as the primary provider with XAI as a fallback, so a single upstream outage doesn't take a run with it. Long-running operations expose two streams: WebSocket for fully bidirectional progress and SSE for one-way event feeds. The latter is what most callers use.
The pipeline runs phases in a defined order. A typical deep run touches: analyze (rewrite the question), plan (split into sub-queries), search (corpus first, then the web), scrape (fetch and extract), screen (score and prune), analyze (deep read), cite (build bibliographic records), synthesize (compose the final answer), with finishing passes around them. Every phase writes a step row; every chunk of reasoning writes a reasoning row; every URL writes a source row. By the time the run is done, the timeline is complete and queryable.
§ 03The corpus service
Sourcipedia is both the public reading experience and the compute host for the corpus auto-indexing pipeline. The worker drains the linker queue, the auto-generator drives pending topics through the research model, and a cleanup coordinator runs an always-on duplicate purger plus an opt-in nightly checker.
The two services share one Postgres database. The research engine owns the migrations and the corpus schema; the background compute that writes knowledge cards and concepts lives on Sourcipedia. The corpus HTTP surface is still proxied through the research engine, so the URL contract the client depends on never changed when the compute moved.
The relationship runs both ways. The corpus is written by ResearchAnything, and it is also read by it: because every knowledge card carries the citations behind it, the corpus doubles as a large index of vetted sources. The search orchestrator queries it before it queries the web, and when the corpus covers part of a target the paid round is steered away from domains it already supplied.
§ 04The science service
Essarion Science ships as a single FastAPI web service: /api/* is served by FastAPI and the built client SPA is served from the same process. Inside it sits the biomedical workbench — a reactive planner loop, a per-run notebook kernel, thirteen key-free database connectors, and the verification stack.
State lives in Postgres (or SQLite for small deployments): accounts, run ownership, papers, watches, and the falsification ledger. Model calls route through OpenRouter with XAI as the fallback, matching the research engine. Run progress streams over SSE as biomed.* events.
Science does not depend on the corpus service, and it does not use Google OAuth. It authenticates with its own accounts, optionally delegating to a shared Essarion ID provider — see Access & accounts.
§ 05Shared identity
One user account spans the platform. The mechanism depends on the caller.
- Browsers authenticate with a JWT cookie set after sign-in. The cookie is HTTP-only, secure, and short-lived.
- Code authenticates with an
esk_API key on theAuthorizationheader. Keys are tied to a user and resolved server-side against the hashed keys table. - Internal services authenticate to each other with a service bearer token. When the gateway calls the engine, it adds
X-Essarion-User-IdandX-Essarion-Key-Idheaders so the engine can attribute the work without ever needing to read the key itself.
The rule is strict: the engine never accepts caller-supplied user IDs without a valid service token. Identity is decided at the gateway, signed into the headers, and trusted only because the underlying transport is.
§ 06Data flow for a query
Concretely, this is what happens when a developer sends a query.
- The caller sends
POST /api/v1/querytoessarion_apiwith anesk_key in theAuthorizationheader. resolveApiKey()hashes the key, looks it up in Postgres, checks status, and returns the owning user. Invalid or revoked keys are rejected immediately.- The gateway calls
callResearchAnything()to proxy the request upstream. It attaches the service bearer token and theX-Essarion-User-Id/X-Essarion-Key-Idheaders. - The research engine accepts the request, creates a run row with a fresh
request_id, and starts working through phases. Each phase persists steps, reasoning chunks, sources, and (eventually) citations. - The engine returns a response envelope containing the answer, the citations, and the
request_id. - The gateway inserts a usage row keyed to the user and the key, then returns the envelope to the caller.
- Later, the caller can fetch the full timeline with
GET /api/v1/runs/{request_id}— the same run, fully persisted.
{
"request_id": "...",
"answer": "...",
"citations": [ ... ],
"sources": [ ... ]
}
§ 07Storage
Three stores, each with one job.
- Neon Postgres (serverless) backs identity and the research engine — users, keys, usage, runs, steps, sources, citations.
- The shared corpus tables hold Sourcipedia's knowledge cards, concepts, and auto-topic queue. They live in the same Postgres database as the engine, whose migration chain owns their schema.
- Per-project file roots hold uploads and artifacts — the documents each project accumulates. A project's files never live in the database; the database only points at them. Essarion Science persists its own accounts, papers, watches, and falsification ledger, in Postgres or SQLite depending on deployment size.
§ 08Streaming
The platform exposes two stream shapes, each chosen for the workload it serves.
Server-Sent Events (SSE) back the query stream. A research run emits five event types in order: start (run created), message (incremental reasoning), sources (sources discovered and scored), reasoning (chunks of model thinking attached to a step), and final (the synthesized answer with citations). SSE is one-way and survives reconnection cleanly, which is what a long research run actually needs.
WebSocket backs the live event subscription on a research run — phase transitions, reasoning chunks, and partial outputs pushed as they happen, with the event-history endpoint available to backfill any gap after a disconnect.