DEV Community

jackymenCZ (jackymenCZ)
jackymenCZ (jackymenCZ)

Posted on

Agentix Lite Sentinel v0.2.2: I Built a Tiny Linux Security Guard, Then Tried to Break It

I have been building a small Linux security agent called Agentix Lite Sentinel.

The idea sounds simple:

«Watch a server. Notice suspicious behavior. Remember enough to recognize a pattern. React when there is enough evidence.»

No giant SIEM.

No mandatory AI.

No cloud service sitting in the middle of the security pipeline.

No 47-container architecture for protecting one VPS.

The first version worked.

Then somebody pointed out several ways it could hurt itself.

That was useful.

So instead of adding another layer of abstraction, I rebuilt the weak parts.

This is Agentix Lite Sentinel v0.2.2.

And this version is less interesting because it has more features.

It is more interesting because it has more limits.


What is Agentix Lite?

Imagine you rent a small Linux VPS.

Maybe it runs:

a website
an API
a small game server
a private service
a monitoring tool
a side project

You probably do not need a giant enterprise security platform.

But you also do not want your server sitting on the internet with its doors wide open while thousands of automated scanners wander past.

That is the problem Agentix is trying to address.

It is a small defensive host guard.

Think of it as a night guard who does not record every person walking past the building.

He watches for behavior that deserves attention.

One person opens the front door:

«probably normal.»

Someone tries twenty different doors in five seconds:

«okay, interesting.»

Someone finds a fake administrator entrance that does not actually exist:

«now we are paying attention.»

Someone connects to a port that exists specifically as a honeypot:

«that is significantly more interesting.»

The basic idea is:

observe
↓
recognize behavior
↓
accumulate evidence
↓
decide
↓
possibly react

And there is one important rule:

an observation is not automatically an attack.


Why not just collect everything?

Because security tools have a strange habit.

You start with:

«"I want to know what happened."»

Then somebody says:

«"Let's log it."»

Then:

«"Let's keep the logs."»

Then:

«"Let's ship them somewhere."»

Then:

«"Let's index them."»

Then:

«"Let's search them."»

And eventually your tiny Linux server is running a miniature data center because someone requested a log of a log of a log.

Agentix takes the opposite approach.

The runtime uses a bounded event buffer and compact persistent state.

The current defaults are deliberately finite:

rolling buffer 1000 events / 600 seconds
ingest queue 2048 events
per-IP state 10000 IPs
persistent actors 100000 rows
SQLite guard 100 MiB

Those are not promises that the system can survive an arbitrary DDoS.

They are guardrails.

There is a difference.

The point is to make the defender predictable under pressure.


The Honey API

One of the most interesting parts of Agentix is the Honey API.

It exposes fake application endpoints that look plausible enough to attract automated probing.

For example:

/api/v2/admin/config
/debug/env
/api/v2/payment/status
/api/v2/user/update/1

These are not real administration interfaces.

They are decoys.

Why would that help?

Because real traffic contains a lot of noise.

A decoy endpoint is different.

If somebody starts probing:

/debug/env
/api/v2/admin/config
/api/v2/user/update/1

the application is not the important part.

The behavior is.

The Honey API turns that behavior into a small structured event which the local Sentinel can analyze.

And it does not need to save somebody's entire HTTP request forever.

The raw request body is not persisted.

Full request headers are not persisted.

The point is to preserve the useful signal and throw away the junk.


There is also a honeypot port

Agentix Lite can listen on a dedicated TCP port that has no legitimate application purpose.

For example:

22222

A connection there becomes a high-signal event.

Again, there is no need to store whatever random bytes a scanner sends next.

The fact that somebody discovered and connected to the decoy is already useful information.

It is like putting one fake door on the building and checking whether anyone tries to open it.


What happens after an event?

Agentix turns incoming signals into a small internal event.

Then the event travels through several stages.

Honey API / SSH / honeypot
↓
event ingestion
↓
bounded queue
↓
pattern engine
↓
reputation
↓
decision
↓
SQLite state
↓
nftables

The important change in v0.2.2 is what happens when one part becomes overloaded.

The telemetry path is now designed to drop events rather than block the protected application.

That is an important philosophical choice.

I would rather lose a few security telemetry events than make a honeypot block a real HTTP request because the security engine is having a bad afternoon.

The security system is there to protect the service.

It should not become the service's most talented attacker.


Does it block attackers?

It can.

But the safe default is shadow mode.

That means Agentix can reach a decision such as:

BAN_CANDIDATE

without immediately changing the firewall.

That allows real-world observation before enabling enforcement.

When enforcement is enabled, bans are temporary.

There is no permanent automatic ban mechanism in this version.

There are also safeguards around which addresses are eligible for blocking.

This matters because automation is wonderful right up until it confidently blocks the person who owns the server.


Does Agentix use AI?

No, not in the critical path.

That is intentional.

The current core is deterministic.

It can recognize things such as:

