DEV Community

jackymenCZ (jackymenCZ)
jackymenCZ (jackymenCZ)

Posted on

Agentix honeypot Lite

The prototype is 100% functional, fully coded, and tests are passing. But before I push it into a full public release, I want to validate if this architecture actually solves a real problem for you—or if I'm just solving my own itch. ☺️

_
Would Developers Actually Want a Tiny Host Sentinel? I Built One to Find Out

There is a strange thing about security software.

The moment it becomes useful, it usually becomes complicated.

You start with:

«"I just want to know who's poking my server."»

A few months later you have dashboards, agents, collectors, databases, SIEM integrations, alert pipelines, cloud APIs, retention policies, vector databases, machine learning and approximately fourteen more services than you originally intended to run.

So I started asking a smaller question:

What if a defensive security agent stayed small on purpose?

That is the idea behind Agentix Lite Sentinel.

It is a small defensive Linux host guard written primarily in Python. It watches a few carefully selected signals, turns them into small structured events, looks for suspicious behavior, keeps a compact reputation score, and can apply a temporary firewall ban.

No giant log warehouse.

No LLM sitting in the hot path.

No permanent automatic bans.

No Docker socket.

And, at least for this version, no attempt to pretend that a few clever rules have solved cybersecurity.

This is version 0.2.1.

And I am publishing the idea partly because I want developers to attack the design with criticism.

What is Agentix Lite Sentinel?

Imagine a small security guard standing outside a server.

It is not trying to record everything that happens in the building.

It is watching the doors.

If somebody tries a normal door once, the guard mostly ignores it.

If somebody walks around touching twenty different doors in five seconds, the guard becomes interested.

If somebody touches a fake administrator door that should not exist, the guard becomes much more interested.

If somebody connects to a honeypot port that exists specifically to attract suspicious traffic, that is strong evidence.

The important part is that the system does not need to understand the entire internet.

It only needs to recognize a few patterns.

That gives Sentinel a simple job:

observe → recognize behavior → accumulate evidence → decide what to do

The key word is evidence.

One strange request should not necessarily result in a ban.

A new pattern should not magically become a firewall rule.

And the system should not need an AI model to explain basic things like:

«"This IP touched 20 different paths in 5 seconds."»

That is a counting problem.

So Sentinel treats those things as deterministic signals first.


The little "honey" trick

One of the more interesting parts is the Honey API.

Instead of waiting for an attacker to discover a real administrative endpoint, you can put out fake ones.

For example:

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

These are decoy endpoints.

They are designed to look believable enough to attract probing, but they do not expose real secrets or real administrative functionality.

The fake admin endpoint always refuses access.

The fake payment endpoint returns sandbox data.

The fake user update endpoint looks like something a real application might have, but it is still only a decoy.

Why do this?

Because normal traffic is usually messy.

People visit real endpoints for real reasons.

A fake endpoint can give you a much cleaner signal.

If something starts systematically probing "/debug/env", "/api/v2/admin/config", unusual user IDs and similar paths, that is more interesting than another line in a giant access log.

The honey layer is basically a security "please touch this if you're doing something weird" sign.

And yes, I like calling it the honey layer because "decoy telemetry ingestion surface" sounds like it was invented by someone who has never slept.


It also listens to a honeypot port

Sentinel Lite has another deliberately suspicious little door:

TCP 22222

Nothing important lives there.

The service listens, notices who connects, generates a small event, and closes the connection.

It does not need to store whatever the client sends.

That makes the port useful as a high-signal tripwire.

A normal user generally has no reason to discover and interact with that port.

A scanner might.

A bot might.

An attacker looking for exposed infrastructure might.

And that difference is exactly what the system wants to observe.


It does not want to become a SIEM

This was one of the design constraints from the beginning.

A small host guard should not become a second job whose purpose is to store everything the first job saw.

So Agentix uses:

short-lived memory + compact persistent state

The rolling in-memory buffer is bounded by both age and size.

The current configuration keeps roughly:

600 seconds
1000 events

whichever limit becomes relevant first.

Persistent state goes into SQLite.

Instead of keeping every raw HTTP request forever, Sentinel stores compact information such as:

IP
first_seen
last_seen
score
attempt_count
last_pattern
last_action

and pattern counters.

That makes the system much closer to a small behavioral memory than a log warehouse.


The system can start in observation mode

This is another deliberate choice.

The default configuration has enforcement disabled.

