<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Qyleron Engineering Log]]></title><description><![CDATA[Deep architectural breakdowns, performance benchmarks, and development updates from the engineers building EchidraOSS (the lightweight, high-performance open-source cyber deception network and real-time automated threat intelligence framework).]]></description><link>https://qyleron-blog.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6a7e0e020d2a17dc5eace17b/7877926d-0035-4682-b399-4ba143dd4c3f.png</url><title>Qyleron Engineering Log</title><link>https://qyleron-blog.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 24 Sep 2026 19:22:07 GMT</lastBuildDate><atom:link href="https://qyleron-blog.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Defending Against Automated Botnet Floods Without Degrading Container CPU Footprints]]></title><description><![CDATA[A botnet-driven connection flood doesn't just threaten availability, it can quietly double or triple your container CPU bill before anyone notices, depending entirely on how your listener is built. Th]]></description><link>https://qyleron-blog.hashnode.dev/defending-botnet-floods-container-cpu</link><guid isPermaLink="true">https://qyleron-blog.hashnode.dev/defending-botnet-floods-container-cpu</guid><category><![CDATA[Security]]></category><category><![CDATA[cybersecurity]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Devops]]></category><category><![CDATA[containers]]></category><dc:creator><![CDATA[Qyleron]]></dc:creator><pubDate>Sat, 19 Sep 2026 14:21:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a7e0e020d2a17dc5eace17b/c0b7e95a-f28e-47ee-9e5b-e5a83f59ae1b.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A botnet-driven connection flood doesn't just threaten availability, it can quietly double or triple your container CPU bill before anyone notices, depending entirely on how your listener is built. This guide compares how synchronous blocking engines and asynchronous event loops handle the same flood at the kernel level, and why that difference determines whether your infrastructure degrades gracefully or falls over.</p>
<p>When people talk about surviving a connection flood, the conversation usually jumps straight to rate limiting and network-layer mitigation. Those matter, but they don't explain why two services under the exact same flood, same packet rate, same source distribution, can show wildly different CPU graphs. That difference comes down to a decision made long before the flood ever starts: how the listener itself is architected to handle concurrent connections.</p>
<p>This matters directly to SREs and cloud architects because CPU footprint under load determines your autoscaling behavior, your bin-packing density, and ultimately your infrastructure cost during exactly the kind of event you can't schedule around.</p>
<h2>1. How synchronous blocking engines spend kernel CPU</h2>
<p><strong>Synchronous, thread-per-connection listeners spend rising kernel CPU on context-switching as connection count grows, independent of how much real traffic those connections carry.</strong> A synchronous, thread-per-connection (or process-per-connection) architecture handles concurrency by handing each incoming connection its own thread, which then blocks on I/O until that connection has data to process. This model is simple to reason about, but it has a structural cost that only becomes visible under load: every thread the kernel schedules carries real overhead, independent of whether that thread is doing useful work.</p>
<ul>
<li><p><strong>Context switching dominates under high concurrency.</strong> Once thread count climbs into the thousands, the kernel spends a growing share of every CPU cycle just deciding which thread runs next, rather than executing application code. This overhead scales with connection count, not with actual traffic volume.</p>
</li>
<li><p><strong>Each thread carries a fixed memory and scheduling cost.</strong> A blocked thread waiting on a slow or intentionally slow-drip connection, exactly what a flood does, still occupies a kernel scheduling slot and its full stack allocation, even while doing nothing.</p>
</li>
<li><p><strong>Lock contention compounds under pressure.</strong> Many synchronous frameworks serialize access to shared state, connection pools, logging buffers, rate-limit counters, behind a lock. As thread count rises, contention on that lock rises with it, and CPU that should be doing I/O work goes into spinning or waiting on the lock instead.</p>
</li>
</ul>
<p>The practical result: CPU usage under a synchronous architecture rises non-linearly as connection count increases, because each additional connection adds fixed kernel-level overhead on top of whatever actual work it represents. A flood of mostly-idle, slow-drip connections, the kind a botnet is cheap to generate, is close to a worst case for this model, since it maximizes thread count while minimizing actual useful throughput per thread.</p>
<blockquote>
<p>A useful diagnostic: if CPU usage during an incident tracks connection count more closely than it tracks request throughput, that's a strong sign the bottleneck is architectural, kernel scheduling and context-switch overhead, rather than application logic actually working harder.</p>
</blockquote>
<h2>2. How async event loops absorb the same flood</h2>
<p><strong>Async event-loop architectures decouple CPU cost from connection count, since a small fixed pool of threads multiplexes across thousands of connections via non-blocking I/O.</strong> An asynchronous, event-loop-based architecture handles concurrency differently: a small, fixed number of OS threads, often just one per CPU core, cooperatively multiplexes across thousands of connections using non-blocking I/O and an event notification mechanism like <code>epoll</code> on Linux.</p>
<ul>
<li><p><strong>Connection count no longer drives thread count.</strong> Whether the event loop is juggling 100 connections or 100,000, it's still running on the same small, fixed set of OS threads, so the kernel-level context-switching cost that scales with connection count in the synchronous model simply doesn't apply here.</p>
</li>
<li><p><strong>Idle connections cost almost nothing.</strong> A connection with no data to process just sits in the kernel's I/O readiness table until it has something to report. It doesn't hold a thread, a stack, or a scheduling slot the way a blocked synchronous thread does.</p>
</li>
<li><p><strong>CPU usage tracks actual work, not connection count.</strong> Because the event loop only spends cycles on connections that have real I/O ready to process, CPU usage under an async architecture scales much more closely with actual request throughput than with the raw number of open connections.</p>
</li>
</ul>
<p>This is precisely why a slow-drip flood, designed to maximize open connection count while minimizing real work per connection, is far less effective against an async architecture: the attack's core mechanism, exhausting per-connection kernel resources, doesn't have the same leverage when connections aren't consuming dedicated threads in the first place.</p>
<h2>3. What this means for container CPU footprints specifically</h2>
<p><strong>A synchronous listener's container CPU limit must be sized for worst-case connection count, while an async listener's CPU footprint stays close to its normal-load baseline even under a flood.</strong> In a containerized environment, this architectural difference has a direct, measurable cost implication. A container running a synchronous, thread-per-connection listener needs its CPU limit sized for worst-case concurrent connection count, not average throughput, because that's what drives its actual CPU consumption under load. Undersize it, and a flood causes CPU throttling that degrades every other workload sharing that node; oversize it defensively, and you're paying for headroom that sits idle outside of incident windows.</p>
<p>An async architecture decouples those two numbers. Its CPU footprint under a connection flood stays much closer to its footprint under normal load, because the mechanism driving synchronous CPU spikes, kernel scheduling overhead scaling with thread count, isn't in play. This makes capacity planning materially simpler: you size for expected request throughput, not for a worst-case connection count that a botnet can generate for free.</p>
<h2>4. Operational signals worth tracking</h2>
<p><strong>A synchronous listener's CPU usage tracks connection count under a flood, while an async listener's CPU usage stays tied to actual request throughput.</strong></p>
<ul>
<li><p><strong>Ratio of open connections to CPU usage.</strong> A synchronous listener will show this ratio climbing during a flood; an async one should stay comparatively flat, since connection count and CPU cost are decoupled by design.</p>
</li>
<li><p><strong>Kernel context-switch rate</strong> (visible via <code>vmstat</code> or <code>/proc/stat</code> on the host) during a suspected flood. A sharp rise here, disproportionate to actual request volume, points directly at thread-per-connection overhead rather than application-layer load.</p>
</li>
<li><p><strong>Container throttling events</strong> correlated with connection count rather than request rate. If your orchestrator's CPU throttling metric spikes in lockstep with concurrent connections rather than requests per second, the listener architecture is very likely the root cause, not insufficient CPU limits.</p>
</li>
<li><p><strong>Thread count inside the container</strong> during an incident. A synchronous architecture will show thread count scaling directly with connection count; an async one will show a flat, small thread count regardless of load.</p>
</li>
</ul>
<p>This same asymmetry is what makes a lightweight async decoy service a useful tool for absorbing and studying flood traffic directly: because it doesn't spend a thread per connection, it can safely accept far more concurrent flood traffic on the same CPU footprint as a normal service, generating detailed telemetry on the attack instead of falling over from it. This is the same architectural principle <a href="https://github.com/Qyleron/EchidraOSS">Echidra OSS</a> is built on.</p>
<p>The floods this architecture absorbs most often originate from the same botnet infrastructure covered in <a href="https://qyleron.com/blog/ssh-bruteforce-botnet-patterns/">Analyzing SSH Brute-Force Patterns</a>, the credential-spraying stage there and the connection-flood stage here are frequently run from overlapping pools of compromised hosts.</p>
<h2>See it in action</h2>
<p>We ran a live SYN flood against a deployed Echidra OSS honeypot and measured what actually happened to its CPU and memory in real time.</p>
<p><a class="embed-card" href="https://www.youtube.com/watch?v=7fJcO2ziX-4">https://www.youtube.com/watch?v=7fJcO2ziX-4</a></p>

