cd /news/ai-agents/agents-that-act-on-connected-data-bu… Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-152201] src=dev.to β†— pub= topic=ai-agents verified=true sentiment=↑ positive

Agents That Act on Connected Data: Building BlastShield with FalkorDB

A developer built BlastShield, a security-investigation prototype that uses FalkorDB as a graph database and Gemini via MCP tools to trace modeled access routes from an alert to critical data assets. Gemini selects from server-derived response candidates but cannot write Cypher or access the mutation tool; a separate controller applies bounded changes to synthetic graph data and re-queries the graph to verify results. The project, presented at Graph Hacks 2026, runs on a React/Vite dashboard and FastAPI backend and ships with four canonical scenarios scored by a transparent demo rule set rather than calibrated breach probabilities.

by read9 min views3 publishedOct 18, 2026

How we used graph evidence, constrained tools, and a fresh query after every simulated response action.

Graph Hacks 2026 Β· Track 01: Agents That Act on Connected Data

Repository: github.com/sdkharade12/blast-shield-graph-hacks

In brief: BlastShield is a security-investigation prototype that explores how a graph-backed agent can trace modeled access routes to critical data. FalkorDB supplies the paths and relationships. Gemini can choose from response candidates derived by the server, but it does not write Cypher or receive the mutation tool. A separate controller applies bounded changes to synthetic graph data and queries the graph again to check the result.

Imagine an alert for an active session belonging to developer_07. Its demonstration risk score is 35 out of 100, labelled Medium.

Is that dangerous? The score alone cannot answer the question. The relevant context is several relationships away:

developer_07
  └─ HAS_SESSION β†’ sess_007
       └─ CAN_ACCESS β†’ build-runner-03
            └─ RUNS_AS β†’ deployment-role
                 └─ CAN_ASSUME β†’ production-role
                      └─ CAN_READ β†’ customer-db

The identity can reach a customer database through a chain of represented relationships. No single record tells the whole story; the route only becomes visible when the connections are traversed together.

That is the question behind BlastShield: what modeled route connects an alert to a critical asset, and what remains if one route is blocked?

A boundary matters here. All data in BlastShield is synthetic. The prototype does not connect to a real cloud account, identity provider, or production telemetry. A path represents reachability in the model, not proof of exploitation. Likewise, no qualifying path in the graph is not proof that a real environment is safe.

BlastShield combines a React and Vite dashboard, a FastAPI backend, FalkorDB as the graph database, and Gemini accessed through MCP tools. The dashboard presents four canonical scenarios:

Scenario Investigation target Seeded baseline described by the project
BH-1042 β€” identity access path developer_07 One modeled route to the customer database; 35/100, Medium
BH-IAM-2001 β€” excessive IAM permission analytics-role A path through a relationship classified EXCESSIVE ; 65/100, High
BH-WORKLOAD-3001 β€” exposed workload exposed-api-01 Three qualifying paths to critical assets; 75/100, High
BH-CONTAINMENT-4001 β€” containment containment-user-01 Two independent active routes; 60/100, High

The scores use the project's demonstration rule set, demo-rules-v1. They are transparent prototype values, not calibrated probabilities of a breach. The graph paths, risk presentation, and response state should be read as evidence from the synthetic model rather than claims about a live cloud tenant.

The model represents five main kinds of entities: Identity, Session, Workload, IAMRole, and DataAsset. The access path uses relationships such as:

(Identity)-[:HAS_SESSION]->(Session)
  -[:CAN_ACCESS]->(Workload)
  -[:RUNS_AS]->(IAMRole)
  -[:CAN_ASSUME]->(IAMRole)
  -[:CAN_READ]->(DataAsset)

Edges can carry properties that affect an investigation, including route identifiers, containment and response status, permission assessment, granted actions, scope, and sensitivity. Those properties help the query distinguish an active modeled route from a route that has been blocked.

A simplified version of the identity query looks for an active session, a running workload, the roles involved, and a critical data asset:

MATCH p =
  (i:Identity {id: $identity_id})
  -[session_edge:HAS_SESSION]->(s:Session {status: 'ACTIVE'})
  -[:CAN_ACCESS]->(w:Workload {status: 'RUNNING'})
  -[:RUNS_AS]->(r:IAMRole)
  -[:CAN_ASSUME]->(pr:IAMRole)
  -[:CAN_READ]->(d:DataAsset {sensitivity: 'critical'})
WHERE coalesce(session_edge.response_status, 'ACTIVE') = 'ACTIVE'
RETURN
  [node IN nodes(p) | {
    id: node.id,
    label: coalesce(node.name, node.id),
    type: labels(node)[0]
  }] AS nodes,
  [rel IN relationships(p) | {
    source: startNode(rel).id,
    target: endNode(rel).id,
    type: type(rel),
    properties: properties(rel)
  }] AS edges

The important result is not just true or false. The query returns the path, its nodes, its directed edges, and the edge properties. That is the evidence the dashboard can draw and the agent can use.

This is also why FalkorDB is central to the product. The graph query provides the path evidence, response candidates are derived from the returned relationships, and verification after a response action relies on a fresh graph query. Remove the graph and the application loses the connected evidence and the means to check whether a simulated route remains.

A shallow lookup can miss exposure that sits behind role assumptions. To demonstrate that, we added a supplemental audit fixture separate from the four canonical scenarios.

Source Target Maximum depth Expected result
auditor_02 analytics-cache 1 No qualifying path
auditor_02 analytics-cache 4 Path found
auditor_02 staging-report 4 No qualifying path
auditor_02 staging-report 5 Path found

The five-hop route to staging-report is:

auditor_02 β†’ sess_auditor_02 β†’ audit-reporter-02
          β†’ audit-readonly-role β†’ staging-observer-role
          β†’ staging-report

