<?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>Forem: Douxx</title>
    <description>The latest articles on Forem by Douxx (@douxxtech).</description>
    <link>https://forem.com/douxxtech</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%2F3758859%2Fad212daa-edd8-4191-b6ef-8959803c14c8.jpeg</url>
      <title>Forem: Douxx</title>
      <link>https://forem.com/douxxtech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/douxxtech"/>
    <language>en</language>
    <item>
      <title>An Attempt to Ban Bad Bots Crawling My Sites</title>
      <dc:creator>Douxx</dc:creator>
      <pubDate>Tue, 31 Mar 2026 17:23:16 +0000</pubDate>
      <link>https://forem.com/douxxtech/an-attempt-to-ban-bad-bots-crawling-my-sites-2lhg</link>
      <guid>https://forem.com/douxxtech/an-attempt-to-ban-bad-bots-crawling-my-sites-2lhg</guid>
      <description>&lt;p&gt;I don't really like &lt;strong&gt;bad bots&lt;/strong&gt;, and by that I mean crawlers that don't care about &lt;a href="https://www.robotstxt.org/" rel="noopener noreferrer"&gt;robots.txt&lt;/a&gt;. The reason is simple: I don't want my data fed into obscure systems, and also just by principle, &lt;em&gt;if we give you rules, follow them&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Credit where it's due: the idea came from &lt;a href="https://caolan.uk/" rel="noopener noreferrer"&gt;Caolan's website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The idea is simple: make the bad bots click a link they aren't supposed to, then ban them. To do that, I added a &lt;code&gt;robots.txt&lt;/code&gt; at the root of my site, explicitly disallowing robots from a specific page (I went with &lt;code&gt;/roboty/&lt;/code&gt;, because why not):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User-agent: *
Disallow: /roboty/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I slipped a link to that page somewhere on the root page.&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%2Fimages.dbo.one%2Ff2419bf9" 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%2Fimages.dbo.one%2Ff2419bf9" alt="link" width="484" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I don't want curious humans getting instantly banned, the page itself just explains what's going on and links to &lt;code&gt;article.php&lt;/code&gt;, the actual dangerous script. I named it like that to bypass possible keyword blacklists like &lt;code&gt;ban&lt;/code&gt; or &lt;code&gt;ban-ip&lt;/code&gt;. &lt;code&gt;¯\_(ツ)_/¯&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Talking about the script, here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="nv"&gt;$cf_api_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$zone_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Auto banned by dtech/roboty at '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"H:i d/m/y"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REMOTE_ADDR'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'mode'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'configuration'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'target'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ip'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'notes'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$note&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$ch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://api.cloudflare.com/client/v4/zones/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$zone_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/firewall/access_rules/rules"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;CURLOPT_POST&lt;/span&gt;           &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;CURLOPT_POSTFIELDS&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;CURLOPT_IPRESOLVE&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;CURL_IPRESOLVE_V4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$cf_api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;curl_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Location: /?blehhhhh"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// redirect to '/', should be blocked&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Bye ;)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right now it only bans the bot's IP on &lt;code&gt;douxx.tech&lt;/code&gt; (proxied through Cloudflare), but I plan to eventually implement it into an internal API to block across every domain I own, and maybe throw in some &lt;code&gt;iptables&lt;/code&gt; rules too.&lt;/p&gt;

&lt;p&gt;So yeah, I'll keep it running for a bit and see how many IPs we get.&lt;br&gt;&lt;br&gt;
For the record, the first one to be banned is an IP from &lt;a href="https://en.wikipedia.org/wiki/Tencent" rel="noopener noreferrer"&gt;Tencent&lt;/a&gt; datacenters 🤡&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%2Fimages.dbo.one%2F2e254199" 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%2Fimages.dbo.one%2F2e254199" alt="tencent ipban" width="512" height="122"&gt;&lt;/a&gt;&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%2Fimages.dbo.one%2F28a91d76" 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%2Fimages.dbo.one%2F28a91d76" alt="ip info screenshot" width="652" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web</category>
      <category>development</category>
      <category>php</category>
      <category>crawlers</category>
    </item>
    <item>
      <title>Building a Web Server from Scratch (No, Actually)</title>
      <dc:creator>Douxx</dc:creator>
      <pubDate>Mon, 16 Mar 2026 00:20:44 +0000</pubDate>
      <link>https://forem.com/douxxtech/building-a-web-server-from-scratch-no-actually-2o38</link>
      <guid>https://forem.com/douxxtech/building-a-web-server-from-scratch-no-actually-2o38</guid>
      <description>&lt;p&gt;When I say from scratch, I &lt;em&gt;mean&lt;/em&gt; it. No frameworks, no &lt;code&gt;node_modules&lt;/code&gt; taking 500MB of disk space, no runtime. Just you, and your Linux kernel.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Bit of Context
&lt;/h2&gt;

&lt;p&gt;Exactly one week ago, I was in my NoSQL class, and got bored, like, really. And what does a sane person do when they're bored? Certainly not learn assembly. But that's what I did.&lt;/p&gt;

&lt;p&gt;To be honest, the idea had been running on my mind for some time already. So I said to myself that it could be more interesting than reading papers about MongoDB, and looked for a guide.&lt;/p&gt;

&lt;p&gt;I directly found &lt;a href="https://github.com/0xAX/asm" rel="noopener noreferrer"&gt;this guide from Alex Kuleshov&lt;/a&gt;, and started reading. That afternoon, I read about 3 posts instead of listening to my teacher, and then went home.&lt;/p&gt;

&lt;p&gt;Since I didn't want to digest more theory that day, I decided to do some practice. You learn more from random &lt;a href="https://en.wikipedia.org/wiki/Segmentation_fault" rel="noopener noreferrer"&gt;segfaults&lt;/a&gt; than from pages of theory.&lt;/p&gt;

&lt;p&gt;The guide didn't cover exercises + answers, so I decided to use the thing that will probably steal my job in a few years: &lt;a href="https://claude.ai" rel="noopener noreferrer"&gt;Claude&lt;/a&gt;. Even if it can't &lt;em&gt;(yet)&lt;/em&gt; write good assembly code, it can create "good" exercises and correct them. So I spent the evening doing that.&lt;/p&gt;

&lt;p&gt;The next day, I continued the course and read the final chapters. After that, I felt like I knew enough things but had clearly not enough practice.&lt;/p&gt;

