Under the hood
Architecture
One Next.js app, two databases, and a very strict rule about who is allowed to make an action happen.
The whole map
The app is stateless. Every request is authenticated, scoped to one workspace, and then reads or writes that workspace’s graph. You can run many copies of it behind a load balancer, which is exactly what Vercel does.
Why two databases?
They hold different kinds of truth, so they use different kinds of database.
| Store | Holds | Why this one |
|---|---|---|
| FalkorDB | Facts, beliefs, decisions, actions, sources and the edges between them. | Staleness and taint are graph questions (“what can I reach from here?”). A graph database answers them in one query instead of many. |
| Turso / SQLite | Users, password hashes, sessions, API keys, integrations, review items, ingest ledger. | Plain relational data with constraints, and a free hosted tier. Locally it falls back to a file, so there is nothing to set up. |
The graph model
Three kinds of edges, and that is all:
(Node {kind: fact | belief | decision | action})
-[:DEPENDS_ON]-> (Node) what this was built on
(Node) -[:FROM]-> (Source {trust}) where a fact came from
(new) -[:SUPERSEDES]-> (old) a fact was replaced
Staleness is reachability over DEPENDS_ON. Taint is reachability to an untrusted Source. Both are computed when you ask, not stored, so they can never drift out of date.
One message, start to finish
Follow a single email through the system.
It arrivesA connector (here, email forwarding) turns the raw message into a common shape: text, author, source, and a verdict on whether the sender is outside the company.
Duplicates are droppedEach message has an external id (an email message-id, a Slack timestamp). The ingest ledger remembers which ones were seen, so retries and replays do nothing.
Claims are extractedAn LLM reads the text and returns structured claims with the exact quote and a confidence. If no OpenAI key is set, a rules engine does a simpler pass.
Each claim is reconciledIt is compared with existing facts: the same (confirm), new (add), or contradicting (supersede).
Risky changes are queuedA stranger, low confidence, or a possible conflict goes to the review queue instead of changing memory.
Memory is updatedSafe changes are applied. A superseded fact turns everything that depended on it stale, and the response lists the impact, including actions already taken.
The firewall guards actionsLater, an agent proposes “wire $48,000”. Its ancestry reaches the stranger’s email, so it is tainted and blocked. The check returns the exact path.
How a request is handled
- Authenticate. A browser session cookie, or
Authorization: Bearer sk_…. - Authorize. The role must be high enough: viewer for reads, member for writes, admin for keys and settings.
- Open the workspace graph. The graph name comes from the workspace id, never from the request body.
- Validate input. Every body is checked against a schema before anything touches the database.
- Respond uniformly. Errors are JSON with a status and details. If FalkorDB is down you get a clear 503, not a hang.
The stack
- Next.js (App Router) serves the website, the REST API and the MCP endpoint from one deployment.
- FalkorDB for the graph, libSQL for the control plane.
- Model Context Protocol SDK for the agent interface, over streamable HTTP and stateless: a fresh server per request.
- OpenAI for claim extraction and audio transcription (optional; there is a rules fallback).
- Zod for validating every input.