← back to all projects
PythonFastAPINeo4jWazuhGemini · MCP

Orbit-Node: Security Orchestration

A security-orchestration platform for a bank network, built for a US-based client (NJSecure) by a five-person team I led in the Design & Analysis of Software Systems course. Alerts are enriched against a Neo4j asset graph, scored for PII exposure, reasoned over by a Gemini agent through MCP tools, and executed by the Wazuh SIEM, but only after a human analyst approves each proposed action. Because threat intelligence is pushed from a central server, it defends against global threats, not just the ones a host has already observed locally.

Private repository, built for a client. Happy to walk through the code and architecture in an interview.

The problem

SOC teams drown in alert volume. A bank’s SIEM raises far more alerts than any analyst team can properly triage, and each one demands the same slow, expert questions: what fired, on which asset, what data sits behind that asset, and is this worth acting on? Most of that effort is spent on alerts that turn out to be noise while the one that matters waits in the queue.

LLMs are genuinely good at exactly this triage work: reading an alert, pulling in context, and writing up a reasoned recommendation. But you cannot let a model execute mitigations on a bank network unsupervised. A hallucinated firewall rule that drops the payments gateway is not a “wrong answer” but an outage with regulators attached.

Orbit-Node’s answer is a strict separation of powers: the AI proposes, a human disposes, the SIEM executes. The model can only ever produce an ActionCard, a proposed remediation with its evidence and reasoning. Nothing touches the network until an analyst approves it, and then it is Wazuh, not the model, that carries the action out.

Interactive: from raw alert to human-approved response

The stage below is the whole pipeline: an attacker brute-forcing a simulated bank host, Wazuh detecting it, the orchestrator enriching the alert against the Neo4j asset graph, Presidio redacting PII before the model sees anything, and the agent producing an ActionCard. Scenario 3 hands the final decision to you.

orbit-node · ssh brute-force → ActionCard AC-042
MCP attacker 203.0.113.7 bank network web-01 core-db ad-01 monitors Wazuh (SIEM) FastAPI Orchestrator Neo4j asset graph RUNS STORES web-01 payments-app customer-data criticality: HIGH Presidio (PII) Gemini agent analyst queue: empty

In scenario 3 you are the analyst. Click Approve or Reject on the ActionCard.

How it works

Every alert takes the same four-step path from detection to (possible) execution:

  1. Detect. Wazuh monitors the hosts, matches its correlation rules against suspicious activity, and forwards each alert to the orchestrator’s webhook.
  2. Enrich and score. The orchestrator enriches the alert against the Neo4j asset graph (which hosts, applications and data are affected, and how critical they are) and runs Presidio over the evidence to estimate PII and data-leakage exposure.
  3. Reason. A Gemini agent collects that evidence through MCP tool calls, applies a deterministic decision policy, and produces an ActionCard: a proposed remediation with its reasoning and a mitigation plan.
  4. Decide and execute. An analyst reviews each ActionCard and approves or rejects it. Approved cards trigger Wazuh active-response actions, such as a firewall block.

Architecture

The FastAPI orchestrator sits at the center: it owns the alert/webhook pipeline, the agent evaluation, and the ActionCard review UI. The agent never talks to infrastructure directly. Two MCP servers are its only hands: orc_mcp exposes Neo4j graph evidence, and wazuh_mcp exposes SIEM query and active-response tools. For end-to-end testing we built bank_simulation, a dummy corporate network (core database, web application, Active Directory) that the SIEM watches like a real estate.

The whole platform runs as a multi-container Docker Compose stack. A single setup.sh generates the Wazuh certificate set and strong random passwords locally, writing them into a gitignored .env, so the repository ships no credentials at all.

The agent design

The agent is deterministic-first. It gathers evidence through MCP tool calls, a fixed rule set makes the actual decision (for example: brute-force pattern + HIGH-criticality asset → recommend a block), and only then is the model asked for what it is genuinely good at: the explanation and the mitigation plan. It also fails open: if Gemini is unreachable, the deterministic policy still emits an ActionCard on its own, so the pipeline is never blocked by a model outage (scenario 4 in the demo).

This shape matters for two reasons. First, auditability: the decision is reproducible from the rules and the evidence, not from a temperature-dependent sample. Second, a bounded blast radius for AI mistakes: the worst thing a confused model can do is write a bad paragraph, because it cannot invent an action the policy never proposed.

My role

I led this project end to end. I owned the system architecture, ran the client relationship with NJSecure directly, and built the core of the platform myself: the AI reasoning, the Gemini MCP server, and the orchestrator over the Neo4j asset graph are mine, along with the Presidio anonymization step that guards the model. I also designed and contributed roughly half of the Wazuh, Neo4j, and Presidio modules, guiding the four teammates who built them out with me. We shipped the platform end to end, earning an A grade and recognition from the client.

Design decisions & trade-offs

  • The approval gate sits before execution, not after. A post-hoc review model (execute now, audit later) responds faster, but on a bank network an executed mistake, whether a dropped payments host or a blocked branch office, cannot be un-executed. We accepted minutes of human latency to guarantee that no AI-originated action ever runs without a signature.
  • A graph database for the asset model. The triage question is always “what can be reached from this host, and how bad is it?”, which is a multi-hop traversal. In Cypher that is one MATCH over RUNS and STORES edges; in a relational schema it is a stack of recursive joins that grows with every hop. Blast-radius queries are the workload, so the database was chosen for them.
  • Anonymize before the model sees anything. The evidence is bank data and the model is a third-party API, so Presidio redacts PII from the bundle before any model call. The model reasons over placeholders like <EMAIL> and <ACCOUNT>. It loses a little context, but the decision never depended on that context, because the policy is deterministic.
  • MCP as the tool boundary. The agent has no database driver and no SIEM client: every piece of evidence it touches is an explicit, typed, logged MCP tool call. That makes the agent’s entire view of the world auditable, and means it structurally cannot query anything it was not given a tool for.

Honest scope

Orbit-Node is a course project (Design & Analysis of Software Systems), exercised end-to-end against bank_simulation, a containerized dummy corporate network with a core database, a web application and Active Directory, rather than a production bank estate. It has been demoed, graded and client-reviewed, but it has not been hardened for or survived contact with a real SOC.

The claim I will defend anywhere is the architecture: propose → human-approve → execute, with deterministic decisions and an auditable MCP tool boundary. The deployment hardening is course-project grade, and I say so.