&lt;p&gt;And damn, I was so right.&lt;/p&gt;

&lt;p&gt;I decided to create an HTTP client to train. Basically curl, but with no other feature than &lt;code&gt;get&lt;/code&gt;-ing pages. It was a horror. Every time I took a step forward, I took three steps back due to code that stopped working, mostly because of those damn &lt;a href="https://en.wikipedia.org/wiki/Processor_register" rel="noopener noreferrer"&gt;CPU registers&lt;/a&gt; &lt;code&gt;&amp;gt;:[&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Well, after a bit of practice, I got something working:&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%2Fimages.dbo.one%2Fc1012703%2F" 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%2Fimages.dbo.one%2Fc1012703%2F" alt="asmclient" width="683" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One day passes, we're now Tuesday, 10AM. My next project was pretty obvious: a web server. What's the point of having a web client without one?&lt;/p&gt;

&lt;p&gt;So in the rest of this article, I'll explain how I built &lt;a href="https://nasmserver.douxx.tech/" rel="noopener noreferrer"&gt;NASMServer&lt;/a&gt;, the 95% &lt;a href="https://nasm.us" rel="noopener noreferrer"&gt;NetWide assembly&lt;/a&gt; web server that runs &lt;a href="https://douxx.tech" rel="noopener noreferrer"&gt;douxx.tech&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quick note: I won't talk &lt;em&gt;assembly&lt;/em&gt; in this article. It would require &lt;em&gt;you, the reader&lt;/em&gt;, to have knowledge about it, and it isn't needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ok, let's start!&lt;/p&gt;
&lt;h2&gt;
  
  
  The Basics
&lt;/h2&gt;

&lt;p&gt;This article covers &lt;strong&gt;only&lt;/strong&gt; x86_64 Linux. Any other OS or architecture would have different instructions.&lt;/p&gt;

&lt;p&gt;I'll try to avoid talking directly in assembly, but I'll regularly add links to the relevant parts on the GitHub repo. You don't need assembly knowledge, but you might need some about Linux and programming in general.&lt;/p&gt;

&lt;p&gt;Two things to keep in mind before we continue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In Linux, &lt;a href="https://en.wikipedia.org/wiki/Everything_is_a_file" rel="noopener noreferrer"&gt;everything is a file&lt;/a&gt; (&lt;a href="https://dev.to/eteimz/everything-is-a-file-explained-g2a"&gt;Dev.to article&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;We talk to the Linux kernel using &lt;a href="https://en.wikipedia.org/wiki/System_call" rel="noopener noreferrer"&gt;System Calls&lt;/a&gt;, the &lt;em&gt;bridges&lt;/em&gt; between our application and the hardware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a system call in &lt;a href="https://en.wikipedia.org/wiki/C_(programming_language)" rel="noopener noreferrer"&gt;C&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;sys/syscall.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello, world!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SYS_write&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// fd=1, buffer, length&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's one in NASM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_start:
    mov rax, 1    ; syscall number for write
    mov rdi, 1    ; fd = 1 (stdout)
    mov rsi, msg  ; buffer
    mov rdx, len  ; length
    syscall       ; call kernel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;System calls will be the only thing we use for I/O, so make sure you're comfortable with them. Here's the &lt;a href="https://filippo.io/linux-syscall-table/" rel="noopener noreferrer"&gt;full Linux x86_64 syscalls table&lt;/a&gt; for reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Logic
&lt;/h2&gt;

&lt;p&gt;Before writing a single line, you need to plan what the program will do and leave the &lt;em&gt;how&lt;/em&gt; to your future self. Here's what I planned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ Listen to a port&lt;/li&gt;
&lt;li&gt;☐ Wait for requests, and accept them&lt;/li&gt;
&lt;li&gt;☐ Read the content&lt;/li&gt;
&lt;li&gt;☐ Parse the HTTP request&lt;/li&gt;
&lt;li&gt;☐ Read the requested file&lt;/li&gt;
&lt;li&gt;☐ Send a HTTP response back, with the file content&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Listen To a Port
&lt;/h2&gt;

&lt;p&gt;The first thing we need is something clients can connect to and "talk with us": a &lt;a href="https://en.wikipedia.org/wiki/Network_socket" rel="noopener noreferrer"&gt;TCP Socket&lt;/a&gt;. It's, well, a &lt;em&gt;file&lt;/em&gt;, and it's basically the way the client says "I'm here, and I want to talk to X application".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/program.asm#L82-L93" rel="noopener noreferrer"&gt;[-&amp;gt; program.asm]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating the socket alone isn't enough though. It exists, it &lt;em&gt;can&lt;/em&gt; do its job, but it isn't accessible to anyone yet. We need to bind it to a port and an interface.&lt;/p&gt;

&lt;p&gt;The interface is one of the IP addresses available to the system: &lt;code&gt;127.0.0.1&lt;/code&gt;, &lt;code&gt;192.168.x.x&lt;/code&gt;, etc. To simplify our lives, we'll use &lt;code&gt;0.0.0.0&lt;/code&gt;, "listen on every interface". The port is a value between 1 and 65535, and HTTP usually lives on &lt;code&gt;80&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We give the kernel the socket &lt;a href="https://en.wikipedia.org/wiki/File_descriptor" rel="noopener noreferrer"&gt;file descriptor&lt;/a&gt; and the interface + port to bind to. It either returns &lt;code&gt;0&lt;/code&gt; (done), or a negative error code, usually meaning the port is already in use on the given interface, or we don't have enough permissions (&lt;code&gt;&amp;lt; 1024&lt;/code&gt; ports require root).&lt;/p&gt;

&lt;p&gt;Finally, we tell the kernel we're ready to listen with the &lt;a href="https://manpages.debian.org/unstable/manpages-dev/listen.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;listen&lt;/code&gt;&lt;/a&gt; syscall.&lt;br&gt;
&lt;a href="https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/program.asm#L109-L127" rel="noopener noreferrer"&gt;[-&amp;gt; program.asm]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a socket: &lt;a href="https://manpages.debian.org/unstable/manpages-dev/socket.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;socket&lt;/code&gt;&lt;/a&gt; syscall&lt;/li&gt;
&lt;li&gt;Bind it: &lt;a href="https://manpages.debian.org/unstable/manpages-dev/bind.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;bind&lt;/code&gt;&lt;/a&gt; syscall&lt;/li&gt;
&lt;li&gt;Start listening: &lt;a href="https://manpages.debian.org/unstable/manpages-dev/listen.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;listen&lt;/code&gt;&lt;/a&gt; syscall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And just like that, we're reachable on &lt;code&gt;0.0.0.0:80&lt;/code&gt;!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Listen to a port&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Wait For Requests, And Accept Them
&lt;/h2&gt;

&lt;p&gt;This is where the main loop lives:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```plain text&lt;br&gt;
[Wait for a request] --&amp;gt; [Accept it] --&amp;gt; [Handle it (explained later)] --&amp;gt; |&lt;br&gt;
        ^------------------------------------------------------------------+&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The [`accept`](https://manpages.debian.org/unstable/manpages-dev/accept.2.en.html) syscall handles both waiting (blocking) and accepting in one shot. And guess what it returns? A file!!
[[-&amp;gt; program.asm]](https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/program.asm#L142-L156)

That file is the private space where we and the client will talk to each other.

- ☑ ~~Wait for requests, and accept them~~

## Read The Client Request

The "private space" file contains the request the client wrote. Reading it is easy: use the [`read`](https://manpages.debian.org/unstable/manpages-dev/read.2.en.html) syscall and dump it into a buffer.

[[-&amp;gt; program.asm]](https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/program.asm#L222)
[[-&amp;gt; fileutils.asm]](https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/macros/fileutils.asm#L113-L121)

Then we check if it's a valid HTTP request. If not, we send back a [400 Bad Request](https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Status/400). A very minimal valid request looks like:



```plaintext
GET / HTTP/1.0
\r\n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which breaks down to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;METHOD&amp;gt; &amp;lt;path&amp;gt; &amp;lt;HTTP_VERSION&amp;gt;
&amp;lt;crlf&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a static server, we only handle &lt;code&gt;GET&lt;/code&gt;, and anything else gets a &lt;a href="https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Status/405" rel="noopener noreferrer"&gt;405 Method Not Allowed&lt;/a&gt;. If the method is valid, we parse the path and append it to the &lt;strong&gt;document root&lt;/strong&gt; (e.g. &lt;code&gt;/var/www/html&lt;/code&gt;), which is the directory we'll be serving files from.&lt;/p&gt;

&lt;p&gt;One important thing: path traversal prevention. In Linux, &lt;code&gt;..&lt;/code&gt; means "go to the previous directory", so a path like &lt;code&gt;/../../../opt/sensitive/passwords.txt&lt;/code&gt; appended to &lt;code&gt;/var/www/html&lt;/code&gt; would resolve to &lt;code&gt;/opt/sensitive/passwords.txt&lt;/code&gt;. Not great. We simply check for any &lt;code&gt;..&lt;/code&gt; in the path and drop the request with a &lt;a href="https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Status/403" rel="noopener noreferrer"&gt;403 Forbidden&lt;/a&gt; if we find one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/program.asm#L221-L278" rel="noopener noreferrer"&gt;[-&amp;gt; program.asm]&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/macros/httputils.asm" rel="noopener noreferrer"&gt;[-&amp;gt; httputils.asm]&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Read the content&lt;/del&gt;
&lt;/li&gt;
&lt;li&gt;☑ &lt;del&gt;Parse the HTTP request&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Read The Requested File
&lt;/h2&gt;

&lt;p&gt;We have a safe path, now let's actually get the file. A couple of things to handle first.&lt;/p&gt;

&lt;p&gt;If the client requested &lt;code&gt;/&lt;/code&gt;, we'd end up with &lt;code&gt;/var/www/html/&lt;/code&gt;, figure out it's a directory, and go crazy. So we &lt;em&gt;internally&lt;/em&gt; append an index file (e.g. &lt;code&gt;/index.html&lt;/code&gt;) to the path (no redirecting the client, I see you bad programs). This works for subdirectories too: &lt;code&gt;/home/&lt;/code&gt; becomes &lt;code&gt;/home/index.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;"But what about directories that don't end with &lt;code&gt;/&lt;/code&gt;?". Fair point, and we'll get there. For now, let's move on.&lt;/p&gt;

&lt;p&gt;We use the &lt;a href="https://manpages.debian.org/unstable/manpages-dev/stat.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;stat&lt;/code&gt;&lt;/a&gt; syscall to check if the file exists and what type it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn't exist → &lt;a href="https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Status/404" rel="noopener noreferrer"&gt;404 Not Found&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It's a directory → the trailing slash was missing, add it and loop back to the index-appending step&lt;/li&gt;
&lt;li&gt;It's a regular file but not readable → &lt;a href="https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Status/403" rel="noopener noreferrer"&gt;403 Forbidden&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Otherwise → continue!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/program.asm#L280-L326" rel="noopener noreferrer"&gt;[-&amp;gt; program.asm]&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/macros/fileutils.asm" rel="noopener noreferrer"&gt;[-&amp;gt; fileutils.asm]&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Read the requested file&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Send The Response
&lt;/h2&gt;

&lt;p&gt;All edge cases handled, time to actually send something. We write to the "private space" file, starting with the HTTP header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.0 200 OK
Server: NASMServer/1.0
Content-Type: text/html
Content-Length: 1442
Connection: close

[file content]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking it down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;HTTP/1.0 200 OK&lt;/code&gt;: static string, HTTP version + status code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Server: NASMServer/1.0&lt;/code&gt;: not required, but nice to have&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Content-Type: text/html&lt;/code&gt;: tells the client what it's receiving, must follow &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types" rel="noopener noreferrer"&gt;Media Types&lt;/a&gt; format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Content-Length: 1442&lt;/code&gt;: byte count of the response, grabbed from &lt;a href="https://manpages.debian.org/unstable/manpages-dev/stat.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;stat&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Connection: close&lt;/code&gt;: we won't keep the connection alive after sending&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\r\n&lt;/code&gt;: blank line separating header from body. HTTP uses &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/CRLF" rel="noopener noreferrer"&gt;&lt;code&gt;CRLF&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We write the header with &lt;a href="https://manpages.debian.org/unstable/manpages-dev/write.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;write&lt;/code&gt;&lt;/a&gt;, send the file content with &lt;a href="https://manpages.debian.org/unstable/manpages-dev/sendfile.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;sendfile&lt;/code&gt;&lt;/a&gt; (no manual copying needed), then close up with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://manpages.debian.org/unstable/manpages-dev/shutdown.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;shutdown&lt;/code&gt;&lt;/a&gt;: tell the client we're done&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://manpages.debian.org/unstable/manpages-dev/close.2.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;close&lt;/code&gt;&lt;/a&gt;: close the connection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then jump back to waiting. &lt;code&gt;:D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/douxxtech/nasmserver/blob/0f7cab0cbe27963e078fb7257371919416c107b9/program.asm#L496-L562" rel="noopener noreferrer"&gt;[-&amp;gt; program.asm]&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Send a HTTP response back, with the file content&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And just like that, we have a &lt;strong&gt;working HTTP 1.0 static file server&lt;/strong&gt;!!&lt;/p&gt;

&lt;h2&gt;
  
  
  And Now?
&lt;/h2&gt;

&lt;p&gt;I lied, but not entirely. This works, but it wouldn't survive being spammed. There's no proper per-request handling, so a request coming in while another is being processed will either be queued or dropped.&lt;/p&gt;

&lt;p&gt;The fix is to &lt;a href="https://manpages.debian.org/unstable/manpages-dev/fork.2.en.html" rel="noopener noreferrer"&gt;fork&lt;/a&gt; the process on each request, and the main process immediately goes back to waiting while the clone handles it. I won't go into detail here, but the code is there if you want to look!&lt;/p&gt;

&lt;p&gt;Other improvements are possible too, but this post only covers the basics. If you're interested, consider reading, starring, or contributing!&lt;br&gt;
&lt;a href="https://git.douxx.tech/nasmserver/" rel="noopener noreferrer"&gt;Github:douxxtech/nasmserver&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The logic explanation ends here, feel free to leave now. Otherwise, let's talk numbers.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Fast Is It?
&lt;/h2&gt;

&lt;p&gt;Three servers, three environments, same file, no TLS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NASMServer&lt;/strong&gt;: fully built in assembly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BusyBox HTTPD&lt;/strong&gt;: a really small HTTP server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache2&lt;/strong&gt;: one of the most used web servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Speed measured with cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"
DNS: %{time_namelookup}s
Connect: %{time_connect}s
TLS: %{time_appconnect}s
Start Transfer: %{time_starttransfer}s
Total: %{time_total}s
&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Each command is run 10 times, results are averaged.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Environments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;localhost&lt;/code&gt;: staying on the machine&lt;/li&gt;
&lt;li&gt;Windows &amp;lt;&amp;gt; WSL: servers running in Fedora WSL, testing the virtual interface&lt;/li&gt;
&lt;li&gt;Local network: fetching over LAN&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Localhost&lt;/th&gt;
&lt;th&gt;Windows Host&lt;/th&gt;
&lt;th&gt;Network&lt;/th&gt;
&lt;th&gt;Average&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BusyBox HTTPD&lt;/td&gt;
&lt;td&gt;0.0004677s&lt;/td&gt;
&lt;td&gt;0.0075919s&lt;/td&gt;
&lt;td&gt;0.0038408s&lt;/td&gt;
&lt;td&gt;0.0039668s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NASMServer&lt;/td&gt;
&lt;td&gt;0.0005997s&lt;/td&gt;
&lt;td&gt;0.0082924s&lt;/td&gt;
&lt;td&gt;0.0076072s&lt;/td&gt;
&lt;td&gt;0.0054998s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache2&lt;/td&gt;
&lt;td&gt;0.0004769s&lt;/td&gt;
&lt;td&gt;0.0102861s&lt;/td&gt;
&lt;td&gt;0.0062916s&lt;/td&gt;
&lt;td&gt;0.0056849s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;BusyBox HTTPD&lt;/strong&gt; wins across the board. NASMServer holds its own on localhost but falls behind on the network. Apache2 is the slowest on the Windows host by a noticeable margin, which makes sense given its heavier feature set.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NASMServer and Apache2 being slower over WSL than over LAN is likely due to WSL's virtual network interface adding overhead that a direct LAN connection doesn't have. Not 100% sure on that though.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Final Words
&lt;/h2&gt;

&lt;p&gt;I really loved building this project, writing this article, and learning assembly. I'll keep updating the server, so if you have feature ideas, bug reports, etc. feel free to reach out via &lt;a href="https://git.douxx.tech/nasmserver" rel="noopener noreferrer"&gt;GitHub issues&lt;/a&gt;, the &lt;a href="https://dev.to/douxxtech/building-a-web-server-from-scratch-no-actually-2o38"&gt;dev.to&lt;/a&gt; comments, or &lt;a href="mailto:douxx@douxx.tech"&gt;mail&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Would I recommend NASMServer in production? For god's sake, &lt;strong&gt;NO!&lt;/strong&gt;&lt;br&gt;
Did I do it? &lt;em&gt;Maybe.&lt;/em&gt;&lt;br&gt;
Will I regret it? &lt;em&gt;Surely.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But remember, I started this because I was bored in a NoSQL class.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>assembly</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Bringing Web Radios Back to FM</title>
      <dc:creator>Douxx</dc:creator>
      <pubDate>Sat, 28 Feb 2026 12:00:29 +0000</pubDate>
      <link>https://forem.com/douxxtech/bringing-web-radios-back-to-fm-eai</link>
      <guid>https://forem.com/douxxtech/bringing-web-radios-back-to-fm-eai</guid>
      <description>&lt;p&gt;When going on vacation, I listen to local radios stations using a small portable radio that I bring with me. I absolutely love doing this as the music often changes of what I'm used to, and it makes me discover new things.&lt;/p&gt;

&lt;p&gt;One of those radios I love listening to is the &lt;a href="https://rtl.it" rel="noopener noreferrer"&gt;RTL 102.5&lt;/a&gt;, an italian radio. I always listen to it when I go on a trip in Italy. However, it only is a national station and doesn't broadcast in other countries such as Switzerland.&lt;/p&gt;

&lt;p&gt;A good way of continuing to listen to programs that aren't broadcasted on FM are &lt;a href="https://en.wikipedia.org/wiki/Internet_radio" rel="noopener noreferrer"&gt;web radios&lt;/a&gt;, and you'll ask me, why don't I want to listen to them ? They're near perfection, they're live, have a good audio quality, and much more !&lt;br&gt;&lt;br&gt;
And the answer is in the question. &lt;em&gt;They're perfect&lt;/em&gt;. I find that this perfection breaks the charm of FM. Having lossless 96kHz in your headset doesn't have the same &lt;em&gt;vibe&lt;/em&gt; at all than getting a signal from a tower being at hundreds of kilometers from your tiny 15 bucks portable radio.&lt;/p&gt;

&lt;p&gt;So in this article, I'll try to &lt;strong&gt;take a live stream from the RTL 102.5 web radio, and broadcast it in my house, on FM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As always, here is an overview of what I'll be doing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ Finding a way of broadcasting FM on a short range&lt;/li&gt;
&lt;li&gt;☐ Getting the audio source for the web radio stream&lt;/li&gt;
&lt;li&gt;☐ Putting both together&lt;/li&gt;
&lt;li&gt;☐ Automate everything&lt;/li&gt;
&lt;li&gt;☐ Enjoy !&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Figuring Out What I Even Need
&lt;/h2&gt;

&lt;p&gt;As I just said, I only need two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A device being able to broadcast FM radio&lt;/li&gt;
&lt;li&gt;A stream from where I can get the live radio feed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And great news, I already have an idea on how to get them !&lt;/p&gt;
&lt;h3&gt;
  
  
  1: The broadcaster
&lt;/h3&gt;

&lt;p&gt;This is the easiest part of this article, since I literally made a software being able to do that, and I won't hesitate to use it !&lt;/p&gt;

&lt;p&gt;For those who don't know it, it's &lt;a href="https://git.douxx.tech/botwave/" rel="noopener noreferrer"&gt;BotWave&lt;/a&gt;, a software that lets you easily play files and live feeds on FM using a &lt;a href="https://raspberrypi.com" rel="noopener noreferrer"&gt;Raspberry Pi&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2: The source
&lt;/h3&gt;

&lt;p&gt;What I initially wanted to do was simple: Go to &lt;a href="https://radio-browser.info" rel="noopener noreferrer"&gt;radio-browser.info&lt;/a&gt;, a library of almost every "big" web radios that documents a lot of information about them, but more importantly, the stream url.&lt;/p&gt;

&lt;p&gt;So I went on the website, searched for RTL 102.5, found it, and got this stream url:&lt;br&gt;&lt;br&gt;
&lt;code&gt;https://dd782ed59e2a4e86aabf6fc508674b59.msvdn.net/live/S97044836/tbbP8T1ZRPBL/playlist_audio.m3u8&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Sadly, once I opened it up in my browser, I saw that it was a dead link and that nothing was served anymore :/&lt;/p&gt;

&lt;p&gt;So it's time for the fallback plan ! Find the stream url directly on the website !&lt;br&gt;&lt;br&gt;
It sounds like an epic thing, but it's really not much, I just went on the website player, and then checked for any &lt;code&gt;m3u&lt;/code&gt; files in the network tab of the devtools. &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%2Fimages.dbo.one%2Fa025e208%2F" 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%2Fimages.dbo.one%2Fa025e208%2F" alt="The network tab" width="1330" height="726"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And I found it:&lt;br&gt;&lt;br&gt;
&lt;code&gt;https://streamcdnb1-dd782ed59e2a4e86aabf6fc508674b59.msvdn.net/live/S97044836/WjpMtPyNjHwj/playlist_audio.m3u8&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And this one &lt;em&gt;actually&lt;/em&gt; works ! &lt;/p&gt;

&lt;p&gt;Ok so just like that, we got the first two points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Finding a way of broadcasting FM on a short range&lt;/del&gt;
&lt;/li&gt;
&lt;li&gt;☑ &lt;del&gt;Getting the audio source for the web radio stream&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Getting That Stream on FM
&lt;/h2&gt;

&lt;p&gt;I already had an idea on how to do that, but I'll have to check if it works. I'm planning on using &lt;a href="https://ffmpeg.org" rel="noopener noreferrer"&gt;FFmpeg&lt;/a&gt;, basically the swiss-knife of audio, video, and image editing. &lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Test it
&lt;/h3&gt;

&lt;p&gt;I'll start by trying to record 5 seconds of the stream, and put them in a &lt;code&gt;.wav&lt;/code&gt; file.&lt;br&gt;&lt;br&gt;
And, surprisingly, I succeeded first try, which is pretty unusual :]&lt;/p&gt;

&lt;p&gt;Here is the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"https://streamcdnb1-dd782ed59e2a4e86aabf6fc508674b59.msvdn.net/live/S97044836/WjpMtPyNjHwj/playlist_audio.m3u8"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 5 test.wav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes the stream in input, and converts 5 seconds of it in the wave format to save it !&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Put it Into Practice
&lt;/h3&gt;

&lt;p&gt;BotWave exposes a sound card in which we can input audio, and it will play it live. So all we have to do is, instead of outputting the audio into a wave file, we output it directly in the sound card !&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"https://streamcdnb1-dd782ed59e2a4e86aabf6fc508674b59.msvdn.net/live/S97044836/WjpMtPyNjHwj/playlist_audio.m3u8"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; alsa plughw:BotWave
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I removed the time limit so it plays indefinitely, and the other stuff redirects the sound to the sound card.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Broadcasting it
&lt;/h3&gt;

&lt;p&gt;The last step is actually telling the software to take the card output and broadcast it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;bw-local
botwave&amp;gt; live 102.5 &lt;span class="s2"&gt;"RTL 102.5"&lt;/span&gt; &lt;span class="s2"&gt;"aka.dbo.one/webtofm"&lt;/span&gt;
botwave&amp;gt; &lt;span class="c"&gt;# This plays at 102.5 FM, with the name and desc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's check if we got anything on radio !&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%2Fimages.dbo.one%2F18503ffc%2F" 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%2Fimages.dbo.one%2F18503ffc%2F" width="446" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And yes ! We can see the broadcast on the spectrum, and if we stop the broadcast, it disappears:&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%2Fimages.dbo.one%2Fff7d526f%2F" 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%2Fimages.dbo.one%2Fff7d526f%2F" width="391" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Putting both together&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Automating Everything
&lt;/h2&gt;

&lt;p&gt;It works, but currently it's kinda painful to setup, we need to get into the pi shell, two times actually, run ffmpeg and then BotWave, and leave it open.&lt;/p&gt;

&lt;p&gt;So what I'll do is automating it, and it's surprisingly simple !&lt;/p&gt;

&lt;p&gt;First, I'll create a file that will execute at the moment where BotWave starts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;bw-nandl l_onready_webtofm.hdl
&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%2Fimages.dbo.one%2F0a8e1649%2F" 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%2Fimages.dbo.one%2F0a8e1649%2F" alt="nano editor" width="931" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside, I put the ffmpeg command and the live instruction, so this will run ffmpeg in the background, and then start the broadcast automatically.&lt;/p&gt;

&lt;p&gt;Now, when we run &lt;code&gt;bw-local&lt;/code&gt;, the broadcast will automatically start. This removed one step of the process, but we still have to open a shell and run the command. So let's also automate this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;bw-autorun &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nt"&gt;--ws&lt;/span&gt; 9939
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will make a systemd service that automatically starts BotWave on boot. It also opens a remote connection on port &lt;code&gt;9939&lt;/code&gt; so I can still manually send commands if needed.&lt;/p&gt;

&lt;p&gt;And we're done !&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Automate everything&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now, I'm able again to take my 15 bucks radio, tune it to 102.5MHz, and listen to it with a less perfect, but more charming audio quality, where and when I want :D&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%2Fimages.dbo.one%2F9577090e%2F" 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%2Fimages.dbo.one%2F9577090e%2F" alt="picture of the Raspberry Pi and the radio" width="688" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Picture taken with my &lt;a href="https://douxx.blog/?p=9-circuit-bending-a-camera" rel="noopener noreferrer"&gt;circuit bent camera&lt;/a&gt;, btw&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Enjoy !&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>radio</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I Built a Random Number Generator (Sort Of)</title>
      <dc:creator>Douxx</dc:creator>
      <pubDate>Sat, 07 Feb 2026 22:26:59 +0000</pubDate>
      <link>https://forem.com/douxxtech/how-i-built-a-random-number-generator-sort-of-37i</link>
      <guid>https://forem.com/douxxtech/how-i-built-a-random-number-generator-sort-of-37i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: I made an Hybrid Hardware Random Number Generator (HHRNG) using radio noise. You can find the full source code on &lt;a href="https://git.douxx.tech/rfdom/" rel="noopener noreferrer"&gt;my GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Generating randomness is fascinating, and I always wanted to go deeper than just importing some library into my python project and calling &lt;code&gt;.random()&lt;/code&gt;. So I set out to build my own library with its own &lt;code&gt;.random()&lt;/code&gt; function. Revolutionary, I know.&lt;/p&gt;

&lt;p&gt;But I didn't want to just cobble together pseudo-random values. I wanted to build a &lt;strong&gt;hardware random number generator&lt;/strong&gt; (HRNG): one that uses actual physical processes to generate entropy. Think Linux's &lt;code&gt;/dev/random&lt;/code&gt;, which harvests entropy from environmental noise: keyboard timings, mouse movements, disk I/O patterns. Windows also have a component named &lt;strong&gt;CNG&lt;/strong&gt; (Cryptography Next Generation) that uses similar inputs.   &lt;/p&gt;

&lt;p&gt;When I hear &lt;em&gt;noise&lt;/em&gt;, the first thing that comes to mind is this:&lt;br&gt;&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%2Fimages.dbo.one%2F43bb815d%2F" 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%2Fimages.dbo.one%2F43bb815d%2F" alt="Radio noise"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
For the uninitiated, this is an &lt;strong&gt;SDR&lt;/strong&gt; (Software Defined Radio), a real-time visualization of the radio spectrum around me. Notice the constant flickering at the bottom? That's noise, pure and simple. &lt;em&gt;Nothing&lt;/em&gt; is transmitting there, yet the intensity is constantly dancing. It's the same static you hear when tuning to an empty FM frequency. If we could capture and measure that movement, we'd have a source of &lt;strong&gt;&lt;em&gt;true&lt;/em&gt;&lt;/strong&gt; randomness, unpredictable and nearly impossible to manipulate.&lt;/p&gt;

&lt;p&gt;So, based on that, I decided to get to work. Note that I will be using a &lt;a href="https://www.SDR.com/v4/" rel="noopener noreferrer"&gt;SDR blog v4&lt;/a&gt; to capture radio signals and compute them.  &lt;/p&gt;

&lt;p&gt;Here is a global overview of what I needed to do to get this project done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ Find a way of getting the radio signals on my PC&lt;/li&gt;
&lt;li&gt;☐ Process them to generate randomness&lt;/li&gt;
&lt;li&gt;☐ Create the core functions&lt;/li&gt;
&lt;li&gt;☐ Add other functions on top of that&lt;/li&gt;
&lt;li&gt;☐ Check that everything works and isn't easily affected by external events&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setting Up the Foundation
&lt;/h2&gt;

&lt;p&gt;So, first of all, I needed to find a way to programmatically access my radio data, served as samples. I'd tried this on Windows before with no luck, so this time I went straight to a &lt;a href="https://gist.github.com/douxxtech/793fe37022d9551df3114f6d72498b94" rel="noopener noreferrer"&gt;template&lt;/a&gt; that connects &lt;strong&gt;over the network&lt;/strong&gt; on an &lt;code&gt;rtl_tcp&lt;/code&gt; server running on one of my Raspberry Pis.&lt;/p&gt;

&lt;p&gt;Once everything setup, I was able to receive samples. The code is quite simple, it uses a socket to connect to the rtl_tcp server, and then reads samples from it. Here is a part of the &lt;code&gt;read_samples&lt;/code&gt; function, that reads and processes the signals:&lt;br&gt;&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%2Fimages.dbo.one%2F386dbaa4%2F" 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%2Fimages.dbo.one%2F386dbaa4%2F" alt="codesnap"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;As we can see, it computes IQ samples. I and Q refer to two components of a &lt;strong&gt;complex baseband signal&lt;/strong&gt;. They capture both amplitude and phase of the RF signal.  &lt;/p&gt;

&lt;p&gt;Well, anyways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Find a way of getting the radio signals on my PC&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Creating Randomness
&lt;/h2&gt;

&lt;p&gt;The next step is building the "randomizer". It is basically a function that will take, as an input, samples from our SDR, and output a seed we will base our calculations on later.&lt;br&gt;&lt;br&gt;
To do this, I've tried a couple of ways  (2, actually), but the most efficient method was using &lt;strong&gt;Phase difference&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The phase is the &lt;strong&gt;angle&lt;/strong&gt; of the signal at a given moment. It can be easily calculated using both values of an IQ sample using this formula:&lt;br&gt;
&lt;code&gt;angle = atan2(Q, I)&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
We'll do this for both the current iteration value (&lt;code&gt;n&lt;/code&gt;), and the previous one (&lt;code&gt;n-1&lt;/code&gt;).  &lt;/p&gt;

&lt;p&gt;After getting both angles, we will retrieve the phase difference, that will tell us in which direction the signal rotated since the previous sample. It can be done like this: &lt;code&gt;delta = (current_angle - previous_angle + π) % 2π - π&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The wrapping in &lt;code&gt;π&lt;/code&gt; is to ensure that the result stays between -π and +π, and doesn't make big jumps, like going from 2° to -359° just by rotating 3°.  &lt;/p&gt;

&lt;p&gt;We got the rotation angle, but that value is still too complex to be properly processed. We will reduce it to a simple &lt;strong&gt;bit&lt;/strong&gt;. The easiest way to do this is by checking its direction:&lt;br&gt;
&lt;code&gt;bit = delta &amp;gt; 0 ? 1 : 0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now that we got a bit, we're simply repeating this &lt;code&gt;n&lt;/code&gt; samples times, to get a randomly generated bits array.&lt;/p&gt;

&lt;p&gt;But there's a problem. After running the program, and logging some statistics, &lt;strong&gt;I observed more 1s than 0s&lt;/strong&gt;, which isn't great, since the program would tend to go on the 1 side more than the 0 one.&lt;/p&gt;

&lt;p&gt;Fixing that is easy, and there are plenty of methods available. I used a very simple one: XOR-ing all the values together, to spread the 1s and 0s. For those who don't know what it implies, it is a simple bit operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 &amp;amp; 0 -&amp;gt; 0
0 &amp;amp; 1 -&amp;gt; 1
1 &amp;amp; 0 -&amp;gt; 1
1 &amp;amp; 1 -&amp;gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used that to compare both bits each others, and the results were perfect: we went from a near 20% difference to a max of around 2%.&lt;/p&gt;

&lt;p&gt;Finally, for convenience, I'm hashing the results to get an uniform seed to continue with (using SHA256).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Process the signals to generate randomness&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
  Code explained in this part
  &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%2Fimages.dbo.one%2Ff6a02ec2%2F" 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%2Fimages.dbo.one%2Ff6a02ec2%2F" alt="code snippet"&gt;&lt;/a&gt;&lt;br&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Core Functionalities
&lt;/h2&gt;

&lt;p&gt;We now have our random seed source, but now we have to let the user actually &lt;em&gt;use&lt;/em&gt; it.&lt;br&gt;&lt;br&gt;
My first plan: every time someone calls &lt;code&gt;.random()&lt;/code&gt;, grab fresh samples from the SDR, generate a seed, and convert it to a float.&lt;/p&gt;

&lt;p&gt;This was &lt;strong&gt;painfully slow&lt;/strong&gt;: we were hitting the hardware for every single random number.&lt;/p&gt;

&lt;p&gt;Solution: make it &lt;strong&gt;hybrid&lt;/strong&gt;. Use a fast Pseudo-Random Number Generator (PRNG) that gets periodically reseeded with fresh radio entropy, instead of hitting the SDR every time.&lt;/p&gt;

&lt;p&gt;This was fairly straightforward to achieve: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We first update our seeder to update its seed each X milliseconds, using a runner thread.&lt;/li&gt;
&lt;li&gt;Then, we create a Pseudo-Random Number generator. I used a simple &lt;strong&gt;Linear Congruential Generator&lt;/strong&gt; (LCG), made with the help of &lt;a href="https://www.youtube.com/watch?v=mXBGXU0zJnw" rel="noopener noreferrer"&gt;this very good video&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We change our code to constantly feed the generator with our new seed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the code uses the LCG as a base, but it is always reseeded with our seeds taken from our radio samples.&lt;/p&gt;

&lt;p&gt;Now, let's build our core random class, that we will call &lt;code&gt;RFDom&lt;/code&gt;. We will optionally pass arguments to it, such as the rtl_tcp host, frequency ranges to scan, gain, and other settings.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.random()&lt;/code&gt; (returns &lt;code&gt;[0.0, 1.0)&lt;/code&gt;) was pretty simple to do, as it used the same code as the youtube tutorial, same for &lt;code&gt;.randint(a: int, b: int)&lt;/code&gt;, that returns &lt;code&gt;[a, b]&lt;/code&gt;. I've just had to implement a &lt;code&gt;include: bool&lt;/code&gt; parameter to the LCG API, but that was quite easy.&lt;/p&gt;

&lt;p&gt;
  LCG .get_float()
  &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%2Fimages.dbo.one%2Fdda6ace7%2F" 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%2Fimages.dbo.one%2Fdda6ace7%2F" alt="code snippet"&gt;&lt;/a&gt;&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;
  RFDom .random()
  &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%2Fimages.dbo.one%2Fa1096e26%2F" 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%2Fimages.dbo.one%2Fa1096e26%2F" alt="code snippet"&gt;&lt;/a&gt;&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;And done !&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Create the core functions&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding Other Stuff
&lt;/h2&gt;

&lt;p&gt;After adding &lt;code&gt;.random()&lt;/code&gt; -&amp;gt; &lt;code&gt;[0.0, 1.0)&lt;/code&gt;, &lt;code&gt;.uniform(a: float, b: float)&lt;/code&gt; -&amp;gt; &lt;code&gt;[a, b]&lt;/code&gt;, and &lt;code&gt;.randint(a: int, b: int)&lt;/code&gt; -&amp;gt; &lt;code&gt;[a, b]&lt;/code&gt; I've looked to the core functions of the python &lt;code&gt;random&lt;/code&gt; library, and tried to replicate them. Some of them were really easy, some were quite a bit harder to do. I'm not going to explain all of them, since it's just a bunch of formulas, but here are two that I found quite interesting:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Choices
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;.choices()&lt;/code&gt; func takes at max 4 arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;population: a sequence of objects&lt;/li&gt;
&lt;li&gt;weight (optional): the probability weight of each object to get picked&lt;/li&gt;
&lt;li&gt;cum_weights (optional): preprocessed cumulative weights&lt;/li&gt;
&lt;li&gt;k (optional): the number of objects to pick&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The way it works is pretty simple, even tho it was a bit hard to implement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each population object has a weight, either the same for every object, or a given one for each object.&lt;/li&gt;
&lt;li&gt;If no weight (or cum_weights) has been provided, simply take a random object in the &lt;code&gt;population&lt;/code&gt; array &lt;code&gt;k&lt;/code&gt; times.&lt;/li&gt;
&lt;/ol&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%2Fimages.dbo.one%2F94137cba%2F" 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%2Fimages.dbo.one%2F94137cba%2F" alt="code snippet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If a weight array is provided, we will need to build a cum_weight list, if it isn't already provided.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The cumulative weights represent the range where a number falls into an object, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;objects -&amp;gt; ["apple", "banana", "orange"]
weights -&amp;gt; [4,        1,       6]

cum_weights = [4,     5,       11]

=&amp;gt; 
apple -&amp;gt; [0, 4)
banana -&amp;gt; [4, 5)
orange -&amp;gt; [5, 11)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Choose a random object &lt;code&gt;k&lt;/code&gt; times, based on the weights:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;we use &lt;code&gt;.uniform(0, max_weight)&lt;/code&gt; to get a float value, get the object falling in that range, add it to the list&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Return the list: we return a list of &lt;code&gt;k&lt;/code&gt; elements chosen randomly&lt;/li&gt;
&lt;/ol&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%2Fimages.dbo.one%2F36594512%2F" 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%2Fimages.dbo.one%2F36594512%2F" alt="code snippet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. shuffle
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;.shuffle()&lt;/code&gt; function is much simpler, but I wanted to talk about the algorithm.&lt;br&gt;
It takes a list as the only argument and edits it in-place (doesn't return it, but changes the original).&lt;/p&gt;

&lt;p&gt;To do this, we're using the Fisher-Yates algorithm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting from the beginning of the list, for each index &lt;code&gt;i&lt;/code&gt;, a random index &lt;code&gt;j&lt;/code&gt; is chosen from the range &lt;code&gt;[i, n - 1]&lt;/code&gt;, and the elements at positions &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;j&lt;/code&gt; are swapped.&lt;/li&gt;
&lt;li&gt;Repeat until everything has been shuffled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is represented by this code:&lt;br&gt;&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%2Fimages.dbo.one%2F71da375e%2F" 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%2Fimages.dbo.one%2F71da375e%2F" alt="code snippet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Add other functions on top of that&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Trying to break the randomizer
&lt;/h2&gt;

&lt;p&gt;My first idea (before even starting the project) was to try to break it out by overflowing the bandwidth using &lt;a href="https://git.douxx.tech/fmjam/" rel="noopener noreferrer"&gt;this tool&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;I then made this simple code to have an eye on the values&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%2Fimages.dbo.one%2F432fe4b2%2F" 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%2Fimages.dbo.one%2F432fe4b2%2F" alt="code snippet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ran it, first without any special radio broadcast or whatsoever.&lt;/p&gt;

&lt;p&gt;Then, I ran the program, and I observed. No changes at all, but that was predictable. Even with the bandwidth saturated, oscillations in the signal are still there, and the phase difference still works. I also later had an AI analyze the results, which confirmed there weren't any major changes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ &lt;del&gt;Check that everything works and isn't easily affected by external events&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  For the End
&lt;/h2&gt;

&lt;p&gt;This project began with a simple question: &lt;em&gt;where does randomness actually come from ?&lt;/em&gt;, and turned into a deep dive into radio physics, signal processing, entropy extraction, and practical limits.&lt;/p&gt;

&lt;p&gt;Building this taught me that randomness isn't magic: it's &lt;strong&gt;trade-offs&lt;/strong&gt;. Hardware entropy is slow but truly random. PRNGs are fast but predictable. Combining both? That's where it gets interesting.&lt;/p&gt;

&lt;p&gt;Is this generator cryptographically secure ? Probably not. Is it faster than Python's built-in &lt;code&gt;random&lt;/code&gt; ? Definitely not. Would I recommend using this in production (or in general) ? Absolutely not.&lt;/p&gt;

&lt;p&gt;But that &lt;strong&gt;was never the point&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the end, I won't replace the casual &lt;code&gt;import random&lt;/code&gt; when I need randomness, but I learned how this works behind the scenes, even if it's only a fraction of the modern random generators.&lt;/p&gt;

&lt;p&gt;If you're interested into seeing the full code, I uploaded it to &lt;a href="https://git.douxx.tech/RFDom" rel="noopener noreferrer"&gt;my GitHub&lt;/a&gt;. Usage examples can be found in the &lt;code&gt;/examples/&lt;/code&gt; dir, and installation instructions on the README.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions? Ideas?
&lt;/h2&gt;

&lt;p&gt;If you have any questions about the implementation, suggestions for improvements, or you've experimented with similar approaches, feel free to drop a comment below. I'm curious to hear what you think!&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;p&gt;Here are some links of videos / websites that I used to understand the subject better (not in any specific order). I might've forgotten some.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Random number generation - &lt;a href="https://en.wikipedia.org/wiki/Random_number_generation" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pseudo-Random Number Generator From Scratch in Python - &lt;a href="https://www.youtube.com/watch?v=mXBGXU0zJnw" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Entropy (information theory) - &lt;a href="https://en.wikipedia.org/wiki/Entropy_(information_theory)" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;XOR (Exclusive or) - &lt;a href="https://en.wikipedia.org/wiki/Exclusive_or" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fisher–Yates shuffle - &lt;a href="https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Linear congruential generator - &lt;a href="https://en.wikipedia.org/wiki/Linear_congruential_generator" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SHA256 - &lt;a href="http://en.wikipedia.org/wiki/SHA-2" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Actual Generator
&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%2Fimages.dbo.one%2F19367830%2F" 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%2Fimages.dbo.one%2F19367830%2F" alt="A SDR connected to a raspberry pi"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>radio</category>
      <category>python</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
