Plumbline: A Graph-Powered Agent for Supply-Chain Incident Response
During a software supply-chain incident, the first question is rarely just “is this package vulnerable?”
The question is more operational:
Which of our services does the poisoned package reach, and how?
That is the problem we built Plumbline to solve for Graph Hacks: Context for AI Agents, the online hackathon by WeMakeDevs and FalkorDB.
Plumbline is an open-source supply-chain triage agent. It follows dependency paths in FalkorDB, produces evidence-backed EXPOSED, CLEARED, or UNKNOWN verdicts, remembers the assumptions behind human decisions, and reopens only the decisions affected by new evidence.
This is not an LLM guessing whether a package is dangerous. The graph supplies the evidence. Deterministic policy produces the verdict. An agent can select tools and explain the result, but it cannot invent or override the security decision.
The complete project is available in the Plumbline GitHub repository.
Landing page
Plumbline is live at soumya-narang.github.io/Plumbline
Demo video
Plumbline's demo video could be found here, it has been recorded by our lead developer Soumya Narang her-self.
What we built
Plumbline has five connected parts:
- An evidence graph: FalkorDB stores repositories, lockfiles, package versions, dependency edges, advisories, decisions, facts, receipts, tasks, and playbooks.
- A deterministic verdict policy: plain, unit-tested code decides whether a repository is exposed, cleared relative to stated assumptions, or unknown because evidence is missing.
- An MCP agent: the agent can triage advisories, explain paths, find the best first fix, check reopened decisions, and coordinate remediation work.
- Decision memory and replay: approvals point to the facts they assumed. When a fact changes, only the affected decisions reopen.
- A working UI and lockfile checker: people can replay the incident, inspect the graph, view receipts, and check a supported npm lockfile in an isolated graph.
The project uses FalkorDB as a central part of the product, not as a visualisation layer. Remove the graph and the core workflow disappears: there is no path-backed verdict, no assumption traversal, no graph-based fix ranking, no shared work queue, and no per-upload graph isolation.
The incident we replayed
For the demo, we replayed a real npm supply-chain incident from 4 August 2026. A compromised maintainer account published trojanized releases across the keyv, cache-manager, and @cacheable/* package families within 25 minutes.
The replay begins with these releases:
-
cache-manager@7.2.10at 11:12 UTC -
keyv@6.0.0at 11:19 UTC -
@cacheable/node-cache@3.1.2at 11:34 UTC -
@cacheable/memory@2.2.1at 11:35 UTC -
@cacheable/utils@2.5.1at 11:36 UTC -
@cacheable/net@2.1.1at 11:37 UTC
We selected this incident because the tight publication burst makes a timeline meaningful, and because transitive dependencies such as keyv are easy to miss when teams inspect only direct dependencies.
The graph contains 18 repositories, 12,370 locked packages, 22,675 dependency edges, and six advisories. It includes real public repositories pinned before the first malicious release as well as clearly labelled synthetic fixtures. The fixtures allow the demo to show an exposed service without accusing a real project of using a malicious package.
Why a graph instead of text retrieval?
A lockfile is already a graph, even when it is stored as JSON.
A repository locks packages. Packages require other packages. Advisories affect package versions. Services run repositories. The incident question depends on following those relationships:
Service -> repository artifact -> locked dependency -> transitive dependency -> malicious package version In a text-retrieval system, the agent may receive several relevant-looking package records and still have to infer the joins. In Plumbline, the relationships are first-class data. The answer is a traversal, and the traversal is also the explanation.
Our core graph model is:
(Service)-[:RUNS]->(Artifact)-[:LOCKS]->(LockedDep) (LockedDep)-[:REQUIRES]->(LockedDep) (LockedDep)-[:IS]->(PackageVersion)<-[:AFFECTS]-(Advisory) (Decision)-[:ASSUMES]->(Fact) (Receipt)-[:PROVES]->(Decision) (Task)-[:USED]->(Playbook) The key Cypher operations include finding repositories that lock an affected version, finding the shortest dependency path, finding decisions whose assumptions have been contradicted, ranking direct dependencies by how much exposure they can remove, ranking critical dependencies with PageRank, and atomically claiming remediation tasks.
Three verdicts instead of one confident guess
Plumbline produces one of three verdicts:
- EXPOSED: the repository locks a version named by the advisory. The result includes the evidence, shortest dependency path, direct dependency to bump, and a receipt.
- CLEARED: the repository does not match the affected versions, but only relative to explicit assumptions such as “the lockfile matches what is deployed.”
- UNKNOWN: the evidence is insufficient, for example when a repository has no supported lockfile.
UNKNOWN is deliberately not treated as safe. A missing lockfile does not prove that a service is clear. Plumbline says what evidence is missing and what would settle the question.
This is one of our central design decisions: an agent should make uncertainty visible rather than hide it behind a confidence score.
Receipts make every result inspectable
Each verdict has a receipt containing the verdict, evidence, dependency path, assumptions, and policy version. The receipt is hashed and can be re-derived from the graph.
Verification checks the hash first. It then asks the graph to calculate the verdict again. This catches both a modified stored result and a receipt whose hash was recomputed around inconsistent evidence.
A receipt is not a digital signature, and it cannot prove that a lockfile matches production. It proves something narrower and useful: the result is consistent with the stated evidence and the current graph.
In the UI, a judge can open an exposed repository, see the path, inspect the assumptions, view the receipt hash, and see the Cypher query that found the path. The answer is not just a badge.
Memory that knows what changed
Incident response is not a single query. Facts change while people are making decisions.
Suppose a team approves a service as cleared because the advisory does not name the version it uses. Later, the advisory is revised, or a new fact shows that the service is running a different lockfile. Without decision memory, someone has to rediscover which approvals need review.
In Plumbline, a decision points to the facts it assumes:
Decision -[:ASSUMES]-> Fact When a fact changes, one graph traversal finds the decisions attached to that fact. Only those decisions reopen. Unrelated decisions stay closed.
That gives Plumbline three kinds of memory:
- Episodic: who approved what and when.
- Semantic: the facts and assumptions behind the decision.
- Procedural: playbooks recalled when a similar remediation task is handed off.
The recorded replay includes five approvals and one clearly labelled hypothetical advisory revision. The revision reopens exactly the decisions that depended on the changed assumption. A visitor can also create a private what-if revision without changing the recorded incident.
The agent acts through MCP tools
Plumbline exposes an MCP server with tools for listing advisories, triaging an advisory across the fleet, explaining a dependency path, checking reopened decisions, finding the direct dependencies that would clear the most exposure, ranking critical dependencies with PageRank, handing fixes to a shared work queue, and claiming or completing remediation tasks.
The default backend is a deterministic rule-based planner. An optional language-model backend can choose tools and phrase answers, but it cannot create or change a verdict. If the model times out, returns malformed arguments, or answers without using evidence tools, the planner takes over and says why.
Approving a decision is deliberately not an agent tool. Only a person can approve it through the site.
The lockfile checker
The checker accepts npm package-lock.json or npm-shrinkwrap.json files using lockfile version 2 or 3. It reads the lockfile structure without installing or executing packages.
Each upload becomes its own isolated FalkorDB graph. The same graph queries and the same policy judge it. For an exposed path, the checker shows the affected package, direct dependency, complete path, assumptions, and graph operations used to find it.
This is intentionally not a general vulnerability database. It checks the six advisories in the replay and any bad versions the user explicitly supplies. Yarn, pnpm, and npm v1 lockfiles are refused with an explanation rather than silently misread.
What we measured
We benchmarked dependency lookups against npm ls --package-lock-only --offline as an independent oracle. Ten repositories could be graded; others were excluded because they used workspaces, lacked a lockfile, or used an unsupported lockfile format.
For the graded questions, FalkorDB reached 100% on whether an artifact locks a package version, which direct dependencies pull in a package version, and which artifacts across the fleet lock a package version.
A tuned SQLite implementation also reached 100% and was faster for these small lookups. The argument for FalkorDB is not that every lookup is faster. The argument is that evidence, decision memory, graph algorithms, shared work, and tenant isolation live in one connected model.
The benchmark also caught a real parser bug: peer dependencies were being treated as installs and could produce the wrong package to bump. We fixed it and added regression coverage.
Why Plumbline fits Graph Hacks
The official Graph Hacks brief asks for an agent whose context lives in a graph, acts on connected data, remembers across sessions, shares state, and shows the path behind an answer. The rules require FalkorDB to be the primary graph database and to do real work in the product.
Track 01: Agents That Act on Connected Data
The agent performs multi-hop security triage. It uses shortest-path queries, fleet-wide fix ranking, PageRank, and MCP tool selection. Its actions are based on what the graph shows, and each verdict comes with an inspectable path and receipt.
Track 02: Agent Memory and Coordination
Approvals are persistent decisions. Assumptions are semantic memory. Playbooks are procedural memory. Triage agents hand work to remediation agents through a graph-backed queue. Task claiming is atomic, and the project tests concurrent agents racing for work. Uploaded projects get separate graphs, while visitor decisions and what-ifs remain isolated per session.
The project is not entering Track 03, Company Brain. Its graph models software supply-chain evidence and incident decisions rather than company people, documents, and teams.
How to evaluate the project in five minutes
- Open the repository and read the README’s graph model and setup instructions.
- Run the local stack with Docker and load the committed processed data.
- Open the replay and move the timeline to the
keyv@6.0.0release. - Open an exposed synthetic fixture and inspect its shortest path, assumptions, receipt, and fix suggestion.
- Try the hypothetical advisory revision and watch only the dependent decision reopen.
- Run
handoff-demoto see concurrent remediation agents claim work exactly once. - Run the benchmark and tests to inspect the measured results and safety checks.
The repository also contains the named Cypher queries, API, MCP server, data pipeline, deployment files, 209 automated tests, benchmark, and deployment instructions.
Innovation, technical implementation, and presentation
The public Graph Hacks judging criteria are Innovation, Technical Implementation, and Presentation. The hackathon page separately lists a Best blog post side prize. It does not publish a separate blog-only scoring rubric, so we designed this article to make the same three qualities easy to judge.
Innovation: Plumbline treats supply-chain triage as a connected evidence and memory problem rather than a package-name lookup. It combines path-backed verdicts, explicit unknowns, assumption-aware decisions, receipts, what-if replay, and agent handoffs in one graph.
Technical implementation: FalkorDB is central to the evidence paths, decision reopening, PageRank, task coordination, and per-upload isolation. The repository includes the graph model, named Cypher, deterministic policy, MCP tools, API, tests, benchmark, and deployment instructions. The benchmark reports where a simpler SQL approach is equally good instead of making an unsupported performance claim.
Presentation: The replay gives the project one understandable incident story. A judge can follow a poisoned package from publication to service exposure, inspect the path, change an assumption, and watch the affected decision reopen. The UI shows evidence instead of asking the viewer to trust a score.
Honest limits
The project currently supports npm lockfiles v2 and v3. The checker is not a complete vulnerability database. The default agent is a rule-based planner, not a recorded language-model trace. A tuned SQL query can answer the benchmark’s lookup questions correctly and faster at this scale.
The incident records and advisory publication times are sourced from OSV. Repository lockfiles and derived dependency edges are real inputs or derived results. Service names, exposure levels, and tiers are declared modelling data. The four exposed demo fixtures are synthetic. The advisory revision in the replay is hypothetical and labelled as such.
These boundaries are part of the design. A trustworthy agent should be explicit about what it knows, what it assumes, and what it cannot decide.
Run Plumbline locally
git clone https://github.com/soumya-narang/Plumbline.git cd Plumbline python -m venv .venv # macOS/Linux .venv/bin/python -m pip install -e ".[dev]" cp .env.example .env docker compose up -d python -m plumbline load python -m plumbline serve Then open http://127.0.0.1:8000.
Useful commands:
python -m plumbline triage MAL-2026-11524 --at 2026-08-04T11:40:00Z python -m plumbline replay --to 2026-08-04T13:00:00Z python -m plumbline handoff-demo --workers 5 python -m plumbline benchmark pytest Closing thought
Supply-chain incidents create pressure to produce a fast answer. Fast is useful, but a fast answer without a path, assumptions, or an honest unknown state is difficult to trust.
Plumbline is our attempt to make the answer inspectable:
Show the path. State the assumptions. Reopen only what broke.
Explore the source on GitHub, try the replay, and tell us what you would add next.


