"Who owns ledger-service?" sounds like a ten-second question. In most companies it takes an afternoon.
The service catalog says Platform. A design doc from last spring says Payments. A Slack thread from Tuesday says "Infra owns Ledger now." The person who would know left in the reorg. Each source is a little bit right, and none of them says when it was right.
The usual AI answer is to throw all of it into a vector store and let a model summarize. That gives you a fluent paragraph, but it can't tell you which source won, whether the answer was true last quarter or is true today, or what it is quietly making up. For questions about ownership, accountability and decisions, a confident wrong answer is worse than no answer.
I wanted a company brain that shows its receipts. That became Nodus.
Nodus is an organizational knowledge graph built entirely on FalkorDB. It pulls people, teams, systems, projects, tickets, docs, chats and architecture decisions (ADRs) into one bi-temporal graph. It answers "who owns this?", "who knows about this?" and "why was this decided?", and every answer comes with an exact quote from its source.
The core idea is simple: a fact is not a chunk of text; it is a versioned claim with evidence. Every claim Nodus learns becomes an Assertion node that points at the exact character range of the Span it came from. Each assertion knows:
valid_from, valid_to) recorded_at, retracted_at) 4 structured data or human confirmation, 2 docs, 1 chat, 0 drafts)
The two clocks let Nodus answer two different historical questions. "Who owned Ledger before the reorg?" asks about reality. "What did we believe about Ledger last month?" asks about the brain's own state. Most systems can't tell those apart. Nodus can, and nothing is ever deleted.
FalkorDB is the only datastore. Storage, vector and full-text search, graph algorithms, temporal reasoning and permission checks all happen inside it. The rest of this post covers the four hardest design problems and how FalkorDB solved each one.
The first design fought itself. If every fact is its own node, you keep perfect provenance, but a question like "what breaks if Ledger goes down?" has to walk through an assertion node at every hop. If facts are plain edges, traversal is fast, but the moment ownership changes you lose the history and the conflicting claims.
I stopped choosing and kept both layers in the same graph:
Source -[:HAS_SPAN]-> Span <-[:EVIDENCED_BY]- Assertion, with assertions linked to each other by SUPERSEDES, CONTINUES, DISPUTES and CONFIRMS. This layer is append-only and holds the full story. OWNS, LEADS, DEPENDS_ON, PART_OF, AFFECTS, REPLACES). Each edge carries {aid, valid_from, valid_to, authority}, where aid points back to its assertion.
Queries walk the fast edges on the right; the dashed aid link is how any answer finds its way back to the claim and quote on the left.
Operational queries run on the fast layer and only touch the ledger to fetch citations. Here is the blast-radius query:
MATCH p = (x:Entity {id:$x})<-[:DEPENDS_ON*1..3]-(s:Entity)
WHERE all(e IN relationships(p) WHERE e.valid_from <= $V AND e.valid_to > $V)
RETURN s.id, s.name, length(p) AS hops, [e IN relationships(p) | e.aid] AS aids
ORDER BY hops
That is a variable-length traversal with a time filter on every hop, and on the demo graph it returns in under a millisecond on FalkorDB's sparse adjacency matrices. The aids it returns resolve straight to the assertions and quoted spans behind each dependency, so the answer cites its sources at no extra cost.
Real companies contradict themselves constantly, so contradiction handling had to be a first-class rule rather than an edge case. Nodus uses one rule: a new claim only replaces an old one if its authority is equal or higher.
The demo shows this end to end. The catalog (authority 4) says Payments owns Ledger. Someone posts "Infra owns Ledger now" in Slack (authority 1). Nodus doesn't flip ownership based on a chat message. It records a dispute, keeps answering "Payments", and flags the conflict. Once the Payments lead confirms the handover, that confirmation (authority 4) supersedes the old fact and Infra becomes the owner, with the full chain of evidence intact.
The same ledger powers stale-doc detection. One query finds the latest version of each wiki page that still asserts a fact superseded after the page was last edited. Outdated docs flag themselves.
A rule like this is easy to get subtly wrong, so I tested it with property-based tests. They feed the same messages in random orders and check that the final state always converges to the same answer. If arrival order changed the outcome, the brain would be guessing.
Some questions aren't about one node's neighbors. They're about the shape of the whole organization: which systems are most critical, who is a single point of failure, where the silos are. FalkorDB ships native GraphBLAS implementations of exactly the algorithms for these, and they are fast.
The catch is that they filter by node label and relationship type, not by property predicates evaluated per hop. I couldn't pass valid_from <= $V < valid_to into PageRank, and running on the live graph would mix in edges from every era.
So Nodus builds snapshot graphs. For a timestamp $V, it materializes a lightweight read-only graph named <graph>__snap_<YYYYMMDD> that contains only the edges valid at that moment, plus pre-aggregated helper edges. The native algorithms run on the snapshot:
| Question | FalkorDB procedure |
|---|---|
| Which systems are most critical? | algo.pageRank('System', 'DEPENDS_ON') |
| Who are our single points of failure? | algo.betweenness over undirectedLINK edges |
| Where are our knowledge gaps and silos? | algo.WCC over OWNS ,LEADS ,MEMBER_OF ,PART_OF |
| How is Bob connected to Ledger? | algo.SPpaths , top 3 paths, up to 4 hops, both directions |
Algorithm output is treated as a ranking, not a claim. Before Nodus names anyone as a single point of failure, it joins the result back to the primary graph and attaches the assertions and quotes that put them in that position. Because snapshots are per-date, you can ask what the risk picture looked like before the reorg and compare it with today.
Nodus doesn't only answer questions; it acts. It routes tickets, settles disputes, drafts superseding ADRs and undoes actions. Each of those needs a permission check: only the lead of the team that currently owns a system can approve handing it to another team.
The usual pattern is to check permissions in application code, then write. That leaves a time-of-check to time-of-use (TOCTOU) gap. Between the check and the write, ownership might change, or someone else might settle the same dispute.
The fix was to move the permission check into the write. Every mutation is a single Cypher query. It first checks eligibility against the org graph itself (Person -[:LEADS]-> Team -[:OWNS]-> System) and confirms the target assertion is still in the state the caller saw. That leaves one question: how do you make a Cypher query abort halfway on purpose?
The answer is a unique constraint and a sentinel. At setup, Nodus creates a unique constraint on :GuardFail(id) and seeds one node, {id: 'sentinel'}. Each write query includes a FOREACH guard:
FOREACH (_ IN CASE WHEN ok THEN [] ELSE [1] END |
CREATE (:GuardFail {id:'sentinel'}))
If the check passed, this does nothing. If it failed, it tries to create a second sentinel, the constraint fires, and FalkorDB rolls back the entire query. The permission check, the state change, the edge-cache update and the Action audit node succeed or fail together.
This relies on two guarantees FalkorDB documents in Atomicity and Concurrency Control: every write query is atomic, so a constraint violation undoes all of it, and writes to a graph are serialized, so the check inside the query always sees the latest committed state.
Those Action nodes enable one of my favorite features. Each records what it was BASED_ON and what it PRODUCED, so Nodus can show any past action side by side: what was true when it was taken, and what is true now.
In Nodus, the graph is the source of truth and the LLM is optional. When a model is available, it only does three narrow jobs: extracting candidate claims from unstructured text, planning which Cypher templates to run, and phrasing the final answer. It never decides what is true.
Every answer passes a deterministic verifier before it reaches the user. For each claim in the draft, the verifier checks against FalkorDB that:
If the graph lacks evidence for something, Nodus says what is missing instead of filling the gap. "I can't find who owns this after the reorg" is a useful answer. A plausible guess is not.
This design has a nice side effect: Nodus runs fully offline. Without an API key, a pattern-based extractor, a rule-based planner and local embeddings take over, and every answer is still cited and verified. Retrieval never leaves FalkorDB either. Embeddings live on Entity and Span nodes, so vector search (db.idx.vector.queryNodes over vecf32) and full-text search (db.idx.fulltext.queryNodes) are fused with graph expansion in the same engine.
The same read and guarded-write tools are also exposed over MCP, so a coding agent like Claude Code or Cursor can ask "who should review my change to ledger-service?" from the IDE and get the same cited answer.
I first treated time as a property I'd add later. That was wrong. Once I separated "when it was true" from "when we learned it", ownership history, stale docs, dispute handling and action audits all fell out of the same two fields. Retrofitting bi-temporality would have meant rewriting every query.
The dual-layer design felt like duplication at first. In practice it's the same trade databases have always made with indexes and materialized views: keep a ledger as the record, and keep a fast projection for reads. The aid on every edge is the small detail that makes it work.
I lost time trying to make GraphBLAS algorithms respect per-hop time filters. Once I accepted that they operate on labels and types, snapshot graphs turned out to be cleaner than any workaround. The same goes for the sentinel guard: I didn't need a new transaction feature, just a creative use of a unique constraint.
Authorization in middleware looked fine until I thought through two people acting at once. Checking permissions inside the same query that writes made a whole class of race conditions impossible rather than unlikely.
The verifier rejects more drafts than I expected, and every rejection is a hallucination that never reached a user. Being able to say exactly which evidence is missing turned out to be one of the most trusted parts of the demo.
Conflict resolution that works in the order I typed the test cases isn't conflict resolution. Property-based tests that shuffle message arrival caught bugs no hand-written test would have found.
Forcing the core to work with no model at all made every boundary explicit. The LLM became a convenience layer on top of a system that is correct on its own.
Ask it "Who owns ledger-service, and what depends on it?", "Why did we adopt ADR-007?" or "Who are our single points of failure?" Then drag the time slider back before the reorg and ask again. The demo company is fully synthetic, so feel free to break things.
To run it locally, start FalkorDB with Docker (docker run -p 6379:6379 falkordb/falkordb-server; see the Docker guide), seed the demo company with brain reset --seed world/seeds/1, and start the API and UI. Or connect it to Claude Code as an MCP server and ask your IDE who owns the code you're about to change.
Track entered: Track 03, Company Brain
Nodus is an organizational intelligence engine built on FalkorDB. It unifies fragmented company state—people, teams, systems, projects, tickets, documents, chats, and architectural decisions—into a single bi-temporal knowledge graph. Rather than relying on probabilistic RAG retrieval over flat text chunks, Nodus models facts as versioned graph assertions backed by exact source spans. It answers "who owns this?", "who knows about this?", and "why was this decided?" with deterministic provenance, and executes permission-guarded workflows directly inside FalkorDB.
Demo video: https://youtu.be/E35AO0AtxxY
Live deployment: https://nodus.rishiklabs.tech/
The demo company is fully synthetic, generated by world/build.py and committed under world/seeds/. No private or personal data is used.
Unified ingestion across docs, tickets, and chats.
Multi-format connectors normalize design docs, issue tickets, Markdown files, and Slack exports into a uniform provenance subgraph: Source nodes segmented into…
Nodus was built for Track 03 (Company Brain) of the FalkorDB hackathon. If you've fought the "who owns this?" problem at your company, I'd love to hear how. Find me at https://github.com/RISHIK92.