Building a "Company Brain" with a Graph Database: Every Answer Is Traceable

Ratified uses a graph database to chain IETF emails, drafts, and RFCs into verifiable citation paths—rejecting model-generated answers.
The Ratified project explores building a "company brain" with provable answers on the FalkorDB graph database, using the IETF TLS mailing list as a testbed. The team sets a hard rule: no link, no answer—answers come from hand-written Cypher queries chosen by a deterministic router, not model generation, and an empty traversal plainly says "the graph doesn't know." The key modeling challenge is that section numbers drift between draft revisions, so emails point to draft revisions rather than RFCs, and ALIGNS_SECTION aligns by heading text. On 13 self-built questions, the graph scores 13/13 versus 1/10 exact hits for retrieval, though the author concedes the gap narrows to 3/3 vs 1/3 where text evidence is visible. On 40 held-out questions, the first round scored 30/40, exposing engineering issues like 404 links, duplicate people, and miscounted scoring.
When a Single Sentence in a Spec Takes an Afternoon to Trace
In March 2018, someone on the IETF TLS mailing list discussed Section 4.2.1 of draft-ietf-tls-tls13-24. Five people, six emails, and three days later the discussion fell silent. That draft eventually became RFC 8446, and Section 4.2.1 became the place where TLS 1.3 declares versions via the supported_versions extension rather than a version field.
Join a team shipping TLS 1.3 today, and you'll almost certainly ask "why was it designed this way?" The answer lives in that email thread, but finding it requires someone with a good memory—or an afternoon. Most companies bury this kind of question in private chat logs: searchable, but not searchable by semantics. The Ratified project set out to test exactly this: can a graph database automatically connect that trail, with every step verifiable? They chose a public mailing list as their proving ground, because there everything can be fully demonstrated.

