<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Education on kmcd.dev</title><link>https://kmcd.dev/tags/education/</link><description>Recent content in Education on kmcd.dev</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>All Rights Reserved</copyright><lastBuildDate>Tue, 12 May 2026 10:00:00 +0000</lastBuildDate><atom:link href="https://kmcd.dev/tags/education/index.xml" rel="self" type="application/rss+xml"/><item><title>Let's Learn About BGP</title><link>https://kmcd.dev/posts/bgp-kmcd-dev/</link><pubDate>Tue, 12 May 2026 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/bgp-kmcd-dev/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/bgp-kmcd-dev/cover.svg" /> &lt;/p>
                
                How a live BGP map evolved into an interactive explainer on internet routing.
                </description><content:encoded><![CDATA[<p>Before we get into the weeds of how this was built, go check out <a href="https://bgp.kmcd.dev" rel="external">bgp.kmcd.dev</a> right now. Play around with the interactive elements:</p>
<p><a href="https://bgp.kmcd.dev" rel="external">
    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/bgp-kmcd-dev/logo_hu_a01c1d260946d2f2.webp" class="center" width="500px"/>
    

</a></p>
<ul>
<li><strong>Learn about BGP through interactive diagrams</strong></li>
<li><strong>Try the RPKI safety test.</strong> You might not like what you find about your own ISP&rsquo;s routing security.</li>
</ul>
<p>Once you have a feel for it, come back here. Or don&rsquo;t. I&rsquo;m not your dad or anything.</p>
<h3 id="the-ultimate-interview-question">The Ultimate Interview Question</h3>
<p>My favorite interview question for software engineers is wonderfully simple: <em>&ldquo;How does the internet work?&rdquo;</em></p>
<p>If a candidate walks me through DHCP, DNS, TCP, TLS, and HTTP, I know I am talking to someone with solid real-world experience. But there is almost always a glaring omission in their answer. A shockingly small number of people ever mention the role of BGP.</p>
<p>It makes sense why so many engineers miss it. Most software development today is incredibly abstracted. An engineer building microservices in AWS or configuring a Kubernetes cluster spends their whole day thinking about application-layer protocols. BGP operates at a layer of the infrastructure that is almost entirely invisible to them; it is treated as &ldquo;the network&rsquo;s problem&rdquo; or something only ISPs and cloud providers need to worry about.</p>
<p>But without BGP, the internet as we know it would literally not exist.</p>
<h3 id="why-bgp-matters">Why BGP Matters</h3>
<p>BGP (Border Gateway Protocol) is essentially the &ldquo;glue&rdquo; that holds the internet together. It is the protocol that determines how data travels from one network to another across the globe. When you click a link, BGP is what decided the path those packets took to get to you.</p>
<p>It is also incredibly fragile. A single misconfiguration or a malicious &ldquo;route leak&rdquo; can accidentally divert traffic for an entire country or knock major services offline. Despite being the backbone of the global internet, much of it still relies on trust. This is why security measures like RPKI (Resource Public Key Infrastructure) are so critical yet inconsistently adopted.</p>
<h2 id="the-power-of-interactive-explainers">The Power of Interactive Explainers</h2>
<p>I am really happy with how this project turned out. I have always found &ldquo;interactive explainer&rdquo; microsites to be super effective for learning complex technical concepts. Reading a whitepaper about the Border Gateway Protocol (BGP) is one thing, but discovering that your own ISP is vulnerable to a BGP hijack, or visually seeing how a Remote Triggered Black Hole (RTBH) can mitigate a DDoS attack, makes the concepts actually stick.</p>
<p>Static documentation often fails to convey the <em>dynamic</em> nature of protocols. An interactive explainer unlocks a &ldquo;feedback loop&rdquo; that docs can&rsquo;t: you change a variable, and you see the consequence immediately. It bridges the gap between abstract theory and practical intuition. I find these sites are worth building whenever a concept involves state transitions, complex spatial relationships, or high-stakes edge cases that are hard to replicate in a lab.</p>
<h3 id="embracing-the-evolution">Embracing the Evolution</h3>
<p>This whole thing started because I wanted to learn more about BGP, so I wrote <a href="https://kmcd.dev/posts/live-internet-map/">a visually cool (and mostly useless) 24/7 live stream</a>. The natural next step was to leverage some of the insights I observed into a dashboard. But as I built the early version of the dashboard, the explanatory text became more interesting and more powerful than the raw dashboard data.</p>
<p>What began as a simple monitoring visualization shifted into a massive interactive learning resource. This taught me a valuable lesson: <strong>projects don&rsquo;t need to start useful.</strong> The most valuable outcome isn&rsquo;t always the original goal. It is often the observations you make while building toward it. By staying flexible, I was able to reshape the project into something far more impactful than just another &ldquo;live map.&rdquo;</p>
<p>This shift aligns with how I tend to learn best: by doing. I&rsquo;ve always found that I don&rsquo;t truly understand a protocol until I&rsquo;ve had to handle its edge cases in code. This is why I write about <a href="https://kmcd.dev/series/http-from-scratch/">HTTP from Scratch</a>, <a href="https://kmcd.dev/series/grpc-from-scratch/">gRPC From Scratch</a> and <a href="https://kmcd.dev/posts/grpc-over-http3/">gRPC Over HTTP/3</a>; to push myself to build one layer deeper than I strictly need for my day-to-day work. But there is another layer to it: I strongly believe that to properly <strong>learn</strong> something, you must be able to <strong>teach</strong> it, or at the very least, communicate it clearly to others. There is a strange shift that happens in my brain when I approach a topic with the intent to present it. It forces a level of rigor that I might otherwise skip. It is the same reason I am such a huge fan of self-reviews while a PR is in draft; looking at my own code through the lens of an external reviewer often reveals &ldquo;perfect&rdquo; code to be anything but. It is also 95% of the reason that I write this blog (the other 5% is vanity).</p>
<h3 id="interactive-tools">Interactive Tools</h3>
<p>I replaced static images with <strong>interactive SVG diagrams</strong> driven by the same data models used in the backend. You can watch different BGP behaviors play out interactively, from route advertisements and withdrawals to full blown route leaks.</p>
<p>The most useful tool on the site is the <strong>ISP RPKI Safety Test</strong>. It lets you check if your own Internet provider is using RPKI to sign and validate routes.</p>
<p>When I first ran this, I was shocked to see that <strong>my own home ISP fails this check.</strong> This means they are effectively trusting the &ldquo;word&rdquo; of any other network on the planet without cryptographically verifying it. It is a sobering reminder that the backbone of our digital lives is often held together by conventions and good faith rather than hard security. If your ISP fails, it is a great excuse to reach out to their support and ask <em>why</em>. This test is powered by <a href="https://isbgpsafeyet.com/" rel="external">isbgpsafeyet.com</a>, and they encourage you to tweet about your ISP if they fail.</p>
<div class="rpki-check" style="background-color: #1f2835; border: 2px solid #42a5f5; border-radius: 8px; padding: 1.5rem; margin: 1rem 0;">
    <h3 style="color: #42a5f5; margin-top: 0;">Is your ISP BGP safe?</h3>
    <p style="color: #cccccc; margin-bottom: 1rem;">
        This tool checks if your ISP is filtering BGP routes based on RPKI.
        It attempts to fetch two resources: one from a validly signed prefix and one from an invalidly signed prefix.
        This is built using the same endpoints as <a href="https://isbgpsafeyet.com/" style="color: #42a5f5; text-decoration: none; border-bottom: 1px dashed #42a5f5;">isbgpsafeyet.com</a>. Go there for more information.
    </p>    <div class="rpki-results" style="font-family: monospace; background-color: #111; padding: 1rem; border-radius: 4px; color: #eee; line-height: 1.5;">
    <div class="rpki-valid-row">
        <div>fetch https://valid.rpki.isbgpsafeyet.com</div>
        <div class="rpki-valid-detail" style="margin-left: 2ch; color: #888;">... loading ...</div>
    </div>
    <div class="rpki-invalid-row" style="margin-top: 1rem;">
        <div>fetch https://invalid.rpki.isbgpsafeyet.com/</div>
        <div class="rpki-invalid-detail" style="margin-left: 2ch; color: #888;">... loading ...</div>
    </div>
</div>
<div class="rpki-summary" style="margin-top: 1rem; font-weight: bold; color: #cccccc;"></div>

<script>
(function() {
    const container = document.currentScript.parentElement;
    const validDetail = container.querySelector('.rpki-valid-detail');
    const invalidDetail = container.querySelector('.rpki-invalid-detail');
    const summaryEl = container.querySelector('.rpki-summary');

    const validUrl = 'https://valid.rpki.isbgpsafeyet.com';
    const invalidUrl = 'https://invalid.rpki.isbgpsafeyet.com/';

    async function checkRpki(url, detailEl, expected) {
        try {
            const response = await fetch(url, { mode: 'cors', cache: 'no-cache' });
            if (response.ok) {
                const data = await response.json();
                if (expected === 'valid') {
                    detailEl.textContent = '  correctly accepted valid prefixes';
                    detailEl.style.color = '#66bb6a';
                } else {
                    detailEl.textContent = '  incorrectly accepted invalid prefixes';
                    detailEl.style.color = '#ff6b6b';
                }
                return { success: true, data };
            } else {
                detailEl.textContent = '  failed to reach prefix (status: ' + response.status + ')';
                detailEl.style.color = '#ffa726';
                return { success: false };
            }
        } catch (e) {
            if (expected === 'invalid') {
                detailEl.textContent = '  correctly dropped invalid prefixes';
                detailEl.style.color = '#66bb6a';
            } else {
                detailEl.textContent = '  failed to reach valid prefixes (unstable connection?)';
                detailEl.style.color = '#ff6b6b';
            }
            return { success: false, error: e };
        }
    }

    async function runCheck() {
        const [validRes, invalidRes] = await Promise.all([
            checkRpki(validUrl, validDetail, 'valid'),
            checkRpki(invalidUrl, invalidDetail, 'invalid')
        ]);

        let ispName = 'your ISP';
        if (validRes.success && validRes.data) {
            ispName = validRes.data.name || 'your ISP';
        }

        if (validRes.success && !invalidRes.success) {
            summaryEl.textContent = `✅ Your ISP, ${ispName}, is BGP safe! They are filtering RPKI-invalid routes.`;
            summaryEl.style.color = '#66bb6a';
        } else if (validRes.success && invalidRes.success) {
            summaryEl.textContent = `❌ Your ISP, ${ispName}, is NOT BGP safe. They are accepting RPKI-invalid routes.`;
            summaryEl.style.color = '#ff6b6b';
        } else {
            summaryEl.textContent = '⚠️ Could not determine BGP safety status.';
            summaryEl.style.color = '#ffa726';
        }
    }

    runCheck();
})();
</script>

</div>

<h2 id="how-it-was-made">How It Was Made</h2>
<p>Getting a live global heartbeat of the internet to run smoothly required completely rethinking the architecture. The <a href="https://kmcd.dev/posts/live-internet-map/">original implementation</a> handled data collection and GPU rendering in a single Go process. It worked fine at first, but garbage collector pauses during high-volume routing bursts (30,000+ updates per second) caused dropped frames in the 24/7 live stream.</p>
<p>The new architecture separates these concerns into two distinct outputs: a <strong>real-time 4K live stream</strong> on YouTube and an <strong>interactive microsite</strong> at <a href="https://bgp.kmcd.dev" rel="external">bgp.kmcd.dev</a>.</p>
<p>Here is the high-level flow: raw BGP updates from global sensors come in, a Rust backend processes and validates them in real-time. This processed data is then broadcast to a Go client (which renders the 60 FPS YouTube stream) and a Go indexer (which generates the static data for the microsite).</p>
<p>Here is a look at the architecture that solved this:</p>
<div class="d2-diagram-wrapper">
    <div class="d2-diagram"
        style="display: block; width: 100%; max-width: 100%; margin-left: auto; margin-right: auto; max-height:140vh; width:100%;"><img src="/d2-diagrams/4c21fdf276fe872212246a3cebe6ddb25f3b22978ed71083ed55bf45e2d1e2ef.svg" alt="D2 Diagram" loading="lazy" style="max-width: 100%; max-height: inherit; width: 100%; height: auto; object-fit: contain; display: block; margin: 0 auto;" /></div>
</div>
<h3 id="the-rust-rewrite">The Rust Rewrite</h3>
<p>I rewrote the telemetry collector in <strong>Rust</strong> using the <a href="https://bgpkit.com/" rel="external">BGPKit</a> ecosystem. Offloading the heavy lifting of parsing BMP and RIS-Live streams to a language built for high-throughput, memory-safe concurrency completely solved the performance bottlenecks.</p>
<p>Could I have just optimized the Go version? Probably. But the sheer volume of small allocations during BGP parsing was a &ldquo;worst-case scenario&rdquo; for Go&rsquo;s garbage collector. Moving to Rust allowed me to manage memory exactly where it mattered, ensuring that even the most massive routing bursts wouldn&rsquo;t stutter the visualization. Plus, I had been wanting to dip my toes into Rust, and this proved to be a great project for it.</p>
<h3 id="go-and-ebitengine-for-the-live-stream">Go and Ebitengine for the Live Stream</h3>
<p>With Rust handling the data ingestion, the Go viewer was freed up to focus entirely on the <strong>24/7 YouTube live stream</strong>. Using the <a href="https://ebitengine.org/" rel="external">Ebitengine</a> game engine, the Go application is now just a lean client that renders a 2D Mollweide projection of the globe at 60 FPS. This output is captured by OBS and pushed to YouTube. That 60 FPS target is nearly always reached now, when it was a pipedream with the first architecture.</p>
<p>Yes, I know how insane I sound when I say &ldquo;oh, and Go is used for the frontend&rdquo;, but I learned to respect the performance and robustness of Ebitengine for this specific real-time visualization task. This is what personal projects are for: to do things you wouldn&rsquo;t normally do in ways you wouldn&rsquo;t normally do them.</p>
<h3 id="unifying-on-protobuf">Unifying on Protobuf</h3>
<p>To manage the complex schema between Go, Rust, and TypeScript, I leveraged <strong>Protocol Buffers</strong> and <strong>gRPC</strong>.</p>
<p>Defining the interface between the Rust collector and the Go viewer in Protobuf simplified the Go code significantly. Instead of managing internal channels, it just subscribes to a gRPC stream of events. I can even restart the Rust collector to update logic without the visualizer dropping a single frame.</p>
<h3 id="static-hourly-snapshots">Static Hourly Snapshots</h3>
<p>To keep the web platform fast without maintaining a live database to service requests, I built a Go indexer. It generates snapshots of the global routing state every hour and commits them to a GitHub repository. This triggers a build on Cloudflare Pages, which deploys the updated snapshots as static assets. This has proven &lsquo;reliable enough&rsquo; for this project. Because of this, the data referenced in the website are updated hourly.</p>
<h3 id="why-a-microsite">Why a Microsite?</h3>
<p>I chose to build <a href="https://bgp.kmcd.dev" rel="external">bgp.kmcd.dev</a> as a standalone microsite rather than integrating it directly into this Hugo blog for a few key reasons:</p>
<ul>
<li><strong>Freedom of Choice:</strong> Starting fresh gave me complete control over the HTML, CSS, and JavaScript. I wasn&rsquo;t constrained by the blog&rsquo;s existing design or Hugo&rsquo;s template system, allowing me to use the best tools for this specific project.</li>
<li><strong>Cohesion:</strong> It makes more sense for the frontend to live in the same repository as the data collection and processing code. Since they are part of the same system, they can evolve together without being tied to the blog&rsquo;s codebase.</li>
<li><strong>Deployment:</strong> By keeping it separate, the microsite has its own build and deployment pipeline. It can be updated or refactored independently, which is much cleaner than jamming dynamic data features into a static blog.</li>
</ul>
<h2 id="the-result">The Result</h2>
<p>This project shifted from a monolithic live map to a distributed educational tool. Choosing specialized tools for each layer (Rust for throughput, Go for rendering, and Protobuf for data delivery) made the system more stable and capable.</p>
<p>I set out to visualize the internet, but ended up understanding it. I even built something that might help others do the same. If there&rsquo;s one takeaway here, it is that &ldquo;learning by building&rdquo; involves more than the code you write. It is also about the clarity you gain when you try to explain that code to the rest of the world.</p>
<p>Explore the tools and live data at <a href="https://bgp.kmcd.dev" rel="external">bgp.kmcd.dev</a>. Source code is on <a href="https://github.com/sudorandom/livemap.kmcd.dev/" rel="external">GitHub</a>.</p>
]]></content:encoded></item></channel></rss>