That means the system can notice:

«"This IP crossed the ban threshold."»

without immediately turning around and attacking your own server with a firewall rule.

Instead it records a ban candidate.

That gives you a way to run the system against real traffic before allowing autonomous enforcement.

This matters because security software has a nasty failure mode:

being wrong very efficiently.

A false positive that appears once is annoying.

A false positive that automatically blocks the wrong address can become an incident.

So the idea is to make the system useful before making it aggressive.


So what is the point?

Agentix Lite is not trying to replace a full IDS, WAF, SIEM or enterprise security platform.

It is trying to answer a much smaller question:

«Can a Linux host have a tiny local defensive brain that notices suspicious behavior without collecting the entire universe of telemetry?»

The current prototype says:

yes, technically.

Whether that is actually useful to other developers is a completely different question.

That is the part I want to test.


The technical half

Okay.

Now we can stop pretending this is magic.

Architecture

The current system looks roughly like this:

             Internet
                |
      +---------+---------+
      |                   |
   Honey API        TCP Honeypot
      |                   |
      +---------+---------+
                |
          normalized Event
                |
        Unix datagram socket
                |
         Rolling RAM buffer
                |
      Deterministic patterns
                |
      Reputation + decay
                |
          Decision engine
            /         \
      OBSERVE       TEMP_BAN
                        |
                    nftables
                        |
                     SQLite
                        |
                   Daily report
Enter fullscreen mode Exit fullscreen mode

The project currently contains 42 files covering the core agent, sensors, Honey API, deployment, systemd units, tests and benchmark tooling.

The core requirement is that the engine works without Docker, ChromaDB or an external API.

That is intentional.


What language is it written in?

The core is Python 3.12+.

The project deliberately has no mandatory Python runtime dependencies for the core package.

That is because the important path uses mostly the standard library:

sqlite3
socket
threading
subprocess
ipaddress
tomllib
argparse
collections
dataclasses

The Honey API is a separate component using:

FastAPI
Uvicorn
Docker

The firewall integration uses:

nftables

Service management uses:

systemd

Persistent storage uses:

SQLite

So the architecture is much closer to a collection of small OS-level components than a large application framework.


The Event object

Everything entering the engine gets normalized into a small event structure.

Conceptually:

{
"ts": 1779840000.0,
"source": "honey_api",
"ip": "8.8.8.8",
"method": "GET",
"path": "/api/v2/admin/config",
"status": 403,
"bytes_in": 0,
"body_sha256": "",
"user_agent_hash": "",
"hinted_pattern": ""
}

The schema is deliberately constrained.

There is an 8 KB maximum serialized event size.

Sources are allowlisted.

IP addresses are validated.

Methods and paths have maximum lengths.

Incoming request bodies are bounded and, where relevant, only a short hash is retained in the event.

The raw body is not persisted.

The same principle applies to request headers and SSH/system logs.

The goal is to prevent an attacker from turning telemetry into an unbounded storage problem.


Pattern detection is deterministic

The current pattern engine includes signals such as:

honeypot_hit
ssh_bruteforce
rate_spike
admin_config_probe
admin_targeting
sqli_probe
path_traversal_probe
scan_burst
subnet_burst

For example, the engine can detect a burst of distinct paths from one IP.

The important detail is that it tracks distinct paths, not merely request count.

So this:

GET /health
GET /health
GET /health
GET /health

does not become a port-scan-like pattern just because somebody repeated the same request.

Whereas this:

GET /admin
GET /debug
GET /api
GET /config
GET /login
...

can eventually become a "scan_burst".

The state is bounded so that the defense mechanism itself does not grow forever.


Reputation is just evidence accumulation

Patterns contribute points.

The current configuration uses thresholds such as:

observe below: 40
rate limit at: 40
temporary ban at: 80
honeypot ban threshold: 100

Examples of signal weights include:

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 also decays over time.

The important design decision here is that a pattern is evidence, not a direct firewall command.

The decision engine still applies safety checks.


Safety before enforcement

Before an IP can be automatically blocked, Sentinel checks things such as:

allowlist
private addresses
loopback
link-local
multicast
unspecified addresses
reserved addresses
CGNAT range

That prevents an overly enthusiastic detector from deciding that:

192.168.1.1
127.0.0.1

has suddenly become the world's greatest cybercriminal.

There is also a maximum active-ban count.

Bans are temporary.