The final connection depends on a CAN_ASSUME relationship in the middle of the chain. With a depth limit of four, the modeled report appears unreachable; at depth five, the route can be discovered. This is the core demonstration: changing the allowed traversal depth changes which relationships the query can find.

BlastShield uses a bounded, directed traversal and allow-lists the relationship types it follows. Blocked session routes and blocked relationships are excluded. The depth supplied by the caller is validated as an integer between one and six and selects one of six prebuilt query templates; caller text is not inserted into Cypher.

FalkorDB versions used during development did not consistently accept a parameter inside the variable-length path bound. Keeping six fixed templates avoids composing executable query text from arbitrary caller input while preserving a validated depth control.

It is tempting to provide a model with a schema and ask it to generate Cypher. That gives the model too much freedom and makes it harder to explain or audit a result. BlastShield instead exposes a fixed catalogue of reviewed tools. Examples include:

investigate_alert(alert_id) β€” returns the canonical graph investigation.get_blast_radius(alert_id) β€” returns workload, role, and asset entities derived from paths.find_shortest_access_path(source_id, target_id, max_hops) β€” performs the bounded traversal.get_containment_route_statuses(alert_id) β€” returns route states for the containment scenario. The query templates are written by a person. Tool inputs are validated before execution, and tool traces capture details such as the tool name, inputs, outcome, duration, and result count.

The distinction between unknown and disconnected entities is just as important. An entity that does not exist should produce an unknown-source or unknown-target outcome; two known entities with no qualifying connection should produce a no-path outcome. Invalid input and query failures are different again. None should silently become a claim that the environment is safe.

Investigation is read-only. Response is where an agent can create risk, so we separated proposing an action from executing it.

The response flow has three boundaries:

The candidate list is derived from the current graph evidence, not invented by Gemini. When Gemini proposes an action, the server re-derives the current candidates and rejects a choice that is no longer present. The action service validates the choice again before performing the write. Gemini does not receive the mutation tool and does not write Cypher.

For example, the synthetic containment operation can mark a modeled active session route as blocked. It changes graph state used by the demonstration; it does not disable a real session or modify a cloud resource.

This design deliberately gives the model a short leash: it can select among supported options, but server-side code decides whether the choice is valid and which write may execute.

Issuing a command and verifying its effect are different things. An agent should not announce success just because it requested a change.

In BH-CONTAINMENT-4001, the seeded model has two independent routes. Blocking only the primary route should not end the run if the alternate route still qualifies. The controller therefore queries the graph after each action and decides whether to continue based on the fresh result:

Investigate
   ↓
Find qualifying route
   ↓
Choose supported action
   ↓
Apply synthetic change
   ↓
Query FalkorDB again
   ↓
Verify remaining routes
   ↓
Continue or stop

The intended seeded sequence is: block the primary route, re-query and discover the alternate route, block the alternate route, then re-query and confirm that no qualifying route remains. A run is complete only when the controller reaches the no-qualifying-path outcome and all actions have been verified. Reaching the action limit with a route still present is a stopped run, not a completed one. Unknown, invalid, or failed outcomes do not count as successful containment.

A regression test exposed a subtle failure mode in the response loop. If an action result did not include a post-action investigation, the controller could reuse its previous snapshot. The next decision could then be made from stale evidence.

We fixed the controller to fetch a canonical investigation from FalkorDB whenever the action response omits the after object. We also corrected the test fixture so its serialized path list matches its actual path data, then strengthened the regression to check that the second decision receives the alternate route rather than the primary route again.

The lesson is simple: a response agent must reason over the latest evidence, not the last evidence it happened to see.

Gemini can produce a useful briefing, but fluent text is not evidence. A briefing checker compares the proposed summary with the canonical investigation. It can flag entity IDs that were not returned by the graph, risk scores that do not match the computed value, relationship types not present in the evidence, and containment claims that are unsupported while a qualifying route remains.

If validation fails, or Gemini is unavailable, the UI uses a deterministic summary assembled from canonical evidence and labels it as a fallback. The safeguard is not to loosen the checks until every summary appears grounded; the safeguard is to keep the evidence authoritative and make fallback states visible.

Tool-level unit tests cover response-loop behavior such as re-querying between actions, terminal no-path handling, unknown or invalid outcomes, action limits, and verification failures. The latest backend test run reported 32 passing tests and six skipped tests. Those six are opt-in live-FalkorDB tests; a skipped test is not a verified pass. Live graph behavior, Gemini/MCP grounding across all scenarios, and the final browser flow require separate end-to-end checks.

There are deliberate limits to the prototype:

algo.SPpaths. demo-rules-v1 provides transparent scenario scores, not calibrated breach probabilities. To run BlastShield locally, start FalkorDB, install the Python dependencies from requirements.txt, seed the scenarios from the backend directory, start FastAPI, and launch the React/Vite frontend in a second terminal. Set GEMINI_API_KEY in the repository-root .env file to enable Gemini-assisted explanations and response decisions; never publish that file or its values. Follow the repository README for exact commands so the article and submitted code stay in sync: BlastShield repository.

This article was prepared with AI assistance and reviewed by the authors for factual accuracy. AI coding assistants were used during development; the team remains responsible for reviewing, testing, understanding, and explaining the submitted code. Gemini is a runtime component, constrained by the fixed MCP tools and checked against canonical graph evidence.

BlastShield explores a practical way to make an AI agent's security decisions easier to inspect: put the relationships in a graph, expose only reviewed tools, derive actions from current evidence, and query the graph after every simulated change. An agent should not be trusted because it can describe its work; it should be evaluated on the evidence it uses and the verification it performs.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @blastshield 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/agents-that-act-on-c…] indexed:0 read:9min 2026-10-18 Β· β€”