FalkorDB

Stars and mentions

Last 30 days, Oct 4 – Nov 2

GitHub stars2,458
mentions9
0123246369492Oct 4Oct 18Nov 2
UTC days · repositories FalkorDB owns10 repositories

Package downloads

Cumulative recorded downloads · since 2026-04-08

crates.io—
PyPI5.2M
01.3M2.6M3.9M5.2MOct 4Oct 18Nov 2
Nov 2crates.io: 0/1 packages observed · PyPI: 1/4 packages observed

Across 5 known packages in repositories FalkorDB owns. Cumulative totals include recorded downloads before the selected range. Missing observations leave gaps; totals may be incomplete. PyPI excludes known mirrors.

Top repositories

FalkorDB’s repositories ranked by new forks, as on trending.

Loading

Contributes to

Repositories outside FalkorDB’s account where FalkorDB opened or reviewed pull requests in the past 365 days.

Who noticed FalkorDB

Everyone who starred or forked FalkorDB’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

10posts
Nov 31 post
  1. FalkorDB 6.0.3 moves unique-constraint checks to index lookups. A small implementation change with a useful principle behind it: enforcing data integrity should not require scanning the graph. github.com/FalkorDB/FalkorDB/releases/tag/v6.0.3

  2. @kunalstwt @falkordb this is my submission for the Graph Hacks hackathon would love to know your thoughts on the blog as well as the project! found bugs in the FalkorDB repo as well while building the project and filed issues for them!! FalkorDB#3356 FalkorDB#3357

    11 likes · 1 repost · 102 views
  3. Three crashes, one body shop: a fraud agent where the graph picks the next query Three crashes, all late at night, all at the same intersection, all within a month. Each time, the other driver drove off. Each time, nobody called the police. All three cars were fixed at the same body shop. All three drivers were treated at the same clinic. The same adjuster handled all three claims and had already approved two of them, for $38,000 and $35,500. Two of the drivers share a phone number. Two of them share an address. The third claim, CLM-48291, is still in the queue. It asks for $42,500 for a night-time side-impact at a blind intersection, and on its own it looks ordinary. Someone reading that one file has nothing to flag. The claims are my demo data, but the shape is real. In a South Florida case reported in 2018, a ring paid tow-truck drivers and body-shop workers $1,500 to $2,000 per patient to steer crash victims to its chiropractic clinics, and took about $23 million from insurers (Fox News). No single claim gives the fraud away. The connections between them do. So for Graph Hacks I built Diktiax. You give it a claim id. It walks a FalkorDB graph, decides which checks are worth running based on what it finds, scores the claim with plain code, and then acts: it writes the verdict back into the graph, freezes the payout and assigns an investigator. Before it looks at a claim again, it reads its own past verdicts there. It's my entry for Track 01 (agents that act) and Track 02 (agent memory). Try it: live demo · code · video · every number and its test {% embed youtu.be %} Similar text is the wrong question My first instinct was the 2026 default: embed the claims and let a model look for patterns. I dropped it quickly. Vector search finds claims that read alike. Staged-crash stories often do, but so do honest ones, and a ring member who files a plain claim drifts out of reach. What ties these three together is a phone number in two customer records and a body shop on all three, and neither moves an embedding much. In a graph that tie is a two-hop path, Customer → Phone ← Customer, which gave me the one modelling rule the project hangs on: anything a fraud ring reuses gets its own node. In the demo graph, the claimant on CLM-48291 and the claimant on CLM-39102, the claim already approved for $38,000, list the same phone, and both cars went to SHOP-801. Neither file mentions the other. !Same three claims: similar text vs shared connections Entity resolution in one MERGE !Diktiax graph model: everything a ring reuses is its own node; dashed edges are written by the agent and the reviewer Phones, addresses and VINs are nodes, merged on their value. This is how filing a claim starts: From INGEST_CLAIM in backend/src/db/queries.rs: MERGE (phone:Phone {number: $phone}) MERGE (addr:Address {line: $address}) MERGE (vin:VIN {vin: $vin}) MERGE (loc:Location {id: $location_id}) ON CREATE SET loc.name = $location_id MERGE (shop:RepairShop {id: $repair_shop_id}) ON CREATE SET shop.name = $repair_shop_id If another customer already has that number, the new customer is attached to the same node, and the link exists the moment the claim is filed. No matching job, no fuzzy join, no nightly batch. Two Vehicle registrations in different states pointing at one VIN node is the cloned-VIN signal, straight out of the shape of the data. The demo graph is synthetic and small on purpose: one staged-collision ring, one cloned-VIN pair and three clean claims as controls. Four fraud patterns, four multi-hop traversals Each pattern is one parameterized Cypher query that returns the node ids making up the evidence. The kickback triangle asks whether an unreported accident's adjuster, shop and clinic keep showing up together: KICKBACK_TRIANGLE, from backend/src/db/queries.rs: MATCH (a:Accident)-[:TRIGGERED_CLAIM]->(c:Claim {id: $claim_id}), (c)-[:REPAIRED_AT]->(shop:RepairShop), (c)-[:TREATED_AT]->(clinic:Clinic), (c)-[:ASSIGNED_TO]->(adj:Adjuster), (other:Claim)-[:REPAIRED_AT]->(shop), (other)-[:TREATED_AT]->(clinic), (other)-[:ASSIGNED_TO]->(adj) WHERE other.id <> c.id AND a.police_report_filed = false OPTIONAL MATCH (shop)-[ref:COLLUDES_WITH]->(clinic) RETURN c.id AS claim_id, adj.id AS adjuster_id, shop.id AS shop_id, clinic.id AS clinic_id, other.id AS linked_claim_id, ref IS NOT NULL AS referral_link ORDER BY linked_claim_id Read it clause by clause. The first line pins the claim and its accident. The next two lines say this claim's shop, clinic and adjuster must also all be attached to some other claim: that's the triangle. WHERE keeps only accidents with no police report, and the OPTIONAL MATCH checks for an explicit referral link between the shop and the clinic without requiring one. In SQL this is three provider tables self-joined on two claims, then filtered; here it's one pattern that reads like the description. The shared-contact query walks Customer → Phone|Address ← Customer → Claim; ghost collision looks for other claims at the same location and shop within 90 days; cloned VIN follows one VIN to every registration that filed a claim. !The ring, highlighted on the claim's graph in the dashboard Okay, but does the graph actually matter? My own seed data makes it look too easy. The seeded fraud also carries the classic red flags (no police report, big amounts), so a dumb rule like "flag it if there's no police report or it's over $25,000" catches it too. So I added ring members who learned the rules (police reports, small claims, same phone, corner and body shop) and honest drivers who trip the red flags, with labels fixed before anything ran. Two reviewers that see one claim at a time got the same claims: those rules, and an LLM (gpt-oss-120b on Groq) handed each claim's full file. | Group | Claims | Per-claim rules | LLM, one claim at a time | Graph agent | |---|---|---|---|---| | Seeded fraud (ring + cloned VIN) | 5 | 5/5 | 5/5 | 5/5 | | Seeded clean controls | 3 | 0/3 | 0/3 | 0/3 | | Evasive ring members | 3 | 0/3 | 0/3 | 3/3 | | Honest claims with red flags | 2 | 2/2 | 1/2 | 0/2 | On the easy data, all three tie. Once the ring adapts, both single-claim reviewers wave it through. The graph judges a claim by its connections, and a ring can't stop reusing what makes it a ring. The groups are small and I built them, so this shows the mechanism, not an accuracy number. So I scaled it up to 5,040 generated claims: mostly honest ones with chance overlaps, plus tight rings, evasive rings, and loose rings that share only a corner and a body shop. !Thirty fraud rings hidden among 5,040 generated claims | Group | Claims | Per-claim rules flagged | Graph froze payout | Graph sent to audit | |---|---|---|---|---| | Tight rings | 120 | 120 | 120 | 0 | | Evasive rings | 80 | 0 | 80 | 0 | | Loose rings | 40 | 0 | 0 | 40 | | Honest | 4,800 | 2,789 | 0 | 169 | | Precision / recall / F1 (frozen = flagged) | | 0.04 / 0.50 / 0.08 | 1.00 / 0.83 / 0.91 | | | Median / p95 per investigation | | | 17 ms / 23 ms | | The graph froze every tight and evasive ring claim and no honest ones, at a median of 17 ms per investigation. The evasive rings carry a police report and a small amount, so the rules miss them by construction. The loose rings are the graph's blind spot: they land in audit, along with 169 honest claims. An LLM reading a sample one claim at a time missed every evasive and loose-ring claim. I then re-ran it on a fresh seed as a pre-registration: the answer key, bound to a hash of the generated data, went into its own commit before the results, so git log shows the order. Same outcome: the rules flagged 120 of 240 fraud claims and 2,753 honest drivers; the graph froze 200 of 240 and no honest claim, with 178 honest claims sent to audit. The hash shows the key wasn't edited after scoring; it can't show the generator wasn't tuned between runs. In backend/, cargo test --release --test scale_benchmark -- --ignored --nocapture reproduces the first run, and SCALE_SEED=20_261_019 with scale_benchmark -- --ignored --exact the pre-registered one, failing if the key's hash changed. The graph picks the next query !How Diktiax picks its next query The agent first explores the claim's neighbourhood three hops out. With the rule-based planner, a detector then only runs if that neighbourhood makes a match possible. Here's the real trace on the ring claim: | # | Tool | Why the agent chose it | Result | |---|------|------------------------|--------| | 1 | explore_claim_subgraph | Map the neighbourhood | 24 nodes, 32 edges | | 2 | detect_ghost_collision | LOC-441 hosts several accidents; repaired at SHOP-801 | match, +45 | | 3 | detect_shared_contacts | Phone +1-555-019-8821 has more than one owner | match, +25 | | 4 | detect_vin_cloning | A VIN is in the neighbourhood | no match | | 5 | detect_kickback_triangle | No police report; adjuster and clinic attached | match, +40 | | 6 | shop_centrality SHOP-801 | The shop appeared in the evidence | 1.71× the average | | 7 | ring_components | FalkorDB's algo.WCC picks which linked claims to re-check | ring of 3 claims | | 8 | detect_ghost_collision on CLM-44730 | In the ring component and named in the evidence | corroborated | | 9 | detect_kickback_triangle on CLM-39102 | In the ring component and named in the evidence | corroborated | The clean control takes three calls: explore, two detectors, done. Nobody shares its claimant's phone or address, and its accident has a police report and no clinic, so those checks never run. Same agent, same code. The graph made the investigations different. !Agent trace in the dashboard Three planners sit behind one interface: rule-based (needs nothing), a Groq-hosted model (openai/gpt-oss-120b), or Claude. All pick from the same nine graph tools, and every step shows the planner's reason, its arguments and the Cypher it ran. The bug that could have paid the fraudster !A failed query used to score zero and clear the claim; now it means inconclusive and manual review This one had no symptom, which was the problem. The scoring code treated a failed query exactly like an empty one: no rows, no match, zero points. Say FalkorDB hiccups (a timeout, a restart, a dropped connection) while the detectors run on CLM-48291. Every detector comes back empty, the score reads 0, and the claim lands in CLEARED_AUTOMATED. A $42,500 staged crash gets marked clean, with nothing red on the dashboard. The fix is one rule: an error is not a no. A failed query means the evidence is incomplete, so the score is only a lower bound, and missing detectors can only add points. If that lower bound already freezes the payout, it freezes. Otherwise the verdict is INCONCLUSIVE with zero confidence: apply_graph_failures in backend/src/agent/verdict.rs: /// With graph failures the score is only a LOWER bound (missing detectors can only add points). /// A bound that already escalates (HIGH/CRITICAL) stands; anything lower becomes INCONCLUSIVE /// with zero confidence, which the dispatcher maps to `NEEDS_MANUAL_REVIEW` (no clear, no freeze). pub fn apply_graph_failures(assessment: &mut FraudRiskAssessment, failures: &[String]) { if failures.is_empty() || crate::actions::status_for(&assessment.risk_tier).1 { return; } assessment.risk_tier = INCONCLUSIVE_TIER.to_string(); assessment.confidence = 0.0; } The claim goes to NEEDS_MANUAL_REVIEW, nothing is cleared, and the rationale names every failed query. In an agent that acts, the dangerous bugs are the quiet defaults that resolve in the wrong person's favour. A crash at least gets noticed. The model never sets the score !The model picks which of the nine graph tools to run; plain code computes the score That bug made me stricter about the LLM too. Models are good at choosing what to look at next. I didn't want one deciding whether to freeze somebody's payout. The planner's job ends at choosing tools: From backend/src/agent/investigator.rs: let patterns = evidence::scoring_patterns(claim_id, &outcomes)?; let mut assessment = FraudRiskEvaluator::evaluate(&patterns); FraudRiskEvaluator is a plain function: it adds the points from detectors that matched on the target claim, counts each pattern once, and caps at 100. When an LLM plans, any detector it skipped runs anyway before scoring, so a model can't clear a claim by not looking. Acting on it, remembering it, and how it's put together The verdict goes back into the graph as a claim status plus an Investigation node with the decision and score. The investigator is picked from the graph too: best specialty coverage of the matched patterns, fewest open cases. !Diktiax architecture The dashboard runs on Vercel; the Rust backend and FalkorDB share one container. The same nine tools, plus seven for outside agents, are served over MCP, so another agent runs exactly the Cypher this one runs. One is FalkorDB's algo.WCC: the agent runs it once per investigation, and the ring component it returns picks which linked claims get re-checked, so on the ring claim both other members are corroborated, not one twice. !Verdicts stay under the claim in the graph, so a new session or another agent over MCP reads the same certificate That makes the graph the agent's memory. Each new verdict supersedes the last in one chain, linked to its evidence claims. On a re-run the agent reads that chain first: it says whether it agrees with the last verdict and skips corroboration the last verdict already relied on (9 tool calls drop to 7 on the ring claim). The score never reads the past verdict; it's context, not evidence. !Two agents, one claim: the Diktiax agent and a scripted MCP client playing the reviewer never message each other; the graph is the shared memory Over MCP, investigation_history returns that chain, and another agent's second investigate_claim gets the stored verdict instead of a second run: → investigate_claim {"claim_id":"CLM-48291"} ← {"reused":true,"certificate_id":"CERT-02987df7-…","decision":"CRITICAL_FRAUD","risk_score":100} What FalkorDB did, and what I built FalkorDB did the heavy lifting: MERGE gave me entity resolution, openCypher let each pattern read like its description, and as a Redis module it shares one small container with the agent. !Evidence queries run on the read-only lane with a timeout; only ingest, the verdict and the assignment write Every evidence query goes through GRAPH.RO_QUERY with a server-side timeout, so the part of the agent that gathers evidence can't write to the graph. GRAPH.EXPLAIN showed one flaw: the kickback pattern starts at (a:Accident), so it opens with a label scan, not an index seek. I left it, because an investigation spends about a millisecond in the database; every second you wait is the LLM. I built the parts around it: the graph model, the detectors and scoring, the planner loop, the write-back and assignment, and the dashboard. Other things that bit me - Path depth can't be a parameter. [*1..$depth] fails on v4.22.1 with "unhandled type in inlined properties" (another team reported it as FalkorDB#3350). The depth is now clamped in Rust and inlined. - A race my own test found, twice. Concurrent runs could assign one claim to two investigators when the check and the MERGE were separate queries. I folded them into one statement, but FalkorDB doesn't document that as atomic, so the guarantee is a unique constraint on ASSIGNED_INVESTIGATOR.claim_id, awaited at startup until enforced. Now 32 racing writers always leave one edge. - Groq's rate limits. Raw JSON results blew the per-minute token budget, so each result now goes back as a one-line summary. What I'd tell the next builder 1. Model the things your problem reuses as nodes. Half my detectors are just "who else points at this node?" 2. Make an error loud. Any "no result means no" default in an agent that acts will eventually act on a failure. 3. Let the LLM choose, let code decide. The planner is replaceable; the scorer isn't. 4. Build the baseline that could prove you wrong, and keep it as a test. 5. Put the guarantee in the database. Folding a check-then-write into one statement looked atomic; only a unique constraint made 32 racing writers leave one edge every time. Where the graph loses, and what it doesn't do The graph loses on loose rings. Claims that share only a corner and a body shop never get frozen: all 40 went to audit, along with 169 honest claims (178 on the pre-registered re-run) that happened to share a shop or a household address. On the easy seed claims, the rules and the LLM tie it. The rings were generated the way the detectors look, so the evasive result is a ceiling, not a field number. The labels are the generator's, though, and the detectors never see them, which is why the graph can and does miss. The data is synthetic, so this shows the approach, not production scale or real-world accuracy. I ran a vector-search baseline late (MiniLM embeddings in a FalkorDB vector index): on the 8 seeded accident stories it found every ring-mate in its top 3, but every fraud claim also had an honest neighbour, and the plain-filed ring members drifted away from their ring. That's a sanity check, not a benchmark. The LLM reading each claim stands in for a reviewer who understands the text. Severity uses fixed thresholds, not learned weights. A flag means a person should look; it doesn't prove fraud, which is why Diktiax freezes and assigns instead of denying. The ghost-collision window is 90 days, so new claims stop matching the seeded ring once its dates age out. Try it docker compose up -d falkordb cd backend && cargo run --release # API on :3001, seeds the graph cd frontend && bun install && bun run dev # dashboard on :3000 Add GROQ_API_KEY to .env for the LLM planner, or leave it out for the rule-based one. Then file a claim from the dashboard with phone +1-555-019-8821, location LOC-441 and shop SHOP-801, and watch it walk straight into the ring. - Live demo: diktiax.vercel.app - Demo video: youtu.be - Repo: alienx5499/Diktiax Built with FalkorDB for WeMakeDevs Graph Hacks: Context for AI Agents, Tracks 01 and 02. If you've worked on fraud or any other link-analysis problem: which pattern would you add as the fifth detector?

    5 reactions
  4. When someone quits, what leaves with them? We asked a graph. --- title: "When someone quits, what leaves with them? We asked a graph." published: true description: "The handoff list shows what a person owns. A FalkorDB graph finds what stays off it: decisions nobody else wrote down, systems nobody else touches, the only link between two teams." tags: graphdatabase, ai, hackathon, opensource cover_image: raw.githubusercontent.com --- When a person leaves a company, the handoff list shows what they own. What only they know usually stays off it: a decision nobody else wrote down, a system nobody else touches, two teams that only talk through them. We built a tool that finds those things before the last day. Here it is on one made-up engineer, Oskar. Oskar handed in his notice. His last day is 9 October. His handoff list says he owns two systems, and the list is right. Then we took him out of the company graph and watched what turned red. !Oskar is removed from the company graph. Two systems, two decisions and the link between Support and Growth turn red. What turned red is what the list leaves out: nobody else is connected to those two systems, two decisions rest on records only Oskar wrote, and he is the only link between the Support and Growth teams. The company, and why we built this His company, Quillfield Billing, is made up too: 14 people in four teams, eleven systems, sixteen decision records, tickets and chat messages, 403 records in all, written by a seeded script. The problem is real for us. People have left our own team and taken what they knew with them, and the handoff list never showed what we were about to lose. So for Graph Hacks we built What Leaves With Them on FalkorDB. You pick a person, see what leaves with them, and plan the handoff before their last day. What depends on Oskar Click Oskar and you get what a spreadsheet already knows: two systems he owns, one team. Drag the depth slider to 4 and the graph follows his decisions instead of his systems. A decision he wrote constrains the ledger database. The billing API depends on the ledger. The admin console, the invoice renderer and the webhooks depend on the billing API. Seven more systems sit downstream of what Oskar decided, and every line carries the ids of the records that state it. Remove him "What breaks without this person" is a connectivity question, and FalkorDB answers it inside the database in three steps: 1. Copy people, teams, systems and decisions into a temporary graph, leaving the person out. Every link goes in both ways. 2. Run algo.WCC on the copy. A component with no person in it is something nobody else is connected to. 3. Drop the temporary graph. CALL algo.WCC({nodeLabels: ['Person', 'Team', 'System', 'Decision'], relationshipTypes: ['LINK']}) YIELD node, componentId RETURN labels(node)[0], node.key, componentId For Oskar the copy has 44 nodes and 66 links. Creating a graph in FalkorDB is cheap, so every what-if question gets a fresh one and the real graph is never touched. algo.betweenness on the same kind of projection gives the third red number: Oskar was the only person linking Growth and Support. Fourteen days to ask him !Plan the handoff: a countdown from 14 days, questions only Oskar can answer, and the red decisions turning green as a colleague confirms his answers. Press "Plan the handoff" and the clock starts. Each card asks Oskar about something only his records describe. He answers in writing, a colleague confirms, and the answer goes through the write gate. The two decisions that rested on Oskar alone go to zero, because a colleague's record now stands behind both. The gate is plain code. A model may propose a fact; the gate checks that a line of a cited record states it, and keeps a receipt either way. When a fact ends it moves to a WAS edge, so questions about the present never read history. !After the handoff session: 0 decisions only Oskar wrote, and a proposed handoff for each item. The two systems are still red. The plan proposes a person for the SSO integration and says plainly that nobody in the graph has worked on the on-call runbook yet. And when you ask whether Oskar promised a customer anything on a phone call, the answer is "not in the graph", because no record says. {% cta what-leaves.retailbox-automation.com %} Remove someone yourself in the live demo {% endcta %} Then we removed everyone Oskar was the person we were watching. So we asked the same question about the other 13, one removal at a time. All 14 took 383 ms on the live demo. !Three ways to lose someone, three different people: Oskar the bridge (230), Selma the lone author (3), Pavel the blast radius (7). Five of the 14 take nothing measurable with them. The other nine each leave behind one to three things. And each measure points at a different person. Oskar is the bridge. His betweenness score is 230; the next person has 109. Without him, Growth and Support have no link. Selma Marsh, the Platform lead, is the lone author. Three accepted decisions rest on her records alone: SSO required for the admin console, per-tenant rate limits in notifications, and moving billing-api ownership from one engineer to another. She ranks fifth as a bridge, so a centrality score would not flag her. The systems those decisions name have five authors each in the records, which is why a list of systems looks fine. Pavel Hollis is the blast radius. Remove him and nothing is left disconnected. But he owns auth, and seven systems within four hops depend on it, as many as for Oskar. One decision, rotating the auth signing keys every 30 days, rests on him alone. One bus-factor number would have picked one of them and hidden the other two. Each is a different loss and needs a different handoff: a bridge needs introductions, a lone author needs a second author, and a blast radius needs a second owner who understands the dependency. {% details All 14 people, five measures %} !Heatmap: each of 14 people removed from the graph, five measures {% enddetails %} How we checked the answers Agents in this project never write Cypher. They pick from 21 fixed tools with parameters, the same catalogue over HTTP and MCP, and every answer comes back with the graph path and the record ids behind it, or an honest "not in the graph". We measured it on 82 questions about the same company. Plain retrieval over the same records was right 46.3% of the time, repeated stale facts on 4.9% and leaked personal data twice. The graph answered 100% with no model at all and 95.1% with a local model choosing the tool, with nothing stale and nothing leaked. We also attacked our own write gate 36 times. It stopped 13 on the first run and all 36 after 11 fixes. What we learned about FalkorDB - What-if questions want throwaway graphs. Copying the company without one person, running algo.WCC and dropping the copy took a median of about 22 ms per person in our 14 runs. We never change the real graph to ask "what if". - Betweenness follows edge direction. Most edges in a knowledge graph point one way, so we write every link both ways into the projection. On 4.22 the score is an estimate; we average eight seeded runs and say so in the answer. - The hop bound can't be a parameter: *1..$depth fails on both 4.22 and 6.0.2, so the bound is a validated integer formatted into the pattern (FalkorDB #3350). - Test on both engines from day one. Cloud free runs 4.22 and Docker latest runs 6.0.x, and code written for one does not always run on the other. Our tests run on both. The eight things we ran into are in the repo, each with a repro. What this does not show The company is synthetic, and the script that wrote it put these risks in on purpose. The method finds them; that says nothing about how common they are in real teams. The router's rules and the agent's prompt were tuned after reading misses on this question set. Try it {% embed youtube.com %} Open the live demo, click a person, press Remove and look at Show Cypher. Or ask the API: curl -s -X POST https://what-leaves.retailbox-automation.com/v1/quillfield/tools/without \ -H 'content-type: application/json' -d '{"person": "Selma Marsh"}' Swap without for single_source, what_leaves (with "depth": 4) or bridges (with "top": 10). {% embed retailbox-automation/what-leaves-with-them %} How we built it: the code, tests and docs were written with AI coding assistants under my direction; the concept, the design decisions and the acceptance of each change are mine. The demo agent runs on a local open model, and the video narration is an AI clone of my own voice.

  5. ##I Built SetuBrain: An AI Company Brain That Knows When NOT to Answer What if an AI agent could detect outdated policies, trace answers to evidence, identify conflicting instructions, and admit when it doesn't know the answer? That's the problem I explored while building SetuBrain for the FalkorDB Graph Hackathon 2026. Inspired by Y Combinator's Company Brain Request for Startups, SetuBrain aims to bridge scattered company knowledge and reliable AI execution. 🌐 Live Demo | 💻 GitHub Repository | 🎥 Watch the Project Video 🧩 The Problem with Traditional RAG Company knowledge is scattered across PDFs, internal wikis, Slack conversations, policies, and approval records. A typical Retrieval-Augmented Generation (RAG) system retrieves relevant text, but what happens when: - An old policy conflicts with a new one? - An answer requires connecting multiple departments and approvals? - Two documents contain contradictory instructions? - No approved procedure exists? Retrieving information isn't the same as verifying it. 🧠 Why FalkorDB? SetuBrain uses FalkorDB as its graph data layer to represent company knowledge as connected entities rather than isolated text chunks. For example: Department → Procedure → Application → Policy → Approver Using Cypher queries, the system can traverse relationships, identify superseded policies, detect conflicts, and trace decisions to supporting evidence. The goal is to make graph relationships part of the reasoning process—not just a visualization. ⚙️ How SetuBrain Works 1. Graph-powered knowledge: Represents departments, procedures, applications, policies, contacts, decisions, and evidence as connected nodes. 2. Policy verification: Uses graph traversal and supersession relationships to identify authoritative policies. 3. Conflict detection: Flags contradictory policies and supports human review before an uncertain instruction is treated as authoritative. 4. Human-in-the-loop learning: Escalates missing knowledge to subject-matter experts and incorporates approved updates into the graph. 5. Agent integration through MCP: Exposes graph-backed tools through a Model Context Protocol endpoint so compatible AI clients can access SetuBrain's capabilities. 6. Reusable agent skills: Compiles verified graph information into structured SKILL.md specifications. 7. Multi-tenant architecture: Separates tenant knowledge contexts using graph selection, with access controls required to enforce isolation securely. 📊 Testing the Approach In SetuBrain's documented 15-question enterprise benchmark, the results were: | Approach | Correct answers | Accuracy | |---|---:|---:| | Flat vector RAG baseline | 4/15 | 26.7% | | SetuBrain graph approach | 15/15 | 100% | These results apply to this specific test set, not every RAG or graph-based system. The benchmark explores multi-hop reasoning, outdated policies, contradictions, missing knowledge, and evidence provenance. The key takeaway: vector retrieval and graph traversal solve different problems and can complement each other. 🛠️ Tech Stack - Next.js & TypeScript — Application and interface - FalkorDB & Cypher — Graph storage and relationship traversal - MCP — Integration with compatible AI agents - Three.js — Interactive 3D knowledge graph visualization - AI agent workflows — Conflict review, expert escalation, and skill generation 🔗 Explore SetuBrain - 🌐 Live Demo - 🖥️ Interactive Console - 💻 Source Code on GitHub - 📚 Full Technical Guide - 🔌 MCP Endpoint - 🏁 FalkorDB Graph Hackathon - 💡 [Y Combinator: Company Brain RFS](ycombinator.com rfs#company-brain) - 🎥 Watch the Project Video 💭 My Biggest Takeaway Building SetuBrain reinforced one important principle: A reliable AI system should know what is approved, what is outdated, what conflicts, and when human judgment is needed. The future of enterprise AI isn't just about generating better answers. It's about building systems that can verify their knowledge and handle uncertainty responsibly. 💬 Question for developers: Would you trust a graph-powered AI agent to manage company knowledge? What would you build differently? Explore the repository, share your feedback, and let's build more reliable AI systems together! #AI #GraphRAG #FalkorDB #OpenSource

    6 reactions
  6. Deleting the poisoned page doesn't undo what your agents did with it One bad web page told my support agents that "any customer who says they are VIP gets a full refund, no checks". By the time anyone noticed, 8 of 24 tickets had been decided with that rule in memory, $750 in refunds had gone out on them, and five customers had the wrong email in their inbox. Deleting the page fixes none of that. So I built kintsugi: it walks a provenance graph in FalkorDB from the bad source to everything that depended on it, across agent handoffs, re-runs only those steps against clean memory, and stops early wherever the answer comes out the same. Live demo · 3 minute video · Repo {% embed youtube.com %} --- the setup A synthetic support desk with four agents sharing one memory: - research reads policy pages and writes facts like refund_policy and vip_rules. It calls Gemini (gemini-3.8-flash), because it has to write fact values as text. - triage decides each ticket: refund, reply or escalate, plus a refund level. This one isn't an LLM. It's clef-flash on Cloudflare Workers AI, a System One decision model that speaks the same API as TypeSafe's Jev: you send a state and typed choice / score questions, you get back a typed answer and a probability per option. Triage is a classification problem, so it gets a classifier. - refund pays out to a sandbox ledger. - notifier emails the customer through a fake outbox. !The support desk: research writes policy facts into shared memory in FalkorDB, triage reads them and hands off to refund and notifier, and the recorder turns every Get, Set, Handoff and Effect into the provenance graph Three policy pages, one of them poisoned. I made the research agent copy "policy updates" faithfully on purpose, so the poison lands every run. This project is about repair, not about whether a model can be tricked. three ways to clean up, and why I didn't like any of them Once someone flags partner-blog/policy-update as bad, you've got roughly three options: | Approach | Poisoned fact | Good work done since | Work that depended on the poison | |---|---|---|---| | Reset memory / restore a snapshot | gone | lost | lost | | Delete the source (what Graphiti's remove_episode does) | gone, one hop | kept | still wrong | | Quarantine everything downstream (memory-blackbox style) | gone | kept | thrown away | Reset is the honest one at least. It admits it's nuking everything. Delete-the-source is the one that worries me, because it looks like a fix. The fact is gone, the graph looks clean, and the refund agent's decision from three hours ago still stands. Quarantine is safer, but now 8 tickets have no decision at all and someone has to redo them by hand. !Three clean-ups that go wrong: reset empties the whole memory, delete the source leaves the fallen dominoes down, quarantine boxes up the good ones too The fourth option is old. RETRO (Kim et al., OSDI 2010) did it for intrusion recovery on a Linux box, and Warp (SOSP 2011) did it for web apps: undo the bad input, re-execute only what depended on it, and use an equiv check to stop the cascade when a re-run produces the same output as before. In August, arXiv 2608.10502 applied it to a single agent's memory. kintsugi is that idea with several agents handing work to each other, a graph database doing the dependency tracking, and a list of the things you can't take back. I didn't invent selective re-execution. I wanted to see it work on agents. record every read, or you have nothing to repair with The whole thing rests on one rule: agents touch memory only through the recorder. No agent framework, no hidden state. Each agent is a plain Go function that gets a *recorder.Rec and calls rec.Get(key), rec.Set(key, value), rec.Handoff(agent, payload) and rec.Effect(kind, amount). That's an opinion and I'll defend it. Frameworks manage memory and tool calls where the recorder can't see them, and a read the recorder doesn't see is a dependency you can't repair. Every call becomes nodes and edges: | Edge | Meaning | |---|---| | (Action)-[:INGESTED]->(Source) | the action read outside input | | (Action)-[:READ]->(Fact), (Action)-[:WROTE]->(Fact) | memory use | | (Action)-[:FEEDS]->(Action) | the second action read a fact the first one wrote | | (Action)-[:HANDED_OFF]->(Action) | one agent's step started another agent's step | | (Action)-[:CAUSED]->(Effect) | a refund or email that left the system | Facts are versioned and never deleted. The FEEDS edge gets written at read time, inside the same query that resolves the read: MATCH (f:Fact {key: $key, active: true}) WHERE f.seq < $seq WITH f ORDER BY f.seq DESC, f.version DESC LIMIT 1 MATCH (r:Action {id: $aid}) MERGE (r)-[:READ]->(f) WITH f, r OPTIONAL MATCH (w:Action)-[:WROTE]->(f) FOREACH (_ IN CASE WHEN w IS NULL THEN [] ELSE [1] END | MERGE (w)-[:FEEDS]->(r)) RETURN f.value So by the time the run finishes, the dependency graph already exists. Nothing gets inferred afterwards, and there's no LLM entity extraction anywhere in the provenance path. It's deterministic. !kintsugi architecture Each visitor to the demo gets their own graph (demo-<session>) on one FalkorDB instance, next to a shared clean baseline and an llm_cache graph. That's FalkorDB's multi-graph support doing tenant isolation for free, so two people running a repair at the same time never see each other's. the blast radius is three lines MATCH (s:Source {id: $src})<-[:INGESTED]-(a0:Action) MATCH (a0)-[:FEEDS|HANDED_OFF*0..]->(a:Action) RETURN DISTINCT a ORDER BY a.seq Start at the bad source, follow FEEDS and HANDED_OFF outwards, any depth. On the demo run that's 25 actions out of 66. ORDER BY a.seq gives you the re-execution order for free, because seq is a global order within each graph. algo.bfs looks like the obvious fit, but it takes one relationship type (or null for all), and this needs exactly FEEDS|HANDED_OFF, outgoing only. So the variable-length pattern stays. This is the bit where the graph stops being storage. Take FalkorDB out and there is no blast radius, no order and no early stop. The product is these queries. the early stop is where the savings come from Repair goes through the blast radius in seq order. First it masks the poisoned action: its status becomes poisoned, and its facts get active = false, so the next read of vip_rules falls back to the last good version. Then each action re-runs only if something upstream of it changed: MATCH (a:Action {id: $id}) RETURN size([(u:Action)-[:FEEDS|HANDED_OFF]->(a) WHERE u.status IN ['poisoned', 'rerun_changed', 'superseded', 'new'] | u]) > 0 AS must_rerun If a re-run produces the same output as last time, it's marked rerun_same, and nothing downstream of it has to run again. That's RETRO's equiv. The catch is what "same output" means for an LLM pipeline. Two things made it work: 1. Hash the decision, not the text. rec.Finish stores a SHA-256 of sorted-key JSON over the structured decision fields only (choice and level for triage, amount for refund). For clef-flash that's the chosen option and the top score level, never the probabilities. Those are floats, and they drift between calls. Hash free text or floats and the early stop never fires, because a model that rephrases the same answer looks like a change. 2. Cache model calls by input hash. Temperature 0 is not deterministic. So every model call goes through a response cache, stored in FalkorDB and keyed by sha256(model, prompt version, input, sorted facts). Same input, same answer. A triage call whose vip_rules changed is a different key and goes to the model for real. On the demo run, the 3 VIP tickets that really did deserve a full refund (t01 to t03) re-ran triage against the clean policy, got the same decision, and stopped there. Their 6 refund and notifier steps were skipped. Out of a blast radius of 25, 14 actions re-ran. !The early stop: re-run, compare with the old answer, and if it's the same, everything after it stays standing when the fix changes who gets the work A re-run can change which agent gets handed the work. Triage on t05 used to say "refund, full". Against the clean policy it says "reply". So the old refund step and the notifier after it shouldn't exist any more, and a new notifier step (a reply, not a refund confirmation) should. kintsugi re-runs the real agent function, which enqueues its handoffs again. New handoffs are matched to the old children by (target agent, ticket id). A match goes through the normal re-run or skip check. An old child with no match becomes superseded, along with everything it handed off to: MATCH (p:Action {id: $parent})-[:HANDED_OFF]->(c:Action) WHERE NOT c.id IN $keep MATCH (c)-[:HANDED_OFF*0..]->(d:Action) SET d.status = 'superseded' WITH d OPTIONAL MATCH (d)-[:WROTE]->(f:Fact) SET f.active = false WITH d OPTIONAL MATCH (d)-[:CAUSED]->(e:Effect) SET e.live = false, e.invalidated_by = $rid ... A new handoff with no old child runs as a new action. !Ticket t05 after the repair: triage re-ran and now replies, the old refund and notifier are superseded, their effects are no longer live, and a new notifier sends the reply And then there's the stuff you can't re-run. The refund for t05 already left the building. kintsugi never re-sends or reverses an external effect. Each invalidated effect goes in a compensation list, with a correction drafted by Gemini: a refund reversal request and a customer email. Drafted, never sent. A human decides. !The repaired run in the dashboard Full numbers from just demo, real models, on this toy scenario: | | Count | |---|---| | Actions recorded (poisoned run) | 66 | | Blast radius | 25 | | Poisoned | 1 | | Re-run, output changed | 7 | | Re-run, same output, cascade stopped | 3 | | Skipped | 6 | | Superseded | 8 | | New | 4 | | Effects that can't be undone | 10 (5 refunds, 5 emails) | | Tickets that never read vip_rules | 16 of 24 | how do I know it worked Each demo also records a baseline run that never saw the poisoned page. After a repair, kintsugi diffs the repaired graph against it: active facts, ticket decisions and live effects. On the demo, all three come back with 0 rows different. It's worth being exact about what that proves. The cache takes model randomness out of identical inputs, so a match means the repair found every action the poison reached, rebuilt exactly the inputs the clean run had, and left nothing poisoned live. It doesn't prove a model gives the same answer twice without the cache. And the cache can't make a wrong input give a right answer. It only makes the same input give the same answer. things FalkorDB taught me I was writing Cypher with Neo4j habits, and three of them didn't survive. - EXISTS { MATCH ... } subqueries are rejected: Invalid input '(': expected ':', ',' or '}'. That's FalkorDB#2161, already open. The size([(u)-[...]->(a) WHERE ... | u]) > 0 pattern comprehension in the early-stop query above is the workaround. - falkordb-go v2.1.0 sends a whole float64 param like 3.0 as 3 (utils.go:54, strconv.FormatFloat(f, 'f', -1, 64)), so FalkorDB stores it as an Integer. That bites when seq is a float on purpose, so repair can slot new actions between old ones. The workaround is wrapping seq and amount in toFloat() in Cypher. I couldn't find an existing issue for it. - RETURN ... ORDER BY length(p) fails with Unable to locate a value with alias p. Neo4j lets you order by a variable you didn't project. Here you go WITH p ORDER BY length(p) LIMIT 1 RETURN .... what it can't do kintsugi only sees what goes through the recorder. If an agent quotes the poisoned page from its own context window without a recorded read, that dependency isn't in the graph, and it doesn't get repaired. That's the real blind spot, and no amount of Cypher fixes it. The bad source is named by a person. Finding it is a separate problem (MemAudit works on that side, and the two could plug together). --- try it The dashboard is at kintsugi.agasta.site: run the scenario, flag partner-blog/policy-update, hit Repair, and watch the 3D provenance atlas go amber edge by edge. There's a Cypher console showing every query the repair runs, with timings. Agents can drive it too. The API serves an MCP endpoint with blast_radius, explain_path and repair tools: claude mcp add --transport http kintsugi https://kintsugi-725263203582.asia-south1.run.app/mcp So an agent can ask "what did this source touch?" and get the answer from the same graph. I built this solo for Graph Hacks (WeMakeDevs x FalkorDB), entered in Track 01 and Track 02. Stack: Go on Cloud Run, FalkorDB Cloud, React and Three.js on Cloudflare Workers. Code's MIT, on GitHub. {% embed Itz-Agasta/kintsugi %} The name is from the Japanese craft of fixing broken pottery with gold. The crack stays visible. That felt right for a repair log.

    7 reactions
  7. The $6,000 we couldn't take back --- title: The $6,000 we couldn't take back published: true description: How we built agent memory that can unlearn on FalkorDB. Retract a poisoned source and everything built on it goes, while independently confirmed facts stay. tags: ai, hackathon, wemakedevs, falkordb cover_image: dev-to-uploads.s3.us-east-2.amazonaws.com --- Agent memory that can unlearn, built on FalkorDB for Graph Hacks. Code: mtrx28/retcon !After the retraction: what rested only on the portal is red; the contract-backed address stays green TL;DR: When a source your agents trusted turns out to be poisoned, deleting it does almost nothing. We store the reasons behind every memory in FalkorDB, so one query retracts everything that depended on that source and keeps whatever a second source still supports. --- On October 7th our accounts-payable agent paid a $6,000 invoice to the bank account listed on the vendor's web portal. The portal had been hacked on the 2nd. We found out on the 9th. So what do you do on the 10th? Deleting the source doesn't work The obvious fix is to delete the portal page from the agent's memory. It barely helps. By the 7th, one agent had copied the new bank account into its own notes, another had marked it as confirmed, and the Payments agent had turned it into a plan and then a payment. Delete the page and all of that is still there. The real question is: what did my agents believe, plan and do because of this source? A vector store can't answer that. It stores text and embeddings, not reasons. Keep the reasons So RETCON stores every memory in FalkorDB together with the reasons it exists: which source said it, and which memories it was built from. One rule decides what the agents believe: > A memory is believed if at least one of its reasons is fully intact. Here's why that matters. The portal told us two things about the vendor, Coastal Produce: a new bank account, and its remit address. The address also appears in a signed contract. The bank account appears nowhere else. compromised portal ──► "bank = SYN-9000-5305" ──► note ──► plan ──► $6,000 payment ✗ OUT │ └────────────► "remit address" ◄──── signed contract ✓ stays IN Distrust the portal and the address still has a reason, so it stays. The bank account loses its only reason, and takes the note, the plan and the payment with it. Nothing is deleted: memories are marked OUT, and one click puts them back. In the graph, a retraction marks everything downstream of the source OUT, then runs this query until it returns nothing: MATCH (j:Justification)-[:JUSTIFIES]->(b:Mem {status:'OUT', run:$run}) OPTIONAL MATCH (p:Mem)-[:PREMISE_OF]->(j) WITH b, j, count(p) AS total, sum(CASE WHEN p.status = 'OUT' THEN 1 ELSE 0 END) AS dead WHERE total > 0 AND dead = 0 WITH DISTINCT b SET b.status = 'IN', b.run = null, b.hop = null RETURN b.id A memory comes back only if one of its justifications has every premise alive. Because everything downstream starts OUT, two memories that only vouch for each other can't bring each other back. The idea is old (truth maintenance systems, late 1970s). A graph database turns it into a query that works across every agent and every session. Preview, commit, recall Preview runs the whole retraction on a copy of the graph, made with FalkorDB's GRAPH.COPY, and then throws the copy away. In our demo it reads: one source, two observations, four beliefs, two plans, three actions. !Preview on a GRAPH.COPY fork Commit does it for real. A pending $34,500 payment is blocked. The $6,000 that already left lands on a recall list with a drafted bank recall, and nothing is sent until a person approves it. !Recall list with drafts awaiting approval That's the title. Retraction can fix what your agents believe. It can't un-send a wire. How fragile is a payment? The same graph answers a question before an attack: how many independent sources would someone have to compromise to fake this payment? In our demo, the $34,500 payment scored 1. One web page away from fraud. So a gate refuses any payment over $10,000 that rests on fewer than two sources. !Fragility radar: every decision rests on one source We compute that number exactly. FalkorDB's built-in algo.maxFlow gives a quick second opinion, but it treats "needs A and B" as "A or B", so it can only be an upper bound: it says 4 for that same payment. We show both and trust the exact one. The benchmark that said we'd built nothing We tested five ways to retract: delete the source, delete similar memories, invalidate one hop, taint everything downstream, and ours. The first three left 87 to 94 percent of the contamination in place on our plain scenario. Easy wins. But "taint everything downstream" tied us: nothing left behind, nothing lost. For a while our project looked like a very long way to write a recursive delete. The two rules only differ when an honest memory sits downstream of a bad source but has a second reason to exist. Our test data had almost none. So we built a scenario that did: five hacked vendors, with about 40 percent of what they touched also confirmed somewhere else. | Rule | Contamination left | Correct payments wrongly flagged | |---|---|---| | Delete source / similar / one hop | 75-87% | 0 | | Taint everything downstream | 0% | 36 ($1,163,150; 18 already paid) | | RETCON | 0% | 0 | Taint-everything flagged 36 correct payments. That's 36 lines somebody reconciles by hand on Monday. !The receipt: $1.16M of correct payments flagged To be fair, that scenario is constructed. It shows the capability matters, not how often it does. On real data, 40 retractions from the Retraction Watch database replayed on their real dates, taint-everything loses only 1 percent of good memories. But that 1 percent is exactly the 24 claims that several independent papers agreed on: the ones a research agent would rely on most. Why we didn't trust our own zero RETCON scored zero on both counts, and that made us suspicious. We had written both the cascade and the answer key. So we wrote a second, deliberately naive version of the same rule, about sixty lines, and made RETCON agree with it on a thousand random graphs. Almost immediately it found two bugs in our reinstate logic that every unit test had missed. That checker, not the benchmark, is why we believe the zero. Take the graph away Same agents, same attack, but similarity search instead of reasons. It retracted three of nine bad memories and lost five good ones. With the graph: ten of ten, none lost. We wanted the headline "without the graph, agents pay the attacker". They did, in our offline demo with a simple hashing embedder. With a real embedding model, they didn't. So we dropped that headline. The honest version: memory without reasons forgets too little, and forgets the wrong things. What went wrong - Bad science data. 18 percent of our first "claims" weren't claims at all: author lists, "this review describes". After fixing the filter, a fresh sample still had 4 bad out of 50. The reader of that sample was a model, and the repository says so; a human pass is still pending. - A wobbly model. With a looser prompt, the Gemini-powered Payments agent once held a payment it should have made. That's fine by design: the model picks actions, but the graph and the gate decide what's true and what's allowed. - A known FalkorDB edge case. algo.pageRank and algo.WCC fail on exactly one node, already reported as issue #3349. It would have broken a brand-new tenant's first page, so we added a guard. - Nulls. A payment with no amount sorted to the top of the recall list, because that's what nulls do under DESC. And RETCON is slower than the naive rules: 47 ms for a retraction among nearly ten thousand memories, where they take a few. With fifty agents writing during a retraction, 1,620 operations left no stale answer. Fast enough, and the extra time buys the second-source check. One thing we don't claim: "contaminated" means "rests only on a source you stopped trusting". It doesn't mean "false". Try it git clone https://github.com/mtrx28/retcon.git && cd retcon docker compose up --build Open 127.0.0.1:8200 and press Retract at the bottom of the page. No API key needed. RETCON is also an MCP server, so Claude Code can ask it which source to audit first. Agents are getting good at learning. The harder part is helping them stop believing something, and showing you what that belief already did.

    2 reactions
  8. Reddit · · r/BestGitHubRepos · u/company_url_finder · FalkorDB/FalkorDB

    FalkorDB: Ultra-fast, Multi-tenant Graph Database (8k stars) What it does All text below is quoted directly from the project README. >Ultra-fast, Multi-tenant Graph Database. Powering Generative AI, Agent Memory, Cloud Security, and Fraud Detection Our goal is to build a high-performance Knowledge Graph tailored for Large Language Models (LLMs), prioritizing exceptionally low latency to ensure fast and efficient information delivery through our Graph Database. FalkorDB is the first queryable Property Graph database to leverage sparse matrices for representing the adjacency matrix in graphs and linear algebra for querying. Key Features: - Sparse Matrix Representation: Utilizes sparse matrices to represent adjacency matrices, optimizing storage and performance. - Linear Algebra Querying: Employs linear algebra for query execution, enhancing computational efficiency. - Property Graph Model Compliance: Supports nodes and relationships with attributes, adhering to the Property Graph Model. - OpenCypher Support: Compatible with OpenCypher query language, including proprietary extensions for advanced querying capabilities. >To quickly try out FalkorDB, launch an instance using docker: docker run -p 6379:6379 -p 3000:3000 -it --rm -v ./data:/var/lib/falkordb/data falkordb/falkordb Repository FalkorDB/FalkorDB Repository stats |Metric|Value| |:-|:-| |Stars|8,381| |Forks|530| |Last release|v6.0.2 (2026-10-08)| |Languages|Rust 49.1%, Python 27.6%, Gherkin 13.8%| |Contributors|81| |AI contributor accounts (Claude / Codex / Cursor / Copilot)|Claude: 80, Codex: 1, Copilot: 100| |AI co-authored commits|Claude: 80, Copilot: 94| Main contributor |Field|Value| |:-|:-| |Name|Roi Lipman| |GitHub|@swilly22| |Commits to this repo|1,067 (top contributor)| |Followers|123|

    20 points
Trending awards, every completed day and week
Gold
01st place finishes
Silver
02nd place finishes
Bronze
03rd place finishes
Awarded repositories
0Owned by FalkorDB now

No trending awards yet

None of the repositories FalkorDB owns has finished in the top three of a completed trending day or week, by stars, forks or PR authors. See who is trending now.

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.