Rules First: No Link, No Answer
Before writing any code, the team established a hard rule: a "company brain" that can't be verified is just a confident guesser. So every answer must end with a link a stranger can open. That rule dictated most of the design:
- Answers come from graph traversal, not the model's memory;
- No model writes Cypher—a deterministic router picks one hand-written query per question;
- If the traversal returns empty, the card plainly says "the graph doesn't know."
This approach stands in stark contrast to mainstream RAG solutions, which hand retrieved text chunks to a model to freely compose an answer. Ratified substitutes structured graph paths for generative inference, prioritizing verifiability.
RAG (Retrieval-Augmented Generation) is the dominant architecture for Q&A systems today: it uses vector retrieval to find text chunks related to a question from a document store, then feeds those chunks as context to an LLM, which freely composes a natural-language answer. This approach is flexible and broad in coverage, but the trade-off is weak verifiability—the model may synthesize, paraphrase, or even infer conclusions that don't directly exist in the text, and citations are often attached after the fact. Ratified takes the opposite path: what's retrieved isn't text chunks but structured paths on a graph; what's output isn't model-generated sentences but the results of pre-written Cypher queries. This way every answer maps to a deterministic graph traversal and a set of clickable source nodes, sacrificing some natural-language fluency for auditability.
Three Steps to Walk a Citation Chain
Step 1: The Clue Is an Email
Everything starts with a Message node. The team scraped 3,078 emails from the TLS list (1,324 with full text), 633 threads, and 403 participants, with each email linked to its author and thread.
The first mistake was assuming that "Section 4.2.9" in an email meant Section 4.2.9 of the RFC. It doesn't—section numbers drift between drafts. An email referring to "4.2.9" in draft -21 means 4.2.9 of the -21 version, which ultimately became Section 4.2.10 of the RFC. So an email can't point directly to the RFC; it points to a draft revision, which in turn points to a specific section.
Step 2: The Path Crosses Draft Revision History
The team loaded 113 drafts and their 839 revisions from IETF datatracker, chained them with SUPERSEDED, and connected them to the 66 RFCs they eventually evolved into. The most time-consuming part was ALIGNS_SECTION—mapping every section of every revision to the RFC section it became. The matching basis was heading text, because section numbers lie but headings usually don't. That edge also records whether the text was still changing (similarity below 98% to the final published version).
So citations can be resolved: email → referenced revision → aligned RFC section. Unresolvable citations are discarded rather than guessed. The whole path is handled by a single Cypher query whose final hop is a variable-length traversal:
MATCH p=(dr)-[:SUPERSEDED*0..]->(last:DraftRevision)
MATCH (last)-[:BECAME]->(rfc:RFC)-[:HAS_SECTION]->(s)
RETURN [n IN nodes(p) | n.id] AS chain, rfc.id AS rfc, s.number AS number
For §4.2.1, the chain it returns is: email → thread → draft -24 → -25 → -26 → -27 → -28 → RFC 8446 → §4.2.1. Every node on the path is a link to the email archive, datatracker, or RFC Editor.
Cypher is the graph query language introduced by Neo4j, using an ASCII-art-like pattern syntax: (a)-[:REL]->(b) means node a points to node b via a directed edge of type REL. MATCH describes the graph pattern to match, RETURN specifies output columns, and [n IN nodes(p) | n.id] is a list comprehension that maps all nodes on path p to their ids. The [:SUPERSEDED*0..] in the query is variable-length path syntax, meaning traverse zero to any number of SUPERSEDED edges; the 0 in *0.. makes it match even when an email points directly to the final revision. FalkorDB is compatible with OpenCypher and additionally offers graph algorithm procedures like algo.pageRank and algo.betweenness, callable directly from Cypher.
Step 3: Count Only Those Who Were "There at the Time"
The first version had a flaw that only became apparent when reading the output: a name called "RFC Errata System" was listed as having "discussed" §4.2.10 before it was published. In fact, the errata report, though it names draft -28, was submitted years later.
The fix was to add a rule to the query: an email counts only if it was written during the period when the revision it points to was still current (before the next revision or the RFC's publication month). After the fix, §4.2.1 yields a clean answer: 6 emails, 5 people, March 2018, written during a period when the provision still differed from the final version. The team also stopped using words like "opposed"—edges only mean that some email referenced the provision while it was changing, and some of those people were the draft editors themselves. The card now says "discussed while still changing" and notes below: "connection, not causation."
Finding the Right People, and a Few Easy Pitfalls
Once you have the thread, the next question is "who should I ask." The card lists the five people in the thread, plus the current chair and area director, and drafts a question with sources attached—but never sends it automatically.
There's also a "who knows what" ranking. During data ingestion, the team built a CO_DISCUSSED projection with Cypher (two people speaking in the same thread creates an edge), then ran algo.pageRank and algo.betweenness on it. The draft editors ranked first on both metrics, and a chair ranked second.
Here's a trap: PageRank follows edge direction, and the relationship between two people has no direction. If you write only one edge per pair, the ranking degenerates into alphabetical order. The projection edges must be written in both directions.
PageRank was originally used for web ranking, treating nodes as pages and edges as hyperlinks; a node's score depends on the number and quality of nodes pointing to it. Betweenness (betweenness centrality) measures how often a node appears on the shortest paths between other pairs of nodes, often used to identify information hubs across communities. Both assume directed edges. In the "who knows what" projection, two people discussing in the same thread is a symmetric relationship; if you write only one directed edge, PageRank pushes all scores toward the edge's sink and the ranking degenerates into near-alphabetical order. Writing each pairwise relationship as two oppositely directed edges lets the random walk flow both ways, so the metric reflects the real structure of the discussion network.
Nobody Types "section 4.2.10"
Real users ask "why does early data work this way." A full-text index on section headings can resolve that phrase to §4.2.10 Early Data Indication, show the matched words, and offer the runner-up as a one-click alternative.
Here are two lessons you only learn by testing against the server. Space-separated words default to AND, so a casual question with one extra word matches nothing until you switch to OR. Also, indexing section body text made results worse, because broad overview sections drown out precise headings—so in the end only headings are indexed.
Every Answer Shows Its "Work"
Every card has a panel listing the Cypher FalkorDB actually executed, the parameters, the row count, and the database's self-reported run_time_ms. For the §4.2.1 question, that's 4 queries totaling under 1 millisecond on a laptop. The author suggests any graph product should copy this design—it turns "trust me" into "here's the query."
Conversely, ask about something never ingested, like IESG votes or meeting minutes, and you get a gray card saying "the graph doesn't know." The author says this is their most trusted feature. It's the exact opposite of generative systems' tendency to fabricate rather than admit ignorance, and it's where the "provable" positioning lands.
Does It Actually Beat Ordinary Retrieval?
The team wrote 13 questions and generated answer keys with a parser independent of the main records. The graph approach got 13/13 right. A BM25 plus bge-small retriever on the same text hit exactly 1/10, 3/10 in the top ten, and three counting questions couldn't be scored at all. The author admits this comparison favors them, so they offer a fairer version: on the three questions where the evidence actually exists in text visible to the retriever, graph 3/3, retrieval 1/3; and questions about revision lists and counts have answers not in any text but in the structure—which justifies using a graph database but isn't strictly a retrieval failure.
The router was tuned on those 13 questions, so they had another model write 40 new questions without seeing them. First round, unchanged: 30/40, with 5 confidently misrouted. The scorer initially reported 34, but a line-by-line check found four questions where it read "TLS 1.3" as Section 1.3. After fixing that, the set was no longer a held-out set.
Other caught errors included: every rendered revision link was a 404 until a click-check script found it; one person appeared twice under two email addresses; and the first benchmark credited a counting question to retrieval just because the number happened to appear in some text chunk.
BM25 is a classic sparse retrieval scoring function based on term frequency, inverse document frequency, and document length normalization, and has long been a strong baseline for full-text search; bge-small is a small dense vector embedding model in the BGE family that maps queries and text chunks into the same vector space and ranks by cosine similarity. The retriever compared here combines both, representing a typical "ordinary retrieval" configuration. Counting-type questions (e.g., "how many emails referenced it while the provision was changing") are especially hard for such systems, because the answer doesn't appear as a ready-made sentence in any single text chunk and requires cross-document aggregation—precisely where graph traversal's structural advantage lies.
Boundaries and Reproducibility
The graph's boundaries are clear: it can show that a connection exists between a thread and a modified provision, but it can't prove the former caused the latter. Meetings, GitHub issues, and IESG votes are outside the records. Of the 84 sections in RFC 8446, 47 can be traced back to discussions, and the rest are marked as partial answers. The author emphasizes that they validated the idea on only one working group and have not yet tested it on a company's private chat.
To try it yourself, click "Try an example" on the online demo, then open "Show how this was computed"; the code is in the ratified repository on GitHub; to run locally, just docker compose up --build, then visit 127.0.0.1:8000—it rebuilds the graph offline from committed records and needs no API keys. The team also discloses that they used AI coding assistants, documented in AI_USE.md.
Related articles

Bilibili's Hit Dify Tutorial: A Practical Guide to Building 20+ AI Apps from Scratch
A distilled guide to Bilibili's top-rated Dify tutorial: Windows + Docker deployment, MySQL connection, five app types, model integration strategy, and publishing.

Agent Skills Beginner's Guide: From Concepts to Multi-Agent Collaboration in Practice
A comprehensive guide to Agent Skills: core concepts, components, the importance of Description, standardization, beginner learning paths, and multi-agent collaboration.

One Neuron Detects Both Cats and Cars? We Reproduced It and Found a New Explanation
The cat-car neuron reinterpreted: researcher Cammarata argues it distinguishes pointy from round, not cats from cars. We reproduced the experiment and found the new explanation holds on most inputs—but cars are the exception. The interpretability debate continues on language models.