DEV Community

MEHRAAN AMIN
MEHRAAN AMIN

Posted on

Why Standard RAG Fails at Cyber Forensics (And How I Built an AI Detective with FalkorDB & Cognee)

If you have ever worked in security or tried analyzing a cyber breach, you already know the worst part of the job isn't stopping the attacker it's reading the logs afterward.

When a company gets breached, the evidence doesn't sit nicely in one place. You get a messy folder containing a VPN log CSV, an email gateway dump, physical badge swipe records, file access logs, and maybe a couple of HR memos in PDF format.

To figure out what happened, a SOC analyst has to sit there with five spreadsheets open, manually pressing Ctrl+F to see if the IP address from a VPN login at 11:50 PM matches a file download at 11:54 PM. It takes hours, and it is super easy to miss a critical link.

So, for this hackathon, I wanted to build KoshurLock Holmes an AI-powered SOC Forensics and Incident Investigation platform where you can drop raw evidence files and ask plain-English questions to find the whole truth.

Here is the story of why standard AI tools failed at this, how I built a graph-backed engine using FalkorDB and Cognee, the nasty bugs I hit along the way, and what I learned.

The Problem: Why Vector RAG Is Terrible for Forensics
When people build "chat with your data" apps, the default move is Vector RAG. You chunk the text, create embeddings, throw them into a vector database, and fetch the top-k similar chunks when a user asks a question.

I tried thinking through how Vector RAG would handle a real forensic dataset (like our Halcyon Systems breach case), and the problem was obvious immediately: Vector RAG cannot connect dots across different files.
Imagine this attack chain:

In email_logs.csv, user pnair receives a suspicious file from an external sender.

In vpn_logs.csv, user dokafor logs in from a residential ISP in Lagos, Nigeria (198.51.100.77) at 23:50 UTC.

In file_access.csv, an account accesses design_specs_v2.pdf minutes later.

If you ask a standard RAG system, "How did the attacker get access to the design specs and which accounts were compromised?", vector search falls apart. It retrieves isolated text chunks based on keyword similarity. Worse, when it doesn't find the exact connection in one chunk, the LLM starts hallucinating.

In cybersecurity, a hallucinated IP address or a made-up timestamp makes the tool completely useless. Analysts need exact relationships and raw log citations.
The Solution: Every Log. One Graph. The Whole Truth.

Forensic investigation is naturally a graph problem. People, IP addresses, VPN sessions, devices, files, and timestamps aren't just paragraphs of text—they are nodes, and the actions between them (LOGGED_IN_FROM, ACCESSED_FILE, SENT_EMAIL) are edges.

That is why I built KoshurLock Holmes on top of FalkorDB and Cognee.
Instead of treating logs like flat text, KoshurLock ingests multi-file evidence (CSVs, PDFs, text notes) and extracts a unified Knowledge Graph stored natively
inside FalkorDB.

What KoshurLock Holmes Does:

Multi-File Evidence Ingestion: Upload raw CSV logs, emails, and PDFs into a custom case workspace.
Interactive Knowledge Graph UI: Visually inspect 150+ extracted entities (Person, Account, IP, Location, Device, File) and 900+ links. Clicking on any suspect node like dokafor or pnair immediately highlights every session, device, and file connected to them.

Automated Timeline & "The Turn": Automatically orders cross-file events chronologically and pinpoints the exact pivot moment where normal activity turned into a breach.
Plain-English Investigation Console: Ask complex questions like:
"Based on halcyon_vpn_logs.csv, which individuals performed the VPN logins, and what was the sequence of access for users like 'dokafor', 'pnair', and 'mrivera'?"
And get a complete forensic breakdown with UTC timestamps, internal vs. external geolocations, session IDs, and clickable citations back to the exact row in the CSV.
1-Click Demo Case: Not everyone has raw forensic CSVs lying around on their laptop, so I built a one-click "Northgate Financial" demo case right on the homepage so anyone can test the graph and AI investigator in 2 seconds.
How FalkorDB & Cognee Power the Engine Under the Hood
My stack consists of a Next.js / React frontend, a FastAPI (Python) backend, Cognee for structured entity/relationship extraction, and FalkorDB as the property graph database.
When a user uploads evidence files, here is what happens:
Parsing & Formatting: The backend parses raw CSV rows and PDF documents into structured forensic records with source metadata (halcyon_vpn_logs.csv, row numbers, timestamps).

Graph Extraction via Cognee: Cognee processes the records to identify domain entities and relationships and writes them directly into FalkorDB.
Sub-5ms Traversal in FalkorDB: Once the graph is inside FalkorDB, our UI queries it to render the 3D/2D force graph, build the timeline, and run multi-hop graph completion for the Investigation Console.
When I opened the local FalkorDB Browser (localhost:8080) during testing, seeing 95+ nodes and nearly 300 edges being traversed with a response time of 5.09ms was honestly wild. Even when doing multi-hop lookups across 8 different evidence files, the database latency was basically zero.
Real Talk: The Bugs That Almost Broke Me (My Learnings)

Building this wasn't smooth sailing. If you are building with GraphRAG and LLMs on tabular data, here are the three biggest lessons I learned the hard way:

  1. The max_tokens: 16384 Crash on CSV Files Halfway through testing the Halcyon Systems dataset, my ingestion pipeline kept failing on halcyon_vpn_logs.csv and halcyon_file_access.csv, while smaller PDF files worked fine.

When I dug into the backend logs, I saw that passing a 25-row CSV file as a single document block caused the structured output prompt to explode. The LLM tried to generate a massive JSON object containing every single entity and relationship for all 25 rows at once, hitting the 16,384 output token ceiling and returning truncated, broken JSON.

The Fix: I rewrote ingest.py to batch CSV evidence into smaller chunks (5 rows per batch) while keeping the CSV headers intact on every batch. That way, each LLM extraction call stayed tiny, never hit the token limit, and FalkorDB still merged the shared entities (like user dokafor) into the same graph seamlessly.

  1. Cloud Deployment vs. Local Docker Resources
    Running FalkorDB, FastAPI, Cognee, and Next.js locally via docker compose up was super smooth—except my laptop fan sounded like a jet engine!
    When deploying the live production build to Railway, I also had to make sure that if a judge uploads 8 files at once, one heavy LLM call or rate-limit hiccup doesn't crash the whole request. I added isolated per-batch fallback handling so every file gets indexed reliably even under strict API rate limits.

  2. Visual Debugging on Port 8080 Is a Lifesaver
    One of the best things about using the falkordb/falkordb Docker image was getting the built-in FalkorDB web UI on localhost:8080 alongside Redis/FalkorDB on 6379. Whenever I wasn't sure if an entity was being linked properly, I didn't have to guess—I just opened localhost:8080, looked at the native graph nodes and property keys, and verified the edges myself.
    Try It Out Yourself!
    Whether you want to test the pre-loaded Northgate Financial breach or upload your own CSV/PDF logs to see FalkorDB and Cognee in action, check out the links below:

Live App (Railway): https://koshurlock-production.up.railway.app

GitHub Repository: https://github.com/CodeWithMehru/KoshurLock

Video Demo (YouTube): https://youtu.be/S549w7txR9E?si=d-U9On6cFftpGkpU

Every log. One graph. The whole truth. Thanks for reading!

Top comments (0)