honeypot_hit
ssh_bruteforce
scan_burst
rate_spike
admin_config_probe
admin_targeting
sqli_probe
path_traversal_probe
subnet_burst

These are rules and compact state, not an LLM trying to philosophically interpret a GET request.

There may eventually be an optional slower analysis layer.

But the core should still work without it.

A firewall should not need an API key to understand that someone just walked into a honeypot.


So where is this useful?

Agentix Lite is aimed at small and medium deployments where someone wants a local defensive signal layer without introducing a whole security stack.

For example:

small VPS
developer server
self-hosted application
small API
lab infrastructure
personal project
internet-facing Linux host

It is especially interesting where you care about behavioral evidence, not just a giant stream of raw logs.

The Honey API is also useful when you control the application surface and can deliberately place decoys where suspicious behavior becomes visible.


What changed in v0.2.2?

This is where the story gets more interesting.

Version 0.2.1 was reviewed adversarially.

The criticism identified several possible failure modes around:

memory growth
socket backpressure
SQLite growth
per-IP state
firewall action storms
proxy identity
high traffic

Instead of replying:

«"Well, technically..."»

I changed the architecture.

The main changes are:

Non-blocking telemetry

The Honey API and local sensors no longer wait for the Agentix core to catch up.

If the telemetry path is unavailable or overloaded, the event can be dropped.

The HTTP request continues.

Bounded ingestion

Socket reading is now separated from event processing.

The socket reader validates events and puts them into a bounded queue.

SQLite work happens later.

That prevents the socket consumer itself from becoming the database worker.

Smaller scan state

The scan detector no longer keeps a collection of attacker-controlled raw paths for every IP.

It uses a fixed-size hash sketch.

That gives predictable memory usage at the cost of a small collision risk.

Smaller rate state

Rate detection uses fixed-window counters instead of retaining lists of timestamps for every IP.

SQLite limits

Persistent actors have a hard upper bound.

The database also has a size guard.

When the actor capacity is reached, older non-banned actors can be pruned rather than allowing state to grow forever.

Firewall action budget

Even when enforcement is enabled, Agentix will not call the "nft" command without limit.

The default budget is:

60 firewall actions / minute

This is specifically designed to stop a ban storm from turning the defense mechanism into a fork/exec machine.

Explicit proxy trust

"X-Forwarded-For" is not automatically trusted.

The Honey API uses the real TCP peer address unless that peer belongs to an explicitly configured trusted proxy network.

That makes proxy behavior an explicit deployment decision instead of an implicit assumption.

Resource limits

The core systemd service and Honey container now have resource limits.

The goal is simple:

«If Agentix has a terrible day, it should have a terrible day inside a box.»


The programmer half starts here

If you have survived the friendly explanation, welcome to the machinery.

The core is written in Python 3.12+.

The design intentionally relies heavily on the standard library.

Persistent state is stored in:

SQLite

The firewall integration uses:

nftables

The Honey API is separated into a container using:

FastAPI
Uvicorn
Docker

The service lifecycle is handled by:

systemd

There is also an operational CLI:

agentix status
agentix patterns
agentix show
agentix ban
agentix unban
agentix report

So the project is not one giant Python file.

It is intentionally split into small responsibilities:

events
buffer
transport
patterns
reputation
storage
firewall
engine
sensors
report
CLI


The important architectural change: ingestion is now separate from processing

This was one of the biggest fixes.

The previous model was too close to:

socket
↓
event
↓
process everything

That makes the receiver part of the processing bottleneck.

Version 0.2.2 uses:

Unix datagram
↓
socket reader
↓
bounded queue
↓
worker
↓
pattern detection
↓
SQLite

The queue has a finite size.

When it fills up, telemetry is dropped.

That is intentional.

The overload policy is:

«drop telemetry before blocking the protected service.»

That sentence is probably the most important design decision in this release.


The scan detector changed too

A naive implementation can accidentally create a memory problem by storing:

IP
├── /admin
├── /debug
├── /foo
├── /bar
├── /whatever
└── ...

for thousands of IPs.

An attacker controls the path strings.

That makes attacker-controlled input part of your memory footprint.

Version 0.2.2 uses a fixed-size hash sketch.

Conceptually:

path
↓
hash
↓
bucket
↓
bit

So memory is bounded.

The trade-off is classic:

less memory, small probability of collisions.

Repeated requests to the same path should still not behave like new unique paths.


Reputation remains intentionally boring

That is a compliment.

The score is just accumulated evidence.

For example:

honeypot_hit +100
admin_targeting +60
ssh_bruteforce +40
sqli_probe +40
path_traversal +35
admin_config_probe +30
scan_burst +20
subnet_burst +15

The score decays over time.

Then the decision engine checks policy.

The current design is explicitly:

pattern ≠ firewall command

A pattern is evidence.

The decision engine decides whether that evidence is sufficient.


Why the firewall has its own budget

Suppose an attacker generates thousands of unique suspicious actors.

Without a guard, the logic could look like:

