September 13, 2026
I Built a Security Decision and Response Engine Over a Weekend
Most security systems are good at telling you that something happened.
By Ericmpta
7 min read
- A login failed.
- An SQL injection pattern was detected.
- A privileged endpoint was accessed.
- A device looks suspicious.
The difficult part is what happens after those individual signals appear.
- Are they related?
- How serious are they together?
- Should access still be allowed?
- Should the user be challenged?
- Should the source be blocked?
- Should the device be isolated?
And, perhaps most importantly:
Can an analyst understand why the system made that decision?
That was the problem I wanted to explore over a weekend.
The result became SentinelMesh โ a security decision and response platform designed around a simple pipeline:
Observe
โ
Detect
โ
Correlate
โ
Score Risk
โ
Decide
โ
Respond
โ
ExplainObserve
โ
Detect
โ
Correlate
โ
Score Risk
โ
Decide
โ
Respond
โ
ExplainIt isn't intended to replace a SIEM, EDR, firewall, or IAM platform.It is an experiment in building the layer that connects security signals to security decisions.
The problem with isolated detections
Consider three events:
5 failed authentication attempts
A privileged endpoint is accessed
An SQL injection pattern is detected5 failed authentication attempts
A privileged endpoint is accessed
An SQL injection pattern is detectedIndividually, each event tells us something.
But treating them independently loses the attack narrative.
The more interesting question is:
What happens when these events are part of the same attack?
That led me to model SentinelMesh around threat progression, rather than simply counting alerts.
For example:
Credential Brute Force
โ
Unauthorized Privileged Access
โ
SQL Injection
โ
Multi-Stage IncidentCredential Brute Force
โ
Unauthorized Privileged Access
โ
SQL Injection
โ
Multi-Stage IncidentThe system can then reason about the sequence rather than treating three alerts as three unrelated problems.
Architecture
The backend is intentionally simple.
- There is no Kafka cluster.
- No fleet of microservices.
- No machine-learning pipeline.
- No complicated event bus.
The core stack is:
Flask
PostgreSQL
REST API
React
Vite
TailwindFlask
PostgreSQL
REST API
React
Vite
TailwindThe simplicity is deliberate.
For a security system, I would rather have a small number of deterministic components that I can reason about than introduce infrastructure whose operational complexity exceeds the problem being solved.
The architecture currently looks roughly like this:
โโโโโโโโโโโโโโโโโ
โ Security โ
โ Events โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Detection โ
โ Engine โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Threat โ
โ Intelligence โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Risk Engine โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Correlation โ
โ Engine โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Incident โ
โ Engine โ
โโโโโโโโโฌโโโโโโโโ
โ
โโโโโโโโโโโโดโโโโโโโโโโโ
โผ โผ
โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ
โ Zero Trust โ โ Response โ
โ Decision โ โ Engine โ
โโโโโโโโโฌโโโโโโโโโ โโโโโโโโโฌโโโโโโโโโ
โ โ
โโโโโโโโโโโโฌโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโ
โ SOC Dashboard โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Security โ
โ Events โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Detection โ
โ Engine โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Threat โ
โ Intelligence โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Risk Engine โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Correlation โ
โ Engine โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ Incident โ
โ Engine โ
โโโโโโโโโฌโโโโโโโโ
โ
โโโโโโโโโโโโดโโโโโโโโโโโ
โผ โผ
โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ
โ Zero Trust โ โ Response โ
โ Decision โ โ Engine โ
โโโโโโโโโฌโโโโโโโโโ โโโโโโโโโฌโโโโโโโโโ
โ โ
โโโโโโโโโโโโฌโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโ
โ SOC Dashboard โ
โโโโโโโโโโโโโโโโโThe important part isn't the number of components.
It's the separation of responsibilities.
1. Security event ingestion
Everything starts with an event. Events are treated as untrusted input. The ingestion layer therefore doesn't allow clients to tell the platform things like:
risk_score = 100
severity = critical
status = containedrisk_score = 100
severity = critical
status = containedThose are security decisions and they belong to the platform.
The API validates incoming events, limits payload size, validates metadata, separates read and write credentials, and records when the server actually received the event.
That last point matters.
An attacker should not be able to make an event appear to have happened yesterday simply by changing a timestamp.The system therefore keeps both the event timeline and the server-side receipt time.
2. Deterministic detection
The next layer converts events into threats. I deliberately avoided machine learning. Instead, I implemented explainable detection rules. For example, the brute-force detector looks for repeated authentication failures from the same source within a configured time window.
The SQL injection detector looks for several independent patterns rather than one enormous regular expression.
And privileged-access detection checks authoritative identity information rather than trusting a role supplied in event metadata.
That distinction is important.
A security system cannot ask the attacker:
"Are you an administrator?"
and then trust the answer.
3. Risk scoring
Once a threat is detected, SentinelMesh calculates risk.
The model deliberately avoids a mysterious "AI score." The score has explainable components. Conceptually:
Risk =
Threat Severity
ร Confidence
+ ContextRisk =
Threat Severity
ร Confidence
+ ContextContext can include things such as:
privileged identity
device trust
resource sensitivity
previous threat types
attack progressionprivileged identity
device trust
resource sensitivity
previous threat types
attack progressionThe final value is bounded between:
0 โ 1000 โ 100with clear risk bands:
0โ29 LOW
30โ59 MEDIUM
60โ79 HIGH
80โ100 CRITICAL0โ29 LOW
30โ59 MEDIUM
60โ79 HIGH
80โ100 CRITICALThe important property isn't the particular mathematical formula.
It is determinism and explainability.
If an analyst asks:
Why did this become critical?
the system should be able to answer.
4. Correlation is where the system becomes interesting
A security platform that produces three alerts from three events hasn't necessarily understood the attack. SentinelMesh therefore introduces an incident correlation layer. Threats can be associated using signals such as:
same source
same principal
temporal proximity
attack progressionsame source
same principal
temporal proximity
attack progressionThe system also maintains a correlation confidence. For example:
Same source 0.40
Same principal 0.15
Temporal proximity 0.25
Attack progression 0.20
-----
1.00Same source 0.40
Same principal 0.15
Temporal proximity 0.25
Attack progression 0.20
-----
1.00The actual implementation bounds and normalizes this appropriately. The goal isn't to claim mathematical certainty. It is to make the reasoning visible. The result might become:
SM-001
Classification:
multi_stage_attack
Correlation:
0.990
Risk:
100 / CRITICALSM-001
Classification:
multi_stage_attack
Correlation:
0.990
Risk:
100 / CRITICALNow the security team has an incident rather than three disconnected alerts.
5. Zero Trust becomes a decision engine
The next question is more important:
Should access still be allowed?
This is where the Zero Trust decision layer comes in. I deliberately separated:
context loadingcontext loadingfrom:
decision evaluationdecision evaluationThe evaluator is a pure decision function.
- It doesn't perform database writes.
- It doesn't modify state.
- It doesn't secretly trigger responses.
It evaluates security context and returns a verdict.
The possible decisions are:
ALLOW
STEP_UP
DENYALLOW
STEP_UP
DENYThe policy considers things such as:
identity
device trust
threat risk
incident risk
resource sensitivityidentity
device trust
threat risk
incident risk
resource sensitivityA critical incident associated with the subject can therefore produce:
DENYDENYThe important thing is that the decision isn't:
"The system thinks this looks bad."
It is:
Decision:
DENY
Policy:
critical_incident
Reason:
Subject is associated with an active critical incident.Decision:
DENY
Policy:
critical_incident
Reason:
Subject is associated with an active critical incident.That difference matters when a security system needs to be defended technically.
6. Detection should lead to response
Detection without response is only half a security system. The response engine evaluates the resulting threat and incident state. The current response tiers are intentionally straightforward.
LOW
No containmentNo containmentMEDIUM
AlertAlertHIGH
Alert
Step-up requiredAlert
Step-up requiredCRITICAL
Alert
Step-up
Contain subject
Block source
Isolate deviceAlert
Step-up
Contain subject
Block source
Isolate deviceThe actions are currently internal security-state changes rather than direct firewall or operating-system commands.
That is deliberate.
I did not want a weekend project executing arbitrary iptables, shell commands, or remote administration actions merely because a detection rule fired.
The system establishes the decision and containment model first.
Real infrastructure integrations can sit behind that model later.
Idempotency matters more than it looks
Automated response introduces a subtle problem.
What happens if the same incident is processed twice?
You don't want:
block source
block source
block source
block sourceblock source
block source
block source
block sourcecreating inconsistent state.
Responses therefore have uniqueness guarantees around the incident and action. Repeated execution returns an already-applied result instead of creating duplicate actions. Concurrency is also protected using database-level locking and unique constraints.
This is one of those details that isn't particularly exciting in a demo but becomes very important in a real security system.
Security actions are audited
Every response action produces an auditable record.
For example:
Incident:
SM-001
Policy:
critical_incident_containment
Action:
isolate_device
Result:
succeeded
Reason:
Critical multi-stage incident
Trigger:
injection.sqlIncident:
SM-001
Policy:
critical_incident_containment
Action:
isolate_device
Result:
succeeded
Reason:
Critical multi-stage incident
Trigger:
injection.sqlThe system therefore maintains a history of what happened and why.
That distinction is important.
An audit log that simply says:
device isolateddevice isolatedisn't nearly as useful as:
device isolated
because:
critical incident
policy:
critical_incident_containment
trigger:
injection.sqldevice isolated
because:
critical incident
policy:
critical_incident_containment
trigger:
injection.sqlSecurity operations require context.
Containment is state, not just an event
Another distinction I wanted to preserve was the difference between:
"we executed a containment action""we executed a containment action"and:
"the subject is currently contained""the subject is currently contained"Those are not equivalent.
SentinelMesh therefore maintains containment state separately from response history.
The SOC can see:
Subject
CONTAINED
Device
ISOLATED
Source
BLOCKEDSubject
CONTAINED
Device
ISOLATED
Source
BLOCKEDwhile also seeing the historical actions that produced those states.
Fail-closed was an important design decision
One of the easiest mistakes to make when building security automation is accidentally creating a fail-open path.
For example:
Critical incident
โ
Automatic containment
โ
Incident marked closed
โ
Zero Trust no longer sees active incident
โ
Access allowedCritical incident
โ
Automatic containment
โ
Incident marked closed
โ
Zero Trust no longer sees active incident
โ
Access allowedThat would be a serious design failure.
I specifically avoided automatically closing incidents as part of containment. The response system changes containment state, but the incident remains available to the access-control layer. This means a later Zero Trust evaluation can still see the security condition that caused containment.
The SOC dashboard
The final layer is the operator experience.
The dashboard is intentionally not a collection of decorative charts.
The primary question is:
Can an analyst understand the attack quickly?
The main investigation flow is:
Incident
โ
Attack timeline
โ
Threats
โ
Risk
โ
Correlation
โ
Zero Trust decision
โ
Response actions
โ
Current containmentIncident
โ
Attack timeline
โ
Threats
โ
Risk
โ
Correlation
โ
Zero Trust decision
โ
Response actions
โ
Current containmentFor a multi-stage incident, the analyst should be able to see something like:
Credential Brute Force
โ
Unauthorized Privileged Access
โ
SQL Injection
โ
MULTI-STAGE ATTACK
โ
RISK 100
โ
ZERO TRUST: DENY
โ
SUBJECT CONTAINED
โ
SOURCE BLOCKED
โ
DEVICE ISOLATEDCredential Brute Force
โ
Unauthorized Privileged Access
โ
SQL Injection
โ
MULTI-STAGE ATTACK
โ
RISK 100
โ
ZERO TRUST: DENY
โ
SUBJECT CONTAINED
โ
SOURCE BLOCKED
โ
DEVICE ISOLATEDThat is much more useful than twelve KPI cards and a spinning "threat level" animation.
What I intentionally didn't build
There is a strong temptation when building security software to add every technology associated with modern security engineering.
I intentionally didn't.
No:
- machine-learning detection
- Kafka
- Kubernetes
- microservices
- arbitrary shell-based response
- fake attack maps
- meaningless threat animations
- external SIEM integrations
- complex event streaming infrastructure
- AI-generated risk scores
Not because those technologies are bad.
Because they weren't necessary for the problem I was solving.
A good architecture isn't the one with the most components.
It's the one where each component has a reason to exist.
The result
The most interesting part of the project isn't any individual detection rule.
It is the complete chain:
Event
โ
Detection
โ
Threat
โ
Risk
โ
Correlation
โ
Incident
โ
Zero Trust
โ
Response
โ
Audit
โ
SOCEvent
โ
Detection
โ
Threat
โ
Risk
โ
Correlation
โ
Incident
โ
Zero Trust
โ
Response
โ
Audit
โ
SOCA security event can therefore move from an untrusted input to an explainable security decision and, when appropriate, to controlled containment.
That creates a much more useful security narrative than simply generating alerts.
What I learned building it
The biggest lesson was that security engineering is often less about adding more detection logic and more about maintaining trust boundaries.
The system must know:
What does the client control?
What does the backend control?
What is derived?
What is authoritative?
What is merely evidence?
What is a decision?
What is an action?
What is current state?
What is historical state?What does the client control?
What does the backend control?
What is derived?
What is authoritative?
What is merely evidence?
What is a decision?
What is an action?
What is current state?
What is historical state?Those distinctions affect almost every design decision.
- A client can submit an event.
- It cannot submit the resulting risk.
- A detector can identify a threat.
- It cannot arbitrarily mark an incident critical.
- A policy engine can return DENY.
- It shouldn't silently mutate unrelated state.
- A response engine can contain a subject.
- It shouldn't automatically erase the incident that caused containment.
Those boundaries are where much of the real security engineering happens.
Where SentinelMesh goes next
The current implementation is deliberately foundational.
The next logical direction is attack simulation and operational hardening.
That means making the complete lifecycle repeatable:
Normal
โ
Attack
โ
Detection
โ
Correlation
โ
Risk escalation
โ
Zero Trust denial
โ
Automated containment
โ
Analyst investigation
โ
RecoveryNormal
โ
Attack
โ
Detection
โ
Correlation
โ
Risk escalation
โ
Zero Trust denial
โ
Automated containment
โ
Analyst investigation
โ
RecoveryFrom there, external integrations become much more interesting because the security decision layer already exists.
- A firewall could consume a source-blocking decision.
- An endpoint platform could consume device isolation.
- An IAM platform could consume step-up or deny.
- A SIEM could consume the incident and response history.
But those integrations should come after the decision model is sound.
Final thoughts
This started as a weekend project.
It became an exercise in something I find more interesting than simply building another security tool:
How do you turn security signals into trustworthy decisions?
The answer isn't necessarily more AI, more infrastructure, or more alerts.
Sometimes it is:
Good data
+
Deterministic detection
+
Explainable risk
+
Strong correlation
+
Explicit policy
+
Controlled response
+
Immutable auditGood data
+
Deterministic detection
+
Explainable risk
+
Strong correlation
+
Explicit policy
+
Controlled response
+
Immutable auditThat is the foundation I wanted SentinelMesh to explore.
And that's probably the biggest takeaway from the project:
A security system becomes valuable when it can explain not only what it detected, but why it acted.