<p><strong>Result:</strong> CPU usage rose by only 0.4% during the flood, and memory (RSS) stayed below its pre-flood baseline throughout. No container restarts, no degraded response times.</p>
<h2>See Deception-Based Detection in Action</h2>
<p><strong>Echidra OSS</strong> is an open-source cyber deception engine (AGPLv3). Deploy a lightweight decoy alongside your real infrastructure and map unauthorized access attempts to MITRE ATT&amp;CK in real time.</p>
<ul>
<li><p><strong>Lightweight:</strong> 83MB RAM floor vs 180MB for legacy honeypots.</p>
</li>
<li><p><strong>Scales cleanly:</strong> &lt;20% CPU under a 5,000-packet flood.</p>
</li>
</ul>
<p><a href="https://qyleron.com/setup-and-onboarding/">View the Deployment Guide</a> · <a href="https://github.com/Qyleron/EchidraOSS">Star it on GitHub</a></p>
]]></content:encoded></item><item><title><![CDATA[Benchmarking Cyber Deception: Why Legacy Honeypots Fail Under High Concurrency (And How EchidraOSS Fixes It)]]></title><description><![CDATA[A concurrency comparison of Cowrie, Thinkst Canary, and generic Twisted listeners against EchidraOSS's async FastAPI core. The difference shows up exactly when it matters most: under a connection floo]]></description><link>https://qyleron-blog.hashnode.dev/benchmarking-cyber-deception-why-legacy-honeypots-fail-under-high-concurrency-and-how-echidraoss-fixes-it</link><guid isPermaLink="true">https://qyleron-blog.hashnode.dev/benchmarking-cyber-deception-why-legacy-honeypots-fail-under-high-concurrency-and-how-echidraoss-fixes-it</guid><category><![CDATA[cybersecurity]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Python]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Qyleron]]></dc:creator><pubDate>Tue, 01 Sep 2026 04:51:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a7e0e020d2a17dc5eace17b/8e67c05b-3580-4fac-af74-ed73a3361fd3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A concurrency comparison of Cowrie, Thinkst Canary, and generic Twisted listeners against EchidraOSS's async FastAPI core. The difference shows up exactly when it matters most: under a connection flood.</p>
<p>Most honeypot evaluations compare feature checklists: protocol coverage, decoy realism, alerting integrations. Those details matter, but they skip the variable that actually decides whether a deception layer survives contact with the internet. That variable is simple: <strong>what happens to the listener process when a hundred connections arrive in the same second.</strong></p>
<p>This isn't a hypothetical. Mass-scanning botnets, credential-stuffing swarms, and internet-wide research crawlers such as Shodan and Censys don't trickle in. They arrive in bursts. A deception system that can't absorb a burst doesn't fail gracefully. It drops connections, skips logging, or pins a CPU core, and that's precisely the moment you lose the session data the honeypot was deployed to capture.</p>
<p>We built EchidraOSS around that failure mode instead of treating it as an afterthought. This post breaks down why the dominant architectures in the honeypot space, including Cowrie's Twisted reactor, Thinkst Canary's managed appliance model, and the raw Twisted or socket listeners teams often build themselves, hit a concurrency ceiling. Then it covers what EchidraOSS does differently at the architecture level.</p>
<h2>1. Asynchronous concurrency: the event loop is the whole story</h2>
<p>Cowrie is built on Twisted's reactor pattern, an event-driven model that predates <code>asyncio</code> by over a decade. In practice, Twisted-based listeners accumulate per-connection state and callback chains that get expensive to schedule as connection count climbs. The reactor is single-threaded, so a burst of new SSH negotiations competes for the same loop turn as everything already in flight. Generic, self-built Twisted or raw-socket listeners inherit the same ceiling, usually a lower one, since they lack Cowrie's years of incremental tuning.</p>
<p>Thinkst Canary sidesteps the software problem by shipping a managed appliance. It solves concurrency by dedicating hardware or a locked-down VM to the problem, not by fixing the underlying model. That approach works, and it's also why Canary is priced and deployed as infrastructure rather than as software you drop into an existing box.</p>
<p>EchidraOSS's listeners and API run on <strong>Python 3.11+ with FastAPI's async event loop</strong>. Every connection handler is a coroutine, not a thread and not a blocking socket read. When a scanning bot opens fifty SSH connections in the same second, those connections don't queue behind each other waiting for a reactor tick to free up. They're all suspended on I/O at once, and the loop services whichever one has data ready, in the order the operating system delivers it. There's no thread-per-connection overhead, no GIL contention from spawning worker threads, and no context-switch cost that scales linearly with load.</p>
<blockquote>
<p>The practical effect: connection spikes and automated bot floods move CPU utilization in small, bounded steps. There's no sawtooth spike, because the listener isn't fighting its own scheduler for cycles.</p>
</blockquote>
<br />

<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/0e773sx2cu5wo6wm9tze.png" alt="Line chart comparing CPU utilization over time under a simulated connection flood: legacy thread-per-connection architecture spikes to near 100 percent while EchidraOSS's async event loop stays under 20 percent" style="display:block;margin:0 auto" />

<p><strong>Fig. 1:</strong> CPU utilization over time under a simulated connection flood, thread-per-connection versus async event loop.</p>
<h2>2. Low memory footprint: deployable on a $4 a month VPS</h2>
<p>Concurrency headroom only helps if it doesn't cost you a fleet of memory. This is where Cowrie's process model and self-built Twisted listeners tend to sprawl. Every open session carries its own buffered state and protocol objects, and Cowrie specifically adds a simulated filesystem context per connection. That overhead adds up fast once you're running SSH, HTTP, FTP, and Telnet decoys side by side.</p>
<p>EchidraOSS keeps its footprint down through <strong>modular service design</strong>. Listeners, the classification engine, and the dashboard API are separable processes that share a single PostgreSQL instance instead of each maintaining redundant in-memory state. Session objects are short-lived: they exist as coroutine-local state for the duration of the connection, get classified, get written to Postgres, and are released. Nothing accumulates in a long-running process heap over the life of the deployment.</p>
<p>That design is exactly why the reference deployment path (see the <a href="https://qyleron.com/setup-and-onboarding/">Setup and Onboarding guide</a>) runs comfortably as a single Docker Compose stack, honeypot listeners, dashboard API, and PostgreSQL together, on a 1 vCPU, 1GB RAM instance that costs about <strong>$4 a month</strong> from any major cloud provider. You're not provisioning for a worst-case memory ceiling that never gets hit. The architecture doesn't have one to provision for.</p>
<br />

<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/smqmna701u5yk0ke9uc6.png" alt="Bar chart comparing RAM usage: legacy Twisted/threaded honeypots at 180MB versus EchidraOSS's FastAPI async stack at 83MB" style="display:block;margin:0 auto" />

<p><strong>Fig. 2:</strong> RAM usage of the full stack, legacy Twisted/threaded honeypots versus EchidraOSS's FastAPI async stack.</p>
<h2>3. Real-time log classification: no post-processing pipeline required</h2>
<p>This is the gap that matters most once you're running one of these tools in production. Cowrie, generic Twisted listeners, and most self-built setups write <strong>unstructured text logs</strong>, whether that's JSON lines or flat text, with no semantic layer on top. Turning "attacker ran <code>cat /etc/passwd</code>, then downloaded a binary with <code>wget</code>" into "this was a credential-harvesting attempt followed by second-stage payload retrieval, MITRE T1552 and T1105" is left entirely to you: a Logstash pipeline, a Splunk query, or a homegrown script that somebody has to maintain and retune every time attacker tooling shifts. Thinkst Canary does better here with its own hosted console, but the classification logic is closed. You get an alert, not an inspectable rule trail.</p>
<p>EchidraOSS classifies sessions <strong>natively, at capture time</strong>, using a deterministic rule engine. It's not an LLM call and not a black box. Every closed session runs through classification rules that assign an actor label (for example, <code>brute_force_bot</code>), an intent such as reconnaissance, credential theft, or persistence, a risk score from 0 to 100, and one or more <strong>MITRE ATT&amp;CK technique IDs</strong>, all before the session reaches a dashboard query. Because the rules are deterministic pattern matches over observed command and request behavior rather than a model inference call, classification resolves in the sub-millisecond range per session. There's no network round trip, no queueing on an inference server, and no variance from a probabilistic model changing its answer between runs.</p>
<p>The downstream result is what the <a href="https://qyleron.com/console-guide/">Console Guide</a> calls <strong>Intelligence</strong>. Instead of a flat firehose of logs, recurring actor-and-technique pairs get grouped into issues with evidence, a recommended fix, and an impact summary, pulled from the same built-in playbook that generated the classification. The reasoning behind every flag stays inspectable, not a mystery.</p>
<br />

<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/v9yxrhupl250hn8p87pi.png" alt="EchidraOSS Issue Intelligence dashboard showing a table of grouped attacker issues by MITRE ATT&amp;CK technique, with the Issue Detail panel open showing Evidence, Recommended Fix, and Impact" style="display:block;margin:0 auto" />

<p><strong>Fig. 3:</strong> The Intelligence view grouping recurring sessions into issues by actor and MITRE ATT&amp;CK technique.</p>
<h2>Head-to-head: architecture</h2>
<p>The table below reflects the structural properties of each project's public architecture, plus EchidraOSS's own internal load testing on a reference 1 vCPU, 1GB VPS. Treat the figures as directional. The point isn't the third decimal place. It's the order of magnitude, and the order of magnitude comes directly from the concurrency model, not from tuning.</p>
<p><em>Methodology note: EchidraOSS figures come from Qyleron's own internal load testing described above. Cowrie and Thinkst Canary figures are drawn from each project's public documentation and community-reported deployment experience, not from independent lab testing we ran ourselves. Treat them as representative, not as a controlled benchmark.</em></p>
<table>
<thead>
<tr>
<th>METRIC</th>
<th>COWRIE</th>
<th>THINKST CANARY</th>
<th>GENERIC TWISTED LISTENER</th>
<th>EchidraOSS</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Concurrency model</strong></td>
<td>Twisted reactor, single-threaded event loop, prone to blocking</td>
<td>Managed appliance, concurrency solved by hardware and not exposed</td>
<td>Twisted or raw sockets, usually thread per connection</td>
<td><strong>Python 3.11, FastAPI async event loop</strong></td>
</tr>
<tr>
<td><strong>Memory footprint</strong></td>
<td>Roughly 250 to 400MB idle, climbs with concurrent simulated-filesystem sessions</td>
<td>Not applicable. Fixed appliance or VM allocation, not user-tunable</td>
<td>200MB or more idle, scales roughly linearly with open threads</td>
<td><strong>Roughly 90 to 130MB for the full stack (listeners and API) on a 1GB VPS</strong></td>
</tr>
<tr>
<td><strong>Parsing and classification speed</strong></td>
<td>None native. Offline log parsing required</td>
<td>Proprietary, cloud-side, latency not disclosed</td>
<td>None native. Raw logs only</td>
<td><strong>Sub-millisecond, deterministic, resolved in-process at session close</strong></td>
</tr>
<tr>
<td><strong>MITRE ATT&amp;CK mapping</strong></td>
<td>Not built in. Requires external tooling</td>
<td>Alerting only, no exposed technique-level mapping</td>
<td>Not built in</td>
<td><strong>Native, per session, with an inspectable evidence trail</strong></td>
</tr>
<tr>
<td><strong>Deployment overhead</strong></td>
<td>Manual Python, Twisted, and log pipeline setup</td>
<td>Vendor-managed hardware or appliance provisioning</td>
<td>Fully manual, no packaged deployment path</td>
<td><strong>A single</strong> <code>docker compose up</code><strong>, or systemd on a bare VM</strong></td>
</tr>
<tr>
<td><strong>Minimum viable hardware</strong></td>
<td>Small VM, though the log pipeline usually needs a second host</td>
<td>Dedicated appliance or cloud instance, vendor-sized</td>
<td>Small VM, degrades under concurrent load</td>
<td><strong>1 vCPU, 1GB: about a $4 a month cloud VPS</strong></td>
</tr>
</tbody></table>
<h2>Why this is an architecture problem, not a tuning problem</h2>
<p>You can add more RAM to Cowrie, move Canary to a larger tier, or hand-tune a Twisted reactor's thread pool. None of that changes the underlying model. You'd be compensating for blocking I/O and unstructured logging with infrastructure spend instead of fixing what's actually blocking. EchidraOSS's advantage isn't a set of tuning flags. The async event loop and the deterministic classifier are load-bearing parts of the design from the first connection handler onward. There's no fast path that degrades to a slow path under load. The async model is the path.</p>
<p>That's also why the comparison holds up on cheap hardware. A honeypot that only performs well on generously provisioned infrastructure isn't solving the deployment problem most teams actually have: running deception at the edges of their network, cheaply, in enough places that coverage is real instead of symbolic.</p>
<blockquote>
<h3>📦 Run it yourself</h3>
<p><strong>EchidraOSS</strong> is open-source under AGPLv3. Clone it, point it at a $4/month VPS, and watch the classification engine work in real time.</p>
<p>👉 <a href="https://github.com/Qyleron/EchidraOSS"><strong>Clone the GitHub Repository</strong></a></p>
</blockquote>
]]></content:encoded></item></channel></rss>