event
↓
ban
↓
subprocess
↓
nft
↓
event
↓
ban
↓
subprocess
↓
nft
...

That is not defense.

That is process generation with extra vocabulary.

Version 0.2.2 caps firewall actions.

When the budget is exhausted, the engine records:

FIREWALL_CAP_REACHED

instead of blindly spawning more processes.

The current implementation still calls the "nft" CLI rather than using a native netlink binding.

That remains an explicit limitation.


What happens to SQLite?

SQLite is not supposed to become a historical archive.

Agentix stores compact state.

The important persistent objects are actor and pattern information, not the full raw event stream.

The current architecture places explicit limits around:

actor count
database size
WAL growth
old state retention

There is also maintenance and pruning.

This is much closer to:

«"remember what matters"»

than:

«"remember every packet humanity has ever seen."»


What has actually been tested?

This part matters.

It is very easy for a README to say:

«"Highly scalable."»

That sentence costs absolutely nothing.

A benchmark does.

For v0.2.2, the project test suite contains 35 tests, and the current run completed:

35 passed

Compile checks also passed.

The setup script passed shell syntax validation.

A real Unix socket integration path was exercised from event emission through ingestion and processing.

There were also adversarial tests around:

transport saturation
storage capacity
proxy trust
firewall action budgeting

The benchmark was also changed so that Python heap allocation and process RSS are reported separately.

That distinction matters because process RSS includes interpreter/runtime overhead and can differ substantially across environments.


Benchmark numbers

One local benchmark using:

100,000 actors
100,000 events

produced approximately:

~11.2k events/sec

for a telemetry-only "/health" workload.

When each event also exercised pattern detection and SQLite writes, throughput was approximately:

~5.0k events/sec

Python-level traced heap remained around:

~7.4 MiB peak

These are observations from one benchmark environment.

They are not production capacity guarantees.

That distinction is important enough to put in bold.

A benchmark is a measurement.

It is not a prophecy.


What is still unsolved?

Quite a lot.

And this is where I want the article to be honest.

Distributed low-and-slow behavior

An attacker can distribute activity across many IPs and keep individual activity below local thresholds.

The current system is therefore still much stronger against obvious bursts than against slow distributed behavior.

CDN and reverse proxy identity

The system supports explicitly trusted proxies, but correct deployment still depends on configuring that trust correctly.

A bad proxy configuration can produce bad identity.

Security software cannot magically infer the network architecture of a server.

Hash sketch collisions

The fixed scan sketch gives bounded memory.

It also means two different paths can map to the same bucket.

The detector can therefore undercount.

That trade-off is deliberate, but it needs real-world measurement.

nftables integration

The current firewall path still uses the "nft" command.

Native netlink integration could reduce process overhead and improve batching.

That is still future work.

False positives

We have unit tests.

That is not the same thing as having a large real-world traffic corpus.

A threshold that looks sensible in a synthetic test can behave differently on:

a CDN
a busy API
a shared server
a reverse proxy
a real production application

This is one of the biggest things I want feedback on.


What Agentix Lite is not

It is not:

a replacement for a WAF
a replacement for a SIEM
a DDoS mitigation service
a full IDS/IPS
an enterprise SOC platform
an AI security oracle

And I don't want to pretend otherwise.

The project is much smaller.

That is the point.

The question is whether there is useful territory between:

do nothing

and:

deploy an entire security platform

Agentix Lite is exploring that middle ground.


The experiment is now bigger than the code

The interesting question is no longer:

«"Can I write a small security agent?"»

Yes.

I can.

The interesting question is:

«Is a tiny local behavioral sentinel actually useful enough that another developer would keep it running?»

That has to be answered by real deployments and real feedback.

Not by me giving the project five stars because I wrote it.


So here is the part where I hand it to developers

I built a little honey pot.

Then I made the honey pot less likely to eat itself.

Now I want somebody smarter than my test suite to poke at it.

Tell me where you think this design breaks.

Not:

«"Nice project!"»

Give me the annoying comments.

Those are the useful ones.

Would the bounded hash sketch worry you?

Would you trust SQLite for this role?

Would you redesign the ingest queue?

Would you batch firewall operations?

Would you track distributed behavior differently?

Would you remove something completely?

Would you never run this on a production VPS?

Tell me why.

I promise not to put "thanks for the feedback" in the next release and then quietly ignore it.

Unless the feedback is:

«"Add Kubernetes."»

Then I reserve the right to stare at the ceiling for several minutes.


Final status

Agentix Lite Sentinel v0.2.2 is a working hardening release, not a finished security product.

The core is deterministic.

The Honey API is isolated.

The telemetry path is bounded and non-blocking.

Persistent state has explicit limits.

Firewall operations have a budget.

Automatic enforcement is disabled by default.

The current test suite passes.

The benchmark is measurable.

And the remaining weaknesses are documented instead of hidden behind a shiny dashboard.

That is probably where this project should be right now.

Small enough to understand.

Concrete enough to attack.

And unfinished enough to learn something.

Top comments (0)