There is no automatic permanent-ban path in this version.

And automatic enforcement is disabled by default.

That last one is important enough to repeat:

the first deployment should be observation, not heroics.


Why nftables?

The firewall integration uses a dedicated nftables table:

inet agentix

with separate IPv4 and IPv6 sets.

The setup intentionally does not flush the host firewall.

It only creates and manages the dedicated Agentix table.

Temporary bans use nftables timeouts so the kernel can expire them.

That gives the defense path a fairly simple shape:

event
↓
pattern
↓
score
↓
decision
↓
temporary nftables element

rather than building a second firewall implementation in Python.


The Honey API is isolated

The Honey API runs in a Docker container.

The container:

drops all Linux capabilities
uses no-new-privileges
has a memory limit
has a CPU limit
has a read-only filesystem
has no Docker socket
has no host filesystem
does not receive the Agentix database

It only needs to emit small normalized events to the Agentix Unix socket.

This is important because a honeypot that becomes the most privileged thing on the server would be a rather spectacular own goal.


What about AI?

There isn't any in the critical path.

That is intentional.

There is a future optional concept called the slow brain.

It could eventually consume aggregated facts such as:

pattern counts
timing
categories
sequence summaries

and help cluster unusual behavior or suggest new deterministic rules.

But it should not directly control nftables.

It should not silently change thresholds.

And it should not receive raw credentials, raw request bodies, secrets, source code or arbitrary host filesystem contents.

The basic security engine should still function if the AI layer disappears completely.

That is a useful property for something whose job is to defend the machine when everything else is having a bad day.


Does it actually perform well?

This is where I want to be careful.

The project has benchmark tooling, but benchmark numbers are measurements, not promises.

In a current local run of version 0.2.1 with:

100,000 synthetic actors
100,000 synthetic events

I measured approximately:

17,883 events/sec
109.8 MiB peak RSS
19.8 MB SQLite after WAL checkpoint

That run was performed in this environment, so it should not be interpreted as a production capacity claim.

The repository also contains an earlier local benchmark result around 24,968 events/sec.

The difference is actually useful.

It is a reminder that:

"fast" is not a property you declare in a README.

It is a measurement that depends on the machine, workload and benchmark method.


What is deliberately missing?

Quite a lot.

There is no:

SIEM pipeline
full packet capture
distributed reputation service
eBPF subsystem
GeoIP database
real-time web dashboard
mandatory LLM
mandatory vector database
permanent automatic blocking
automatic subnet banning

Some of these could be added later.

The bigger question is:

should they be?

Complexity is not automatically progress.


The interesting part: what could go wrong?

This is the part I care about most.

For example:

A detector might correctly recognize suspicious behavior but still produce a bad decision.

A subnet signal might accidentally aggregate unrelated users behind shared infrastructure.

A honeypot could attract scanners so aggressively that the sensor itself becomes the bottleneck.

An attacker might deliberately generate patterns that make the defender burn memory.

A heuristic such as path-based SQL injection detection can obviously be fooled.

A score threshold can look mathematically elegant while being completely wrong for somebody else's traffic.

And a firewall that is technically correct can still be operationally dangerous.

These are not hypothetical reasons to stop building it.

They are the actual things that need testing.


So... would developers actually use this?

I don't know.

And I think that is a more interesting answer than pretending I do.

The prototype is technically real.

The tests pass.

The components are small.

The core can run without an AI service.

The Honey API produces structured security signals.

The firewall path is deliberately conservative.

But none of that proves developers need it.

The real experiment is whether someone looks at the idea and says:

«"I actually have a server where this would be useful."»

Or:

«"Your "scan_burst" detector is going to melt the moment it sees Cloudflare."»

Or:

«"Why are you doing this with SQLite?"»

Or:

«"You are missing the one signal that matters."»

That kind of feedback would be much more valuable to me than ten people saying:

«"Cool project!"»

Your turn

I built the honey pot.

Now I'm interested in the people who know where the honey pot is likely to fail.

What would you change first?

What signal is too naive?

Where would you expect false positives?

How could an attacker make the defender consume too much RAM, CPU or firewall state?

Which part of the architecture would you remove completely?

And what would you measure before trusting this on a real internet-facing server?

Please be brutal.

The goal of version 0.2.1 is not to prove that Agentix Lite is finished.

It is to give other developers something concrete enough to break.

That's a much better starting point.

Top comments (0)