{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "the cabin",
  "language": "en",
  "home_page_url": "https://blog.mnke.org/",
  "feed_url": "https://blog.mnke.org/feed.json",
  "description": "",
  "authors": [
    {
      "name": "Tony Du",
      "url": "mailto:tonydu334@gmail.com"
    }
  ],
  "items": [
    {
      "id": "https://blog.mnke.org/would-you-call-this-function/",
      "url": "https://blog.mnke.org/would-you-call-this-function/",
      "title": "Would you call this function?",
      "content_html": "<h1>Preface</h1>\n<p>Recently, I designed and implemented the foundational blocks of a system at my company, inspired from my <a href=\"https://github.com/tonyd33/circus\">personal version of the system</a>, with the help of a small team of coworkers. Although I laid the groundwork, most of the code that runs today is not written by me anymore and it's diverged from my personal version, especially with regards to softwarre design in a way that I find degrading to the long-term quality and maintainability of the system. I have a difficult time expressing this, partly because I'm not a very confrontational person plus I don't want to be a prick about controlling every minute detail on how the project should look like, partly because restructuring code for higher code quality doesn't align with corporate interests, and partly because I don't even know how to precisely codify my own standards.</p>\n<p>I've written a lot of software in various languages, various frameworks, and various subjects. I don't claim to be an expert in everything that I write, but I believe that I've written enough software in a wide enough area that I've developed an aesthetic for good software. I <em>feel out</em> good software from bad software even if I'm not able to explain why, and I have strong reason to trust these instincts because following them has generally lead me to create more maintainable and well-written software over the course of thousands of hours. Yet, if I'm unable to express this private knowledge, no matter if my instincts are correct or not, I'm locked by myself in a <a href=\"https://plato.stanford.edu/entries/private-language/\">Wittgensteinein box of unintelligibility</a>. So I want to do some exercise to explain some of my practices and standards by starting a series of blog posts documenting software design patterns I've picked up. In doing so, I hope to free myself of long-held dogmas and gain insight into the limits of my beliefs.</p>\n<p>Before I do that, I want to issue a disclaimer that I'm suspicious of general software design principles. There is a time and a place for everything, even the most hackiest code. In fact, I despised the university course I took on software design. &quot;Clean code&quot;? &quot;SOLID principles&quot;? &quot;Liskov substitution principle&quot;? To hell with that. I'll be the judge of what works and what doesn't. Moreover, I'm no stranger to hacking my way around programs. If I need to do some dirty patch of an upstream library to get something working, I'll do it. Or if I need to fix an urgent and otherwise hard-to-resolve bug on production, of course I'll throw out these stupid principles.</p>\n<p>Yet, I find it hard to deny the recurring and overarching themes I find myself running into when designing software. I don't claim that my principles are the only way to write good software. My more modest statement is simply that, in my experience, these principles have lead me to write what I regard as better software. Or, an even more modest statement is simply that these principles have prevented my own stupidity. As I see it, this is the ultimate aim of software design.</p>\n<h1>Kablow! ...Oops</h1>\n<p>Take a look at this function signature. What do you think the function does?</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">gitClone</span><span class=\"token punctuation\">(</span>repository<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span></code></pre>\n<p>It's pretty clear to me that it will do something along the lines of &quot;spawn a git subprocess and clone a repository&quot;. Given the void return, a consumer wouldn't have access to whether the subprocess ran successfully, so I might expect that the function will throw an error if something went wrong in the subprocess. Let's see what the function actually does.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">gitClone</span><span class=\"token punctuation\">(</span>repository<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> sql <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SqlConnection</span><span class=\"token punctuation\">(</span>process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">DATABASE_URL</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">exec</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"git\"</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"clone\"</span><span class=\"token punctuation\">,</span> repository<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> sql<span class=\"token punctuation\">.</span><span class=\"token function\">query</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n    insert into git_clones (repository, timestamp, exit_code)\n    values (</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>repository<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">, now(), </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>res<span class=\"token punctuation\">.</span>exitCode<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">);\n  </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>exitCode <span class=\"token operator\">!==</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"git clone failed\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>...<em>oh</em>.</p>\n<p>Well, we were partially right, but it seems to do a lot more than what we expected and it took us having to open up and read the function to figure this out. I guess... that's fine? I just have to be cautious about every function that this author wrote though. Seeing this, who knows what <code>gitCheckout</code> is doing?</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">gitCheckout</span><span class=\"token punctuation\">(</span>branch<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> sql <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SqlConnection</span><span class=\"token punctuation\">(</span>process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">DATABASE_URL</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">exec</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"git\"</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"checkout\"</span><span class=\"token punctuation\">,</span> branch<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> sql<span class=\"token punctuation\">.</span><span class=\"token function\">query</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"truncate table git_clones;\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>exitCode <span class=\"token operator\">!==</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"git clone failed\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>Okay, what the f**k is this? Sure, maybe there is legitimately a good reason why we're truncating <code>git_clones</code> every time we <code>gitCheckout</code>. Maybe there was some incident that necessitated this. But as a consumer of these functions, I've lost all trust! For all I know, calling a function turns the lights off in the next room or <em>enters the nuclear launch codes!</em> I have no way to reason about what anything in the code does anymore without going and opening through every single function and seeing exactly what they do. On a small scale, fine, I can read <code>gitClone</code> and <code>gitCheckout</code>, but on the scale of even a medium-sized codebase, reading through every single line of code is simply <em>not realistic</em>.</p>\n<details>\n<summary><strong><span>Chaff: Don't you even dare...</span></strong></summary>\n<p>I know there will be some who would respond to the tune of &quot;oh, but you can just get an LLM to read the lines for you and figure out what it does&quot;.</p>\n<p><strong>But why would you even need to rely on that in the first place</strong>? The synthesizing power of LLMs is <strong>not</strong> an excuse to write shitty code. It's like poking yourself with a knife and expecting your body to heal the wound.</p>\n</details>\n<p>Taking a step back, let's be charitable and assume we actually do need to interface with the DB for the git operations for whatever reason. My current frustration isn't that we're doing DB queries alongside git operations, it's that we're <em>secretly</em> doing DB queries alongside git operations. There's no way to tell what <code>gitClone</code> and <code>gitCheckout</code> is doing without going in and auditing the code closely because it severely violated our expectations. Poor Alice could call <code>gitCheckout</code> on her first day of her internship, not understanding its full consequences and cause another incident!</p>\n<p>Let's propose a minimal redesign.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">gitClone</span><span class=\"token punctuation\">(</span>\n  repository<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span>\n  databaseUrl<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span></code></pre>\n<p>Instead of reading the database URL from within the function by reaching for environment variables, it's now required as a parameter. If consumers so wish, they can read the environment variable themselves and pass it into this function.</p>\n<p>It's helpful to think that <strong>these simply <em>are</em> the function's true parameters</strong> and all we've done is do a better job of surfacing them**.** After all, it's getting the database URL from somewhere. It was just previously hiding it. Sure, I still wouldn't touch this function with a ten-foot pole, but at least I can come to an informed judgement to refrain just by reading the function signature.</p>\n<p>So, again, would you call this function?</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">gitClone</span><span class=\"token punctuation\">(</span>\n  repository<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span>\n  databaseUrl<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span></code></pre>\n<h1>Conclusion</h1>\n<p>The trap of having implicit global dependencies that can be promiscuously and magically materialized from anywhere in the code – &quot;from thin air&quot; – is one that I see even well-rounded programmers fall into. The example I gave was admittedly contrived but it serves to show the general pattern and the harmful extremes of its nature. In practice, it takes mindfulness about the dependency graph that the program forms as well as careful judgement on the desired topology of the dependency graph to avoid this.</p>\n<p>Note that it may, in fact, be desirable to have an implicit global dependency if it should be treated as a primitive to all entities in the relevant domain. For instance, it may be desirable for all entities to have access to a <code>deltaTime()</code> function within a game engine framework so each entity can do time-based physics calculations. When we allow for a global dependency, we're allowing a &quot;depends-on&quot; edge from every single module in the program to that dependency in the dependency graph. If these edges are not obvious, it causes confusion and leads to a broken domain model. Furthermore, stripping these edges or making them explicit is tedious, albeit fairly mechanical.</p>\n<p>I feel the need to reiterate my stance on software design before closing off. Materializing hidden dependencies as I described in this post is neither necessary nor sufficient for good software. I'm skeptical &quot;good software&quot; is even well-defined as an objective concept. I simply want to share patterns I've found and I ask that the reader is mature enough to come to their own conclusions, both for this post and upcoming software design posts.</p>\n",
      "date_published": "2026-06-06T19:23:40Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/reflections-on-ai-coding/",
      "url": "https://blog.mnke.org/reflections-on-ai-coding/",
      "title": "Reflections on AI Coding",
      "content_html": "<p>A few years ago, OpenAI released ChatGPT, bringing mainstream attention to the otherwise still niche area of LLM research and development. The world was captivated. Investors flocked to funding LLMs and we've since seen an explosion in the presence of LLMs that's rippled through the world: creating new markets, driving research, and sparking discussion on the ethics of how LLMs acquire their knowledge, on the consequences of LLMs on the economy and even its consequences on human dignity. Whatever your opinions on LLMs are, there's no dispute it's permanently left its mark in the cultural zeitgeist.</p>\n<p>What I want to focus on in this post is the use of LLMs as it relates to software engineering and my personal experiences with using it. For the rest of the post, I'll be reluctantly referring to LLMs as AI, since that's the more common name that society's given it, despite LLMs being only a small subset of what AI traditionally meant in computer science.</p>\n<h1>I'm just vibing, bro 😎</h1>\n<p>Since its launch a few years ago, providers have been trying to push ChatGPT and other AI models to help with programming. In theory, AI models should do well with programming. After all, LLM stands for large language model, programming languages are a kind of language, even if it's artificially constructed and highly structured compared to natural language. People began &quot;vibe coding&quot;, a term coined by Andrej Karpathy referring to a practice where code is created by prompting AI in natural language and guiding it to accomplish your needs.</p>\n<details>\n<summary><strong><span>Chaff: Vibing and coding</span></strong></summary>\n<p>Before the term was even coined, <strong>I</strong> used to refer to programming sessions where I'm just vibing and coding as &quot;vibe coding&quot;. For example, I might've had a sudden urge to write an HTTP server in C to get more familiarity with some of the Linux kernel APIs allow nginx to be so efficient. When doing that, I would be vibing by going with the flow of exploration, and I would be coding. <strong>I would be vibe coding</strong>! Now this term has been co-opted by the AI people. Gah!</p>\n</details>\n<p>When the term was coined and it initially gained its popularity, I treated it as a joke. &quot;Seriously? We're throwing out rigorous computer science and software design in favor of slop generated from natural language?&quot; was my sentiment. I thought it was utterly insane. Smart people have created a way to interface with computers in a precise way via something called <em>programming languages</em> and <em>compilers</em>. By adding another compiler from natural language to programming languages, we're adding another layer of indirection and losing precision.</p>\n<p>That said, I wasn't dogmatically against AI. In fact, I found myself using it when exploring new topics. For example, when I was <a href=\"https://blog.mnke.org/how-i-set-up-scalable-high-capacity-streaming-infrastructure/\">setting up a streaming infrastructure</a>, I consulted AI to figure out what technologies were out there, some of their high level summaries, and interfacing capabilities. Similarly, I'd ask AI about starting points when researching new topics, like asking for references about programming language design.</p>\n<p>For a while, AI was a useful tool to me that aided in software engineering and made it more enjoyable. Purists may loathe this, but I prefer asking AI about some weird syntax error in a programming language I'm trying to learn rather than digging up some StackOverflow post from 12 years ago with answers that are outdated. During this time, my use of AI left the fun part of programming intact for me.</p>\n<h1>The dawn of agents</h1>\n<p>Fast forward to some time in 2025. Throughout the software industry, there had been wide scale adoption of AI in professional development, particularly in <em>agentic coding</em>. I treat agentic coding as a branch of vibe coding: rather than needing to relay information between your system and the AI (e.g. by copying around snippets of code), you install a program that interfaces with an AI and is able to read and write files and run commands on your system directly autonomously (with safeguards of course).</p>\n<details>\n<summary><strong><span>Chaff: Oh, the absurdity</span></strong></summary>\n<p>Only as I write out the description of agentic coding do I remember how crazy it sounded to me initially. An untrusted LLM with a dubious understanding of security running commands on your system? Sure, there are guardrails, but you have to trust that the guardrails even work and you have to be diligent and knowledgeable enough to audit what the agent is doing. I imagined that the only people who would use this technology would be people who <strong>didn't</strong> meet those requirements in the first place!</p>\n</details>\n<p>I was skeptical. I didn't trust that AI could write high quality code. I refused to use AI in any capacity for coding (apart from what was mentioned before, as a fancy search engine with a chat interface). In fact, I had worked with people that did vibe code and had seen first-hand how terrible the code was.</p>\n<p>That was until around the start of 2026. I had coworkers who were trying to push the limits of AI in their daily workflow. Moreover, there had been a cultural shift at my company to be more AI-driven.</p>\n<p>Again, I wasn't dogmatically anti-AI. My chief concern was the quality of code that AI produces. Beyond that, I didn't have a principled reason against using it. So when coworkers started asking me to review AI generated code and seeing a general trend in improvement in the code quality since I last remembered, I decided to give it a try. My company gave me access to Claude so I installed Claude Code and started playing around with it.</p>\n<p>At first, I started using it for only small, focused work like refactoring or writing tests or asking questions to the agent like tracing a variable up a long and confusing call stack or searching how something was done idiomatically in a codebase. I found this to be remarkably successful and as of writing, I still feel positively about this use of agentic coding. It removed some of the frustrating parts of programming, especially on a large and unfamiliar codebase (I was still relatively new to my company at the time).</p>\n<p>I kept experimenting with using AI in my daily programming and pushing the limits of tasks I deferred to it, even outside of work. At first, I was impressed. When I glanced through the code it generated, it believably looked like decent quality and to be fair, it really did generate passable code when working with popular tooling.</p>\n<p>At some point, I was writing maybe 80% of my code at work with AI agents. I was measurably writing a larger quantity of code and in an industry with endlessly increasing demands, it felt difficult to refuse a magic tool that boosted productivity. It was steroids for the software industry: even if it's not explicitly forced upon anyone, people will feel like they're falling behind if they don't use it. Damned if you do, damned if you don't.</p>\n<p>Is this truly a fair analogy to draw though? After all, steroids have known adverse health effects long-term and is generally frowned upon. We should investigate whether this comparison is justified.</p>\n<h1>My anecdote</h1>\n<p>I was asked recently to patch a fork of a 10+ year old C++ library we maintain. Even getting the library to run is a rite of passage, needing to deal with an antiquated CMake build that only works on very specific configurations, and I was one of the few that had passed the ritual, had sufficient C++ knowledge, and had done work on it recently so I was first in line for duty.</p>\n<details>\n<summary><strong><span>Chaff: I hate C++</span></strong></summary>\n<p>I know, I'm so unique. One on hand, writing C++ for my own projects is... not unpleasant, to be honest. I have only a few minor C++ projects here and there so I'm definitely not an expert, but in the times I've used it, my complaints were mainly around C++ being opaque with respect to the underlying machine code it produced compared to C, where every detail is explicit. This mattered to me because I was writing a game hacking library where I wanted precision down to the bytes in assembly instructions.</p>\n<p>What I hate is how odd some of the C++ language/stdlib design decisions are and how easy it is to abuse C++ features that make it absolutely alien to anyone else reading it. It feels like everyone creates their own version of C++ and the knowledge transfer between codebases is thus heavily limited.</p>\n</details>\n<p>I had a great time when I last worked on the library. Well, it was a struggle while working through the problem, but it was satisfying to solve it, like any good problem.</p>\n<h2>A blue story</h2>\n<p>The program was segfaulting when sending it certain data. I was working on macOS laptop and I had very limited familiarity with the LLVM toolchain so I couldn't even get a debugger attached to the process at first, which made debugging painful, especially since this was dealing with a segfault. I got desperate enough to start placing print statements at every line along the critical path of failure. Eventually, I wisened up and just built the project on my Linux desktop with the GNU toolchain I was familiar with and got a debugger attached.</p>\n<p>The segfault was occurring within some STL function that accessed invalid memory. That was odd. I should surely be able to trust the STL to competently handle its own memory, right?</p>\n<p>Weirdly, I also noticed the program was segfaulting at a different line on my desktop. I triple checked on my laptop (still without a debugger) to make sure this was the case.</p>\n<pre class=\"language-C++\"><code class=\"language-C++\">std::cout << \"OK GOT HERE 2 REAL (ACTUAL) FR\" << std::endl;\n// crash\nstd::cout << \"DO NOT SEE!!!\" << std::endl;</code></pre>\n<p>This was informative. The fact that there was a discrepancy between the two platforms hinted that the underlying issue was likely due to some memory corruption introduced earlier in the program rather than the offending lines themselves initiating the invalid memory access.</p>\n<p>Indeed, I found in that in a function earlier in the call stack, there was some odd-looking memory allocation patterns they did with their inhouse dynamic array implementation (why even use an inhouse implementation if we end up using the STL anyways...?). In any case, it looked like an object wasn't properly being initialized with their inhouse implementation and sure enough, adding a single line to initialize it properly fixed the segfaults on both platforms.</p>\n<p>What a joy! Each step in the process was soaked with blood and tears, but I felt so alive when working on the problem. I was proud of myself for being able to use these subtle hints to deduce the underlying problem and it culminated in a sweet, sweet victory.</p>\n<p>At this point, you might be wondering whether you're reading the right post. This is a post about AI, not about some esoteric C++ library and its internal mechanisms. So why am I talking about this? I promise I'll answer this soon. First, let's talk about the more recent patch I did for this library.</p>\n<h2>A bitter victory</h2>\n<p>I was told that this was an important patch and that a deal with a customer was sitting on the line for this. I was reluctant to give estimates because the nature of this work is inherently unpredictable and I was in the middle of other work at the time. The importance of the patch was emphasized when I was invited to a channel &quot;urgent-XYZ&quot; and prompted to fix it though.</p>\n<p>The thing is, this was an issue in the application code in a domain I had absolutely no knowledge of. Realistically, I'd need to take at least a few dedicated days to understand the relevant parts of the domain well enough to give a well-informed assessment of the situation. So I turned to AI.</p>\n<p>I turned on the most advanced and expensive model and asked it to help work through the problem. Understanding the domain could take too much time and it wasn't clear what the benefits would be if this problem could be solved with a blackbox approach. I fed the agent more data, suggesting it to look through potentially relevant files, relaying information from domain experts to it, praying that it'd magically fix the problem.</p>\n<p>It did.</p>\n<p>The executives were happy. I was praised for being able to get the fix in. Yet I felt nothing. What was my role in this process? What even was the underlying problem? What did I even do?</p>\n<p>At this point, I was a glorified CI runner. All I really did was feed a magic text box some funny-sounding words and run builds to see if the magic text box made the code work and reported back with a green checkmark when it did.</p>\n<p>This particular instance made me realize the effects AI was having on me as a programmer. I had a conversation with a coworker a while back about the use of AI in programming and I had agreed with his sentiment that AI takes the joy out of programming, but now I felt it at a visceral level. Programming with AI is just... not fun.</p>\n<p>Unmistakably, I am more productive with it. There's no way I could have made the patch in the time I did without the use of AI. There's no way I can push out the volume of changes I do without the use of AI. But I've realized using to the extent that I feel like I have to to be competitive as a programmer has taken the joy out of programming.</p>\n<h1>Closing thoughts</h1>\n<p>In my mind, there's little doubt that AI is revolutionizing the software industry and it will continue to do so, provided the economics of running LLMs can stabilize.</p>\n<p>Junior engineers equipped with AI tooling are transformed to a human-machine hybrid capable of pumping out medium quality changes at a faster rate than more experienced programmers without AI.</p>\n<p>Even if this isn't completely accurate right now, I believe advancements in LLMs will make this generally true, and the quality of LLM-generated code will increase. It likely won't replace the artistry and craftsmanship of hand-crafted code, but as far as corporations see, I believe it will produce code at a quality indistinguishable from human code.</p>\n<p>So where does that leave us?</p>\n<p>...I don't know. I'm trying to answer this question myself. I enjoy puzzles. I enjoy problem-solving. I enjoy meticulously crafting line-by-line a cohesive answer to a problem in the same way I enjoy crafting an elegant mathematical proof. I enjoy programming.</p>\n<p>The way that the software industry seems to be heading is increasing demands on productivity in a way that I can't keep up without the use of AI, yet using AI in the ways to keep up in the ways I need to is fundamentally at odds with what made working in the software industry appealing to begin with though.</p>\n<p>This is a rather bleak outlook.</p>\n<p>I suppose I should just embrace this. This predicament is surely not unique to the software industry, and it may not be the first time a predicament of this nature has happened even within the software industry. Maybe in the late twentieth century, I would have been lamenting about compilers taking the joy out of those who enjoyed writing machine code.</p>\n<p>Only time will tell if we're able to preserve our dignity.</p>\n",
      "date_published": "2026-05-03T07:33:15Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/taking-the-parser-combinator-pill/",
      "url": "https://blog.mnke.org/taking-the-parser-combinator-pill/",
      "title": "Taking The Parser Combinator Pill",
      "content_html": "<h1>The World Is Your Parser</h1>\n<p>I recently discovered the elegance and expressive power of parsing using parser combinators recently and I immediately fell in love with it, wanting to use it to solve new problems using it.</p>\n<p>In fact, I used parser combinators to parse a mini programming language, <a href=\"https://github.com/tonyd33/corelang\">Core</a>, following in close step to &quot;<a href=\"https://www.microsoft.com/en-us/research/publication/implementing-functional-languages-a-tutorial/\">Implementing functional languages: a tutorial</a>&quot; by Simon Peyton Jones, one of the founders of Haskell. I also used parser combinators to implement a <a href=\"https://github.com/tonyd33/node-uci-protocol/tree/master\">parser for the UCI protocol</a> in NodeJS while writing an adapter for another project. And then I implemented the same parser in Gleam, which I'll link to once I make the repo public.</p>\n<p>It's just so satisfying to break down a seemingly complicated language specification down to atoms in a disciplined manner and reconstruct the full language through the power of parser combinators.</p>\n<p>But I'm getting ahead of myself.</p>\n<p>We'll talk a bit about the theory behind parser combinators and build up to writing a fully-functional parser combinator in Haskell to parse JSON.</p>\n<p>Don't worry if you don't know much Haskell. I'll try to keep things simple and explain things as we go. However, I expect readers to be a bit more mature than my <a href=\"https://blog.mnke.org/monads-demystified/\">previous post on monads</a>. There will be less hand-holding throughout this post for sake of brevity, and I expect readers to be able to piece things together themselves.</p>\n<h1>Parser? I hardly know her!</h1>\n<p>...This joke doesn't really work here, does it?</p>\n<p>So what even are parser combinators? Well, in general, a combinator in the context of programming is just something that... combines things. So a parser combinator is just something that combines parsers.</p>\n<p>The idea is that if we can parse, say, the literal string <code>&quot;true&quot;</code>, then we should be able to parse multiple <code>&quot;true&quot;</code>s. And if we can parse a comma and whitespaces, then we should also be able to parse <code>&quot;true&quot;</code> separated by commas and whitespaces. The way we do this is by combining parsers. Parser combinators!</p>\n<p>This is really vague if you're new to parser combinators, so we'll dive into code examples immediately. Just keep the idea of being able to &quot;combine&quot; parsers in the back of your head.</p>\n<h2>Aside: Haskell Workflow</h2>\n<p>Let's get you set up with a comfortable Haskell workflow so you're able to follow along with these examples.</p>\n<p>First, you need to <a href=\"https://www.haskell.org/get-started/\">install Haskell</a>.</p>\n<p>Once that's done, create a new project and create a file called <code>Main.hs</code> with the following contents:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">module</span> <span class=\"token constant\">Main</span> <span class=\"token keyword\">where</span>\n\n<span class=\"token hvariable\">main</span> <span class=\"token operator\">::</span> <span class=\"token constant\">IO</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token hvariable\">main</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token builtin\">putStrLn</span> <span class=\"token string\">\"Hello, World!\"</span></code></pre>\n<p>In a terminal, navigate to the directory containing <code>Main.hs</code> and run <code>ghci</code>, which will spawn a new interactive environment to run Haskell code.</p>\n<p>Run <code>:l Main.hs</code> to load the file, and then run <code>main</code>. You should see <code>Hello, World!</code> printed to the terminal.</p>\n<p>Now, let's add another function:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">module</span> <span class=\"token constant\">Main</span> <span class=\"token keyword\">where</span>\n<span class=\"token operator\">...</span>\n<span class=\"token hvariable\">double</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Int</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Int</span>\n<span class=\"token hvariable\">double</span> <span class=\"token hvariable\">x</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">x</span> <span class=\"token operator\">*</span> <span class=\"token number\">2</span></code></pre>\n<p>In the existing GHCi session, you can just type <code>:r</code> to reload the file. Now you can actually call <code>double 5</code> to execute the code you just wrote.</p>\n<p>This is my preferred Haskell development workflow, where you make some changes to a file, reload, and then immediately start executing code interactively.</p>\n<h2>The Parser</h2>\n<p>The basic building block of parser combinators – the thing that's being combined – is, of course, the parser. Let's define a parser.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">type</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Maybe</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">String</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Equivalently, in TypeScript,</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">Parser<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">[</span><span class=\"token constant\">A</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span></code></pre>\n<p>A <code>Parser a</code> is something that accepts an input (in our case, we are restricting ourselves to string inputs) and produces an <code>a</code> along with the rest of the input if successful, or it produces nothing if it fails.</p>\n<h2>Character Parser</h2>\n<p>So we might define a function to create a parser to parse a single character like so:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">char</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Char</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">char</span> <span class=\"token hvariable\">c</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">f</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">x</span><span class=\"token operator\">:</span><span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token hvariable\">x</span> <span class=\"token operator\">==</span> <span class=\"token hvariable\">c</span>    <span class=\"token operator\">=</span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">c</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token builtin\">otherwise</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span></code></pre>\n<p>In English, <code>char</code> is a function that accepts a character <code>c</code> and returns a function that checks if the first character is <code>c</code>. If it is, then return the successfully parsed <code>c</code> and the rest of the string. Otherwise, if <code>c</code> didn't match or the input was empty, then return an error result.</p>\n<p>If you reload GHCi with <code>:r</code> and run <code>char 'a' &quot;abcd&quot;</code>, you should get <code>Just ('a', &quot;bcd&quot;)</code>. We've successfully created a parser to parse a character!</p>\n<p>Now, we'll set our sights on parsing a literal string.</p>\n<h2>String Parser</h2>\n<p>An obvious way of parsing strings might be to write something like this:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">module</span> <span class=\"token constant\">Main</span> <span class=\"token keyword\">where</span>\n\n<span class=\"token import-statement\"><span class=\"token keyword\">import</span> Data<span class=\"token punctuation\">.</span>List</span>\n<span class=\"token operator\">...</span>\n<span class=\"token hvariable\">str</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">str</span> <span class=\"token hvariable\">s</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">f</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token hvariable\">input</span>\n      <span class=\"token operator\">|</span> <span class=\"token hvariable\">s</span> <span class=\"token operator\">`isPrefixOf`</span> <span class=\"token hvariable\">input</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">s</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">drop</span> <span class=\"token punctuation\">(</span><span class=\"token builtin\">length</span> <span class=\"token hvariable\">s</span><span class=\"token punctuation\">)</span> <span class=\"token hvariable\">input</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token builtin\">otherwise</span>            <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span></code></pre>\n<p>You can verify that it works by running <code>str &quot;foo&quot; &quot;foo bar baz&quot;</code> and getting <code>Just (&quot;foo&quot;, &quot; bar baz&quot;)</code>.</p>\n<p>However, this is not a very combinatorial approach. If we're able to parse individual characters, surely we should be able to combine a parser that parses <code>'f'</code>, a parser that parses <code>'o'</code>, and a parser that parses <code>'o'</code> to create a parser that parses <code>&quot;foo&quot;</code>, right?</p>\n<p>Indeed, we can! But before we do that, we need to upgrade our parser slightly and prove to Haskell that it's a monad. In doing this, we unlock a ton of features for our <code>Parser</code> that we'll use anyway, so we're not preemptively overengineering.</p>\n<h2>Training Arc</h2>\n<p>If you look at the <a href=\"https://hackage.haskell.org/package/base-4.21.0.0/docs/Control-Monad.html#t:Monad\">reference for <code>Monad</code>s</a> on Haskell, you'll find:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">class</span> <span class=\"token constant\">Applicative</span> <span class=\"token hvariable\">m</span> <span class=\"token operator\">=></span> <span class=\"token constant\">Monad</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">m</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Type</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Type</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">where</span>\n  <span class=\"token operator\">...</span></code></pre>\n<p>This just tells us that in order to be a <code>Monad</code>, our parser has to be an <code>Applicative</code> first. Okay, <a href=\"https://hackage.haskell.org/package/base-4.21.0.0/docs/Control-Applicative.html#t:Applicative\">clicking into <code>Applicative</code></a>, we see this:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">class</span> <span class=\"token constant\">Functor</span> <span class=\"token hvariable\">f</span> <span class=\"token operator\">=></span> <span class=\"token constant\">Applicative</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">f</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Type</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Type</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">where</span>\n  <span class=\"token operator\">...</span></code></pre>\n<p>Alright, so our parser has to be a <code>Functor</code> first. Looking at the <a href=\"https://hackage.haskell.org/package/base-4.21.0.0/docs/Control-Monad.html#t:Functor\">reference for <code>Functor</code></a>, we have to prove that our parser supports the <code>fmap</code>, or functor map operation.</p>\n<p>To do this, we need to define our parser using the <code>newtype</code> syntax. I won't get into why, but using <code>newtype</code> to prove typeclasses is very common in Haskell.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">newtype</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span>\n  <span class=\"token punctuation\">{</span> <span class=\"token hvariable\">runParser</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Maybe</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">String</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span></code></pre>\n<p>This is essentially the same definition as before, but we just have to put <code>Parser</code> in front of the parsing function. So our <code>char</code> function just has to be changed to this:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">char</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Char</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">char</span> <span class=\"token hvariable\">c</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">f</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">x</span><span class=\"token operator\">:</span><span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token hvariable\">x</span> <span class=\"token operator\">==</span> <span class=\"token hvariable\">c</span>    <span class=\"token operator\">=</span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">c</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token builtin\">otherwise</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span></code></pre>\n<p>Additionally, if you want to run the parser now, you have to call <code>runParser (char 'a') &quot;abcd&quot;</code>.</p>\n<h3>Functor</h3>\n<p>Alright, now let's prove <code>Parser</code> is a <code>Functor</code>.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">instance</span> <span class=\"token constant\">Functor</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token builtin\">fmap</span> <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">p</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">do</span>\n    <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">p</span> <span class=\"token hvariable\">input</span>\n    <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">f</span> <span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input'</span><span class=\"token punctuation\">)</span></code></pre>\n<p><code>fmap</code> is a function that maps the &quot;inner&quot; value of <code>Parser</code>. What that amounts to is: we run a <code>Parser a</code> on the input to get an <code>(a, input')</code>, where <code>input'</code> is the rest of the input after parsing the <code>a</code>, and then we simply transform <code>a</code> into <code>f a</code> before returning it.</p>\n<p>Let's see it in action:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>char <span class=\"token string\">'a'</span><span class=\"token punctuation\">)</span> <span class=\"token string\">\"abcd\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span>,<span class=\"token string\">\"bcd\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>This is expected. But what if we wanted to get the character code of the character that we parsed instead? Try this:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> <span class=\"token function\">import</span> Data.Char <span class=\"token punctuation\">(</span>ord<span class=\"token punctuation\">)</span>\nghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>fmap ord <span class=\"token punctuation\">(</span>char <span class=\"token string\">'a'</span><span class=\"token punctuation\">))</span> <span class=\"token string\">\"abcd\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token number\">97</span>,<span class=\"token string\">\"bcd\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>We transformed the &quot;inner&quot; value <code>'a'</code> into its character code using <code>ord</code> by using <code>fmap</code>. Another way to write this would be:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>ord <span class=\"token operator\">&lt;</span>$<span class=\"token operator\">></span> char <span class=\"token string\">'a'</span><span class=\"token punctuation\">)</span> <span class=\"token string\">\"abcd\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token number\">97</span>,<span class=\"token string\">\"bcd\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Here, the <code>&lt;$&gt;</code> acts like the regular <code>$</code> function in Haskell, except the function gets to &quot;inject&quot; itself into a functor. It's really just an infix version of <code>fmap</code>.</p>\n<p>If we really want our <code>Parser</code> to behave properly as a <code>Functor</code>, there are <a href=\"https://wiki.haskell.org/Functor#Functor_Laws\">functor laws</a> we should also prove (this proof takes place outside of code). I'll leave proving the functor laws as an exercise to the reader.</p>\n<h3>Applicative</h3>\n<p>We're finished with one-third of our side quest. Now we just have to prove <code>Parser</code> is an <code>Applicative</code>. According to <a href=\"https://hackage.haskell.org/package/base-4.21.0.0/docs/Control-Applicative.html#t:Applicative\">the reference</a>, we need to implement two functions: <code>pure</code> and (<code>&lt;*&gt;</code> or <code>liftA2</code>).</p>\n<p>Let's start by implementing <code>pure</code>.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">instance</span> <span class=\"token constant\">Applicative</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token hvariable\">pure</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input</span><span class=\"token punctuation\">)</span></code></pre>\n<p>There's basically no other way we could implement <code>pure</code>. After all, <code>pure</code> is supposed to be a function that accepts an <code>a</code> and returns a <code>Parser a</code>. The only sane choice is to create a <code>Parser</code> that always returns <code>a</code>, regardless of the input.</p>\n<p>Moving on to <code>liftA2</code>,</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">instance</span> <span class=\"token constant\">Applicative</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token operator\">...</span>\n  <span class=\"token hvariable\">liftA2</span> <span class=\"token hvariable\">g</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">pa</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">pb</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">do</span>\n    <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pa</span> <span class=\"token hvariable\">input</span>\n    <span class=\"token punctuation\">(</span><span class=\"token hvariable\">b</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input''</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pb</span> <span class=\"token hvariable\">input'</span>\n    <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">g</span> <span class=\"token hvariable\">a</span> <span class=\"token hvariable\">b</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input''</span><span class=\"token punctuation\">)</span></code></pre>\n<p><code>liftA2</code> in general allows us to apply functions to values within functors. For example, if we had a <code>Maybe a</code> and a <code>Maybe b</code> and a function <code>a -&gt; b -&gt; c</code>, I should be able to unwrap the <code>a</code> and <code>b</code> out of the <code>Maybe</code>s and then run <code>a -&gt; b -&gt; c</code> to get a <code>Maybe c</code> right? Or if one of the <code>Maybe</code>s was a <code>Nothing</code>, then we should end up returning <code>Nothing</code>. The same logic applies to <code>liftA2</code> on <code>Parser</code>s.</p>\n<p>To verify it works, let's check it in GHCi:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>liftA2 <span class=\"token punctuation\">(</span><span class=\"token punctuation\">\\</span>x y -<span class=\"token operator\">></span> <span class=\"token punctuation\">[</span>x,y<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span>char <span class=\"token string\">'a'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span>char <span class=\"token string\">'b'</span><span class=\"token punctuation\">))</span> <span class=\"token string\">\"abcd\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token string\">\"ab\"</span>,<span class=\"token string\">\"cd\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>What's happening is that <code>liftA2 (\\x y -&gt; [x, y])</code> is &quot;lifting&quot; the lambda expression into the world of <code>Parser</code>s so that we can apply it to values inside <code>char 'a'</code> and <code>char 'b'</code>.</p>\n<p>Commonly, you'll also see this being written as</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">runParser</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token operator\">\\</span><span class=\"token hvariable\">x</span> <span class=\"token hvariable\">y</span> <span class=\"token operator\">-></span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">x</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">y</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'a'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;*></span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'b'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token string\">\"abcd\"</span></code></pre>\n<p>This is because it allows you to chain more arguments easily. If you also wanted to parse a <code>'c'</code>, then you could just just add to the expression:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token variable\"><span class=\"token punctuation\">((</span>\\x y z <span class=\"token operator\">-</span><span class=\"token operator\">></span> [x<span class=\"token punctuation\">,</span> y<span class=\"token punctuation\">,</span> z]<span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span>$<span class=\"token operator\">></span> <span class=\"token punctuation\">(</span>char 'a'<span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span><span class=\"token operator\">*</span><span class=\"token operator\">></span> <span class=\"token punctuation\">(</span>char 'b'<span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span><span class=\"token operator\">*</span><span class=\"token operator\">></span> <span class=\"token punctuation\">(</span>char 'c'<span class=\"token punctuation\">))</span></span>  <span class=\"token string\">\"abcd\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token string\">\"abc\"</span>,<span class=\"token string\">\"d\"</span><span class=\"token punctuation\">)</span></code></pre>\n<h3>Monad</h3>\n<p>Final stretch. Monads only require the <code>&gt;&gt;=</code> operation, or <code>bind</code> to be implemented.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">instance</span> <span class=\"token constant\">Monad</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">pa</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">>>=</span> <span class=\"token hvariable\">f</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">do</span>\n    <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pa</span> <span class=\"token hvariable\">input</span>\n    <span class=\"token keyword\">let</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">pb</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">f</span> <span class=\"token hvariable\">a</span>\n    <span class=\"token hvariable\">pb</span> <span class=\"token hvariable\">input'</span></code></pre>\n<p>We can more or less play &quot;<a href=\"https://www.reddit.com/r/haskell/comments/1yvfmc/programming_with_types_not_tutorials/\">type tetris</a>&quot; to implement this. We have that <code>pa :: String -&gt; Maybe (a, String)</code>, <code>f :: a -&gt; Parser b</code> and we need to return a <code>Parser b</code>. The obvious choice is to run the <code>pa</code> on the input to get an <code>a</code>, and then get a <code>Parser pb</code> from <code>f a</code>, and then return <code>pb</code> on <code>input'</code>, the input after parsing the <code>a</code>.</p>\n<p>Now we can define a parser to parse the string <code>&quot;true&quot;</code>!</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">true</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Bool</span>\n<span class=\"token hvariable\">true</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token hvariable\">_</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">char</span> <span class=\"token char string\">'t'</span>\n  <span class=\"token hvariable\">_</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">char</span> <span class=\"token char string\">'r'</span>\n  <span class=\"token hvariable\">_</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">char</span> <span class=\"token char string\">'u'</span>\n  <span class=\"token hvariable\">_</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">char</span> <span class=\"token char string\">'e'</span>\n  <span class=\"token builtin\">return</span> <span class=\"token constant\">True</span></code></pre>\n<p>Or you can even write it like this:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">true</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Bool</span>\n<span class=\"token hvariable\">true</span> <span class=\"token operator\">=</span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'t'</span> <span class=\"token operator\">>></span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'r'</span> <span class=\"token operator\">>></span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'u'</span> <span class=\"token operator\">>></span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'e'</span> <span class=\"token operator\">>></span>\n  <span class=\"token builtin\">return</span> <span class=\"token constant\">True</span></code></pre>\n<p>Let's check that it works:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token boolean\">true</span> <span class=\"token string\">\"true false\"</span>\nJust <span class=\"token punctuation\">(</span>True,<span class=\"token string\">\" false\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Pretty neat, huh? And don't worry if this is still too cumbersome. We're on our way to creating a function that accepts any string and creates a parser that parses that string. By then, we should be able to define <code>true = str &quot;true&quot; &gt;&gt; return True</code>.</p>\n<p>Similar to the functor laws, we should check that our monad satisfies the <a href=\"https://wiki.haskell.org/Monad_laws\">monad laws</a>, which I'll leave as an exercise to the reader.</p>\n<h3>Alternative</h3>\n<p>While we're at it, let's <a href=\"https://hackage.haskell.org/package/base-4.21.0.0/docs/Control-Applicative.html#g:2\">implement <code>Alternative</code></a> on our <code>Parser</code>, which will allow us to do things like <code>pa &lt;|&gt; pb</code>, which is a parser that tries to parse <code>pa</code> first, and if it fails, then parse <code>pb</code>.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">instance</span> <span class=\"token constant\">Alternative</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token hvariable\">empty</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">(</span><span class=\"token builtin\">const</span> <span class=\"token constant\">Nothing</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">p1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;|></span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">p2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span>\n    <span class=\"token hvariable\">p1</span> <span class=\"token hvariable\">input</span> <span class=\"token operator\">&lt;|></span> <span class=\"token hvariable\">p2</span> <span class=\"token hvariable\">input</span></code></pre>\n<p>This one's easy. <code>empty</code> needs to give a <code>Parser a</code>. Well, how can we even create a <code>Parser a</code>? We can't. So we have to create a <code>Parser</code> that always fails.</p>\n<p>Since <code>Maybe</code> is itself an <code>Alternative</code>, we can just run each parser on the input and then use the implementation for <code>&lt;|&gt;</code> on <code>Maybe</code> to implement <code>&lt;|&gt;</code>.</p>\n<p>Let's test it out by writing a parser for <code>False</code> and then trying to parse <code>&quot;true&quot;</code> or <code>&quot;false&quot;</code>.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">false</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Bool</span>\n<span class=\"token hvariable\">false</span> <span class=\"token operator\">=</span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'f'</span> <span class=\"token operator\">>></span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'a'</span> <span class=\"token operator\">>></span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'l'</span> <span class=\"token operator\">>></span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'s'</span> <span class=\"token operator\">>></span>\n  <span class=\"token hvariable\">char</span> <span class=\"token char string\">'e'</span> <span class=\"token operator\">>></span>\n  <span class=\"token builtin\">return</span> <span class=\"token constant\">False</span></code></pre>\n<p>Let's check that it works in GHCi:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>true <span class=\"token operator\">&lt;</span><span class=\"token operator\">|</span><span class=\"token operator\">></span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span> <span class=\"token string\">\"false\"</span>\nJust <span class=\"token punctuation\">(</span>False,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span>\nghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>true <span class=\"token operator\">&lt;</span><span class=\"token operator\">|</span><span class=\"token operator\">></span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span> <span class=\"token string\">\"true\"</span>\nJust <span class=\"token punctuation\">(</span>True,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span></code></pre>\n<h2>String Parser Mk. 2</h2>\n<p>Let's finally re-define <code>str</code> now that we have the power of the monad. The idea is that, since <code>String</code> is literally type-aliased as a <code>[Char]</code>, we can map each <code>Char</code> into a <code>Parser Char</code> using our <code>char</code> function, and then do a fold. Roughly,</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token comment\">-- str \"true\"</span>\n<span class=\"token comment\">-- Turns into a [Parser Char]</span>\n<span class=\"token comment\">-- [char 't', char 'r', 'char u', 'char e']</span>\n<span class=\"token comment\">-- Turns into a Parser [Char]</span>\n<span class=\"token comment\">-- char 't' `(liftA2 (:))` (char 'r' `(liftA2 (:))` ...)</span></code></pre>\n<p>We can translate that into:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">str</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">str</span> <span class=\"token hvariable\">s</span> <span class=\"token operator\">=</span> <span class=\"token builtin\">foldr</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">liftA2</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">pure</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">$</span> <span class=\"token builtin\">map</span> <span class=\"token hvariable\">char</span> <span class=\"token hvariable\">s</span></code></pre>\n<p>But it turns out this sort of operation is common enough that there are shortcuts to writing them.</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> :t traverse\ntraverse\n  :: <span class=\"token punctuation\">(</span>Traversable t, Applicative f<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">(</span>a -<span class=\"token operator\">></span> f b<span class=\"token punctuation\">)</span> -<span class=\"token operator\">></span> t a -<span class=\"token operator\">></span> f <span class=\"token punctuation\">(</span>t b<span class=\"token punctuation\">)</span>\nghci<span class=\"token operator\">></span> :t mapM\nmapM :: <span class=\"token punctuation\">(</span>Traversable t, Monad m<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">(</span>a -<span class=\"token operator\">></span> m b<span class=\"token punctuation\">)</span> -<span class=\"token operator\">></span> t a -<span class=\"token operator\">></span> m <span class=\"token punctuation\">(</span>t b<span class=\"token punctuation\">)</span></code></pre>\n<p>The general pattern is that we're able to turn an <code>[Char]</code> into an <code>[Parser Char]</code>, but what we really wanted was an <code>Parser [Char]</code>! <code>traverse</code> or <code>mapM</code> allows us to &quot;turn the applicative/monad inside out of a list&quot; as long as we know how to turn each element of the list into a monad/applicative. <code>traverse</code> is more general because it only requires that <code>f</code> be an <code>Applicative</code>, which <code>Monad</code> must satisfy already. In fact <a href=\"https://hackage.haskell.org/package/ghc-internal-9.1201.0/docs/src/GHC.Internal.Data.Traversable.html#mapM\"><code>mapM</code> is literally defined to be <code>traverse</code>.</a> So let's use it.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">str</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">str</span> <span class=\"token hvariable\">s</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">traverse</span> <span class=\"token hvariable\">char</span> <span class=\"token hvariable\">s</span></code></pre>\n<p>With an eta reduction, we can get:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">str</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">str</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">traverse</span> <span class=\"token hvariable\">char</span></code></pre>\n<p>Yup, this is our entire function. If you're newer to Haskell and functional programming, you might accuse me of making this unreadable compared to what we had before, either the version with <code>foldr</code> or the version with <code>isPrefixOf</code>, but as you get to understand Haskell, you'll realize this is a much more natural and elegant solution compared to the previous iterations. Again, the key to <code>traverse</code> is recognizing that we're just &quot;turning the applicative inside out of a list&quot; (more generally, any <code>Traversable</code>).</p>\n<p>Let's rewrite <code>true</code> and <code>false</code> with our upgraded <code>str</code> function:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">true</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Bool</span>\n<span class=\"token hvariable\">true</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">str</span> <span class=\"token string\">\"true\"</span> <span class=\"token operator\">>></span> <span class=\"token builtin\">return</span> <span class=\"token constant\">True</span>\n\n<span class=\"token hvariable\">false</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Bool</span>\n<span class=\"token hvariable\">false</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">str</span> <span class=\"token string\">\"false\"</span> <span class=\"token operator\">>></span> <span class=\"token builtin\">return</span> <span class=\"token constant\">False</span></code></pre>\n<p>And we may as well write a parser for a <code>Bool</code> in general:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">bool</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Bool</span>\n<span class=\"token hvariable\">bool</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">true</span> <span class=\"token operator\">&lt;|></span> <span class=\"token hvariable\">false</span></code></pre>\n<h2>Combinators</h2>\n<p>I mentioned before that we should be able to take existing parsers and &quot;combine&quot; them to parse bigger things, right? Well, we actually already have one for free: <code>many</code>. We get to use it for free because we proved <code>Parser</code> was an <code>Alternative</code>!</p>\n<p>Try it out, it works exactly like you think:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>many <span class=\"token punctuation\">(</span>str <span class=\"token string\">\"foo\"</span><span class=\"token punctuation\">))</span> <span class=\"token string\">\"foofoofoobar\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"foo\"</span>,<span class=\"token string\">\"foo\"</span>,<span class=\"token string\">\"foo\"</span><span class=\"token punctuation\">]</span>,<span class=\"token string\">\"bar\"</span><span class=\"token punctuation\">)</span>\nghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>many <span class=\"token punctuation\">(</span>str <span class=\"token string\">\"foo\"</span><span class=\"token punctuation\">))</span> <span class=\"token string\">\"\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>I want to remind the reader that the end goal is to parse JSON. So let's define combinators to help us do that.</p>\n<h3>Warmup</h3>\n<p>Let's start by defining <code>many1</code>, which is exactly like <code>many</code>, except it requires parsing at least one item.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">many1</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token hvariable\">a</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pa</span>\n  <span class=\"token hvariable\">as</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">many</span> <span class=\"token hvariable\">pa</span>\n  <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token operator\">:</span><span class=\"token hvariable\">as</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Put more succinctly,</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">many1</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">&lt;*></span> <span class=\"token hvariable\">many</span> <span class=\"token hvariable\">pa</span></code></pre>\n<p>(If you feel uncomfortable with notation, don't use it! Stick with what works for you.)</p>\n<h3>Separator</h3>\n<p><code>sepBy</code> will take two parsers and give us a parser that alternates between using each parser. We should end up with something like this:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>bool <span class=\"token variable\"><span class=\"token variable\">`</span>sepBy<span class=\"token variable\">`</span></span> <span class=\"token punctuation\">(</span>str <span class=\"token string\">\", \"</span><span class=\"token punctuation\">))</span> <span class=\"token string\">\"true, false, true, true foo bar\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>True, False, True, True<span class=\"token punctuation\">]</span>, <span class=\"token string\">\" foo bar\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Let's try this:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">sepBy</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">b</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">sepBy</span> <span class=\"token hvariable\">pa</span> <span class=\"token hvariable\">sep</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token hvariable\">a</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pa</span>\n  <span class=\"token hvariable\">as</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">many</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">sep</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">pa</span><span class=\"token punctuation\">)</span>\n  <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token operator\">:</span><span class=\"token hvariable\">as</span><span class=\"token punctuation\">)</span></code></pre>\n<p>We first have to parse using <code>pa</code>, and then we parse a bunch of <code>sep</code>s, ignore the result (that's what <code>&gt;&gt;</code> is doing), and parse using <code>pa</code>.</p>\n<p>Giving it a go in GHCi:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>bool <span class=\"token variable\"><span class=\"token variable\">`</span>sepBy<span class=\"token variable\">`</span></span> <span class=\"token punctuation\">(</span>str <span class=\"token string\">\", \"</span><span class=\"token punctuation\">))</span> <span class=\"token string\">\"true, false, true\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>True,False,True<span class=\"token punctuation\">]</span>,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span>\nghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>bool <span class=\"token variable\"><span class=\"token variable\">`</span>sepBy<span class=\"token variable\">`</span></span> <span class=\"token punctuation\">(</span>str <span class=\"token string\">\", \"</span><span class=\"token punctuation\">))</span> <span class=\"token string\">\"\"</span>\nNothing</code></pre>\n<p>Ah, but we should be parsing an empty string as <code>[]</code>, not failing. Let's make it so that if we failed to parse, then we just return an <code>[]</code>, using our <code>&lt;|&gt;</code> operator.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">sepBy</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">b</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">sepBy</span> <span class=\"token hvariable\">pa</span> <span class=\"token hvariable\">sep</span> <span class=\"token operator\">=</span>\n  <span class=\"token punctuation\">(</span> <span class=\"token keyword\">do</span>\n      <span class=\"token hvariable\">a</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pa</span>\n      <span class=\"token hvariable\">as</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">many</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">sep</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">pa</span><span class=\"token punctuation\">)</span>\n      <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token operator\">:</span><span class=\"token hvariable\">as</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;|></span> <span class=\"token builtin\">return</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span></code></pre>\n<p>Now it's fixed!</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>bool <span class=\"token variable\"><span class=\"token variable\">`</span>sepBy<span class=\"token variable\">`</span></span> <span class=\"token punctuation\">(</span>str <span class=\"token string\">\", \"</span><span class=\"token punctuation\">))</span> <span class=\"token string\">\"\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Written a bit more succinctly:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">sepBy</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">b</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">sepBy</span> <span class=\"token hvariable\">pa</span> <span class=\"token hvariable\">sep</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">&lt;*></span> <span class=\"token hvariable\">many</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">sep</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">pa</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;|></span> <span class=\"token builtin\">return</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span></code></pre>\n<p>(Again, don't use this if you're not comfortable with this.)</p>\n<h3>Between</h3>\n<p>We want a combinator like this:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser <span class=\"token punctuation\">(</span>between <span class=\"token punctuation\">(</span>char <span class=\"token string\">'['</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span>char <span class=\"token string\">']'</span><span class=\"token punctuation\">)</span> bools<span class=\"token punctuation\">)</span> <span class=\"token string\">\"[true, false, true]\"</span>\nJust <span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>True,False,True<span class=\"token punctuation\">]</span>,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>This one's pretty simple and I won't even bother explaining it.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">between</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">b</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">c</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">c</span>\n<span class=\"token hvariable\">between</span> <span class=\"token hvariable\">pl</span> <span class=\"token hvariable\">pr</span> <span class=\"token hvariable\">pc</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token hvariable\">_</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pl</span>\n  <span class=\"token hvariable\">c</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pc</span>\n  <span class=\"token hvariable\">_</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pr</span>\n  <span class=\"token builtin\">return</span> <span class=\"token hvariable\">c</span></code></pre>\n<p>Put more succinctly,</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">between</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">b</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">c</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">c</span>\n<span class=\"token hvariable\">between</span> <span class=\"token hvariable\">pl</span> <span class=\"token hvariable\">pr</span> <span class=\"token hvariable\">pc</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">pl</span> <span class=\"token operator\">*></span> <span class=\"token hvariable\">pc</span> <span class=\"token operator\">&lt;*</span> <span class=\"token hvariable\">pr</span></code></pre>\n<h1>Parsing JSON</h1>\n<p>We've built all the ingredients we need to parse JSON. To make life a bit easier, let's first build a couple of common parsers we'll be using. These are also parsers that you'll likely take along with you to parse other things.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">satisfy</span> <span class=\"token operator\">::</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Char</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Bool</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">satisfy</span> <span class=\"token hvariable\">pr</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">f</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">x</span><span class=\"token operator\">:</span><span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token hvariable\">pr</span> <span class=\"token hvariable\">x</span>      <span class=\"token operator\">=</span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">x</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token builtin\">otherwise</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span>\n\n<span class=\"token hvariable\">oneOf</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">oneOf</span> <span class=\"token hvariable\">s</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">satisfy</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">`elem`</span> <span class=\"token hvariable\">s</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">noneOf</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">noneOf</span> <span class=\"token hvariable\">s</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">satisfy</span> <span class=\"token punctuation\">(</span><span class=\"token builtin\">not</span> <span class=\"token operator\">.</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">`elem`</span> <span class=\"token hvariable\">s</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">digit</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">digit</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">oneOf</span> <span class=\"token string\">\"0123456789\"</span>\n\n<span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">many</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">oneOf</span> <span class=\"token string\">\"\\r\\n\\t \"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">>></span> <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">commaSep</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">commaSep</span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">`sepBy`</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">','</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">whitespaces</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">doubleQuoted</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span>\n<span class=\"token hvariable\">doubleQuoted</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">between</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'\"'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'\"'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">brackets</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span>\n<span class=\"token hvariable\">brackets</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">between</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'['</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">']'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">curlies</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span>\n<span class=\"token hvariable\">curlies</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">between</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'{'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'}'</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Let's define an AST for JSON to store our data into:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">data</span> <span class=\"token constant\">JSON</span>\n  <span class=\"token operator\">=</span> <span class=\"token constant\">JSONNull</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONBool</span>   <span class=\"token constant\">Bool</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONNumber</span> <span class=\"token constant\">Float</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONString</span> <span class=\"token constant\">String</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONArray</span>  <span class=\"token punctuation\">[</span><span class=\"token constant\">JSON</span><span class=\"token punctuation\">]</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONObject</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">(</span><span class=\"token constant\">String</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">deriving</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Eq</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">Show</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Great, let's knock out the easy ones right off the bat:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">jsonNull</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonNull</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONNull</span> <span class=\"token operator\">&lt;$</span> <span class=\"token hvariable\">str</span> <span class=\"token string\">\"null\"</span>\n\n<span class=\"token hvariable\">jsonBool</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonBool</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONBool</span> <span class=\"token operator\">&lt;$></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token constant\">True</span> <span class=\"token operator\">&lt;$</span> <span class=\"token hvariable\">str</span> <span class=\"token string\">\"true\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;|></span> <span class=\"token punctuation\">(</span><span class=\"token constant\">False</span> <span class=\"token operator\">&lt;$</span> <span class=\"token hvariable\">str</span> <span class=\"token string\">\"false\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Again, don't be scared of the notation. We already discussed how to parse <code>Bool</code>s, so if you're uncomfortable with this, fall back to using that.</p>\n<p>Parsing numbers is a little bit tougher because we have to account for the fact that numbers can be written with or without the <code>.</code>. We can easily split it into two cases though:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">jsonNumber</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonNumber</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONNumber</span> <span class=\"token operator\">&lt;$></span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">float</span> <span class=\"token operator\">&lt;|></span> <span class=\"token hvariable\">int</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">float</span> <span class=\"token operator\">=</span> <span class=\"token builtin\">read</span> <span class=\"token operator\">&lt;$></span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">digit</span> <span class=\"token operator\">&lt;*></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">char</span> <span class=\"token char string\">'.'</span> <span class=\"token operator\">&lt;*></span> <span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">digit</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token hvariable\">int</span> <span class=\"token operator\">=</span> <span class=\"token builtin\">read</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">digit</span></code></pre>\n<p>It's a little bit ugly because we end up using <code>read</code> to turn the <code>String</code> into a <code>Float</code>, but it works:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser jsonNumber <span class=\"token string\">\"1.23\"</span>\nJust <span class=\"token punctuation\">(</span>JSONNumber <span class=\"token number\">1.23</span>,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span>\nghci<span class=\"token operator\">></span> runParser jsonNumber <span class=\"token string\">\"111\"</span>\nJust <span class=\"token punctuation\">(</span>JSONNumber <span class=\"token number\">111.0</span>,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Now, JSON strings are a bit harder to parse because they might contain escape characters like <code>\\&quot;</code> or <code>\\n</code>. We're going to keep things simple and ignore escape characters.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">jsonCapableString</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">jsonCapableString</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">doubleQuoted</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">many</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">noneOf</span> <span class=\"token string\">\"\\\"\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">jsonString</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonString</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONString</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">jsonCapableString</span></code></pre>\n<p>We have a separate <code>jsonCapableString</code> because we need a <code>Parser String</code> to parse keys in JSON object the same way we parse regular strings.</p>\n<p>For the recursive definitions, let's first write a parser for any <code>JSON</code> value:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">json</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">json</span> <span class=\"token operator\">=</span>\n  <span class=\"token hvariable\">jsonNull</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonBool</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonNumber</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonString</span></code></pre>\n<p>A <code>JSONArray</code> is just a bunch of <code>JSON</code>s, separated by commas.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">jsonArray</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonArray</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONArray</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">brackets</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">commaSep</span> <span class=\"token hvariable\">json</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Let's upgrade our main parser:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">json</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">json</span> <span class=\"token operator\">=</span>\n  <span class=\"token hvariable\">jsonNull</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonBool</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonNumber</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonString</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonArray</span></code></pre>\n<p>Testing it out in GHCi, it seems to work:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">ghci</span><span class=\"token operator\">></span> <span class=\"token hvariable\">runParser</span> <span class=\"token hvariable\">json</span> <span class=\"token string\">\"[true, false, null, 1, 1.1, \\\"foo\\\", []]\"</span>\n<span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">JSONArray</span> <span class=\"token punctuation\">[</span><span class=\"token constant\">JSONBool</span> <span class=\"token constant\">True</span><span class=\"token punctuation\">,</span><span class=\"token constant\">JSONBool</span> <span class=\"token constant\">False</span><span class=\"token punctuation\">,</span><span class=\"token constant\">JSONNull</span><span class=\"token punctuation\">,</span><span class=\"token constant\">JSONNumber</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span><span class=\"token constant\">JSONNumber</span> <span class=\"token number\">1.1</span><span class=\"token punctuation\">,</span><span class=\"token constant\">JSONString</span> <span class=\"token string\">\"foo\"</span><span class=\"token punctuation\">,</span><span class=\"token constant\">JSONArray</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span><span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Finally, let's implement our parser for the <code>JSONObject</code>.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">jsonObject</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonObject</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONObject</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">curlies</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">commaSep</span> <span class=\"token hvariable\">entry</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">entry</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n      <span class=\"token hvariable\">key</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">jsonCapableString</span>\n      <span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">char</span> <span class=\"token char string\">':'</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">>></span> <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token hvariable\">value</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">json</span>\n      <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">key</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">value</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Let's upgrade our main parser for the last time:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">json</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">json</span> <span class=\"token operator\">=</span>\n  <span class=\"token hvariable\">jsonNull</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonBool</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonNumber</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonString</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonArray</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonObject</span></code></pre>\n<p>And to make sure it all parses, let's try something in GHCi:</p>\n<pre class=\"language-shell\"><code class=\"language-shell\">ghci<span class=\"token operator\">></span> runParser jsonObject <span class=\"token string\">\"{<span class=\"token entity\" title=\"\\&quot;\">\\\"</span>foo<span class=\"token entity\" title=\"\\&quot;\">\\\"</span>: [true, false, null, 1, 1.1, <span class=\"token entity\" title=\"\\&quot;\">\\\"</span>foo<span class=\"token entity\" title=\"\\&quot;\">\\\"</span>, []], <span class=\"token entity\" title=\"\\&quot;\">\\\"</span>bar<span class=\"token entity\" title=\"\\&quot;\">\\\"</span>: <span class=\"token entity\" title=\"\\&quot;\">\\\"</span>baz<span class=\"token entity\" title=\"\\&quot;\">\\\"</span>}\"</span>\nJust <span class=\"token punctuation\">(</span>JSONObject <span class=\"token punctuation\">[</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"foo\"</span>,JSONArray <span class=\"token punctuation\">[</span>JSONBool True,JSONBool False,JSONNull,JSONNumber <span class=\"token number\">1.0</span>,JSONNumber <span class=\"token number\">1.1</span>,JSONString <span class=\"token string\">\"foo\"</span>,JSONArray <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>,<span class=\"token punctuation\">(</span><span class=\"token string\">\"bar\"</span>,JSONString <span class=\"token string\">\"baz\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span>,<span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Glorious. A functional JSON parser all within 126 lines of code.</p>\n<p>As an exercise, you should try to parse JSON strings with escape characters to complete the spec.</p>\n<h1>Improvements/Variations</h1>\n<h2>Error Reporting</h2>\n<p>If you wanted to turn this into a proper parser combinator library, there's one main thing we're lacking: error reporting. Currently, if we fail to parse, we just return a <code>Nothing</code>, but this isn't very helpful.</p>\n<p>You might want to make the <code>Parser</code> return an <code>Either</code> so that if something failed to parse, you can report the error.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">newtype</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span>\n  <span class=\"token punctuation\">{</span> <span class=\"token hvariable\">runParser</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Either</span> <span class=\"token constant\">ParseError</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">String</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">type</span> <span class=\"token constant\">ParseError</span> <span class=\"token operator\">=</span> <span class=\"token constant\">String</span></code></pre>\n<p>Additionally, for even better reporting, you should consider keeping track of the position in the original string: things like the line number and character in the column would be a lot of help.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">newtype</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span>\n  <span class=\"token punctuation\">{</span> <span class=\"token hvariable\">runParser</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Input</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Either</span> <span class=\"token constant\">ParseError</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">Input</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">type</span> <span class=\"token constant\">ParseError</span> <span class=\"token operator\">=</span> <span class=\"token constant\">String</span>\n<span class=\"token keyword\">data</span> <span class=\"token constant\">Input</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Input</span>\n  <span class=\"token punctuation\">{</span> <span class=\"token hvariable\">inputLine</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Int</span><span class=\"token punctuation\">,</span>\n    <span class=\"token hvariable\">inputColumn</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Int</span><span class=\"token punctuation\">,</span>\n    <span class=\"token hvariable\">inputStr</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span>\n  <span class=\"token punctuation\">}</span></code></pre>\n<h2>Unordered Input</h2>\n<p>We've been parsing <code>String</code>s as a stream of characters using our parser combinators, but it's easy to imagine parsing other things. Other streams are trivially easy. But what about parsing something like command line flags, where order doesn't matter?</p>\n<p>In this case, we would have to change our strategy slightly.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">newtype</span> <span class=\"token constant\">ArgParser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token constant\">ArgParser</span>\n  <span class=\"token punctuation\">{</span> <span class=\"token hvariable\">runArgParser</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Args</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Maybe</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">Args</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">type</span> <span class=\"token constant\">Args</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token constant\">String</span><span class=\"token punctuation\">]</span></code></pre>\n<p>The function signature looks the same, but the strategy to &quot;consume&quot; something is slightly different. While <code>String</code> represented a continuous stream of data to consume top to bottom, <code>Args</code> represented an unordered bag of items we try to pick from. Here's what an <code>ArgParser</code> for a <code>&quot;--verbose&quot;</code> flag might look like:</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token hvariable\">findPop</span> <span class=\"token operator\">::</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Bool</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Maybe</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token hvariable\">findPop</span> <span class=\"token hvariable\">_</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span>\n<span class=\"token hvariable\">findPop</span> <span class=\"token hvariable\">p</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">x</span><span class=\"token operator\">:</span><span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n  <span class=\"token operator\">|</span> <span class=\"token hvariable\">p</span> <span class=\"token hvariable\">x</span>       <span class=\"token operator\">=</span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">x</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n  <span class=\"token operator\">|</span> <span class=\"token builtin\">otherwise</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">case</span> <span class=\"token hvariable\">findPop</span> <span class=\"token hvariable\">p</span> <span class=\"token hvariable\">xs</span> <span class=\"token keyword\">of</span>\n                  <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">y</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">ys</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">y</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">x</span><span class=\"token operator\">:</span><span class=\"token hvariable\">ys</span><span class=\"token punctuation\">)</span>\n                  <span class=\"token constant\">Nothing</span>      <span class=\"token operator\">-></span> <span class=\"token constant\">Nothing</span>\n\n<span class=\"token hvariable\">verbose</span> <span class=\"token operator\">::</span> <span class=\"token constant\">ArgParser</span> <span class=\"token constant\">Bool</span>\n<span class=\"token hvariable\">verbose</span> <span class=\"token operator\">=</span> <span class=\"token constant\">ArgParser</span> <span class=\"token hvariable\">f</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token hvariable\">args</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">case</span> <span class=\"token hvariable\">findPop</span> <span class=\"token punctuation\">(</span><span class=\"token string\">\"--verbose\"</span> <span class=\"token operator\">==</span><span class=\"token punctuation\">)</span> <span class=\"token hvariable\">args</span> <span class=\"token keyword\">of</span>\n               <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">_</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">args'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">True</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">args'</span><span class=\"token punctuation\">)</span>\n               <span class=\"token constant\">Nothing</span>         <span class=\"token operator\">-></span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">False</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">args</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Of course, if we do this type of parsing, we'd have to rework a lot of things, and we might not be able to make our parser monadic, depending on how you approach it.</p>\n<p>If you're interested in this style of parsing, you might want to see <a href=\"https://github.com/pcapriotti/optparse-applicative\">optparse-applicative</a>.</p>\n<h1>Wrapping Up</h1>\n<p>Learning to use parser combinators has sparked a renaissance in parsing as well as in how I approach writing code in general. I had been familiar with functional programming for a long while, but I never realized how amazingly expressive and powerful code could be if the core tenet of composability was upheld. It's not hard to imagine creating a combinatory library for other domains in a similar way so long as your functions remain composable.</p>\n<p>I can't imagine a world without parser combinators now. I've eaten the forbidden fruit. And if you understood this post, you have too.</p>\n<p>Anyway, here's the final code for the entire JSON parser. It is missing some things like being able to parse negative numbers, numbers in the form <code>10e6</code>, escape characters in string, etc., but it's got the core of a legitimate JSON parser. It's only 126 lines long and I don't want to even bother creating an entire repository for it.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token keyword\">module</span> <span class=\"token constant\">ParserCombinators</span> <span class=\"token keyword\">where</span>\n\n<span class=\"token import-statement\"><span class=\"token keyword\">import</span>           Control<span class=\"token punctuation\">.</span>Applicative</span>\n\n<span class=\"token keyword\">data</span> <span class=\"token constant\">JSON</span>\n  <span class=\"token operator\">=</span> <span class=\"token constant\">JSONNull</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONBool</span>   <span class=\"token constant\">Bool</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONNumber</span> <span class=\"token constant\">Float</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONString</span> <span class=\"token constant\">String</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONArray</span>  <span class=\"token punctuation\">[</span><span class=\"token constant\">JSON</span><span class=\"token punctuation\">]</span>\n  <span class=\"token operator\">|</span> <span class=\"token constant\">JSONObject</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">(</span><span class=\"token constant\">String</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">deriving</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Eq</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">Show</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">newtype</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span>\n  <span class=\"token punctuation\">{</span> <span class=\"token hvariable\">runParser</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Maybe</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">String</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">instance</span> <span class=\"token constant\">Functor</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token builtin\">fmap</span> <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">p</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">do</span>\n    <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">p</span> <span class=\"token hvariable\">input</span>\n    <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">f</span> <span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">instance</span> <span class=\"token constant\">Applicative</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token hvariable\">pure</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input</span><span class=\"token punctuation\">)</span>\n  <span class=\"token hvariable\">liftA2</span> <span class=\"token hvariable\">g</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">pa</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">pb</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">do</span>\n    <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pa</span> <span class=\"token hvariable\">input</span>\n    <span class=\"token punctuation\">(</span><span class=\"token hvariable\">b</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input''</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pb</span> <span class=\"token hvariable\">input'</span>\n    <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">g</span> <span class=\"token hvariable\">a</span> <span class=\"token hvariable\">b</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input''</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">instance</span> <span class=\"token constant\">Alternative</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token hvariable\">empty</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token builtin\">const</span> <span class=\"token constant\">Nothing</span>\n  <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">p1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;|></span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">p2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span>\n    <span class=\"token hvariable\">p1</span> <span class=\"token hvariable\">input</span> <span class=\"token operator\">&lt;|></span> <span class=\"token hvariable\">p2</span> <span class=\"token hvariable\">input</span>\n\n<span class=\"token keyword\">instance</span> <span class=\"token constant\">Monad</span> <span class=\"token constant\">Parser</span> <span class=\"token keyword\">where</span>\n  <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">pa</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">>>=</span> <span class=\"token hvariable\">f</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token operator\">$</span> <span class=\"token operator\">\\</span><span class=\"token hvariable\">input</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">do</span>\n    <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">input'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">pa</span> <span class=\"token hvariable\">input</span>\n    <span class=\"token keyword\">let</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Parser</span> <span class=\"token hvariable\">pb</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">f</span> <span class=\"token hvariable\">a</span>\n    <span class=\"token hvariable\">pb</span> <span class=\"token hvariable\">input'</span>\n\n<span class=\"token hvariable\">char</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Char</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">char</span> <span class=\"token hvariable\">c</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">satisfy</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">c</span> <span class=\"token operator\">==</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">str</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">str</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">traverse</span> <span class=\"token hvariable\">char</span>\n\n<span class=\"token hvariable\">satisfy</span> <span class=\"token operator\">::</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Char</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Bool</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">satisfy</span> <span class=\"token hvariable\">pr</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">f</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">x</span><span class=\"token operator\">:</span><span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token hvariable\">pr</span> <span class=\"token hvariable\">x</span>      <span class=\"token operator\">=</span> <span class=\"token constant\">Just</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">x</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">xs</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">|</span> <span class=\"token builtin\">otherwise</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span>\n    <span class=\"token hvariable\">f</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token constant\">Nothing</span>\n\n<span class=\"token hvariable\">oneOf</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">oneOf</span> <span class=\"token hvariable\">s</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">satisfy</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">`elem`</span> <span class=\"token hvariable\">s</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">noneOf</span> <span class=\"token operator\">::</span> <span class=\"token constant\">String</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">noneOf</span> <span class=\"token hvariable\">s</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">satisfy</span> <span class=\"token punctuation\">(</span><span class=\"token builtin\">not</span> <span class=\"token operator\">.</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">`elem`</span> <span class=\"token hvariable\">s</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">digit</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Char</span>\n<span class=\"token hvariable\">digit</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">oneOf</span> <span class=\"token string\">\"0123456789\"</span>\n\n<span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">many</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">oneOf</span> <span class=\"token string\">\"\\r\\n\\t \"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">>></span> <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">commaSep</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">commaSep</span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">`sepBy`</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">','</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">whitespaces</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">doubleQuoted</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span>\n<span class=\"token hvariable\">doubleQuoted</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">between</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'\"'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'\"'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">brackets</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span>\n<span class=\"token hvariable\">brackets</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">between</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'['</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">']'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">curlies</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span>\n<span class=\"token hvariable\">curlies</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">between</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'{'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">char</span> <span class=\"token char string\">'}'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">sepBy</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">b</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">sepBy</span> <span class=\"token hvariable\">pa</span> <span class=\"token hvariable\">sep</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">&lt;*></span> <span class=\"token hvariable\">many</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">sep</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">pa</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;|></span> <span class=\"token builtin\">return</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token hvariable\">many1</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token punctuation\">[</span><span class=\"token hvariable\">a</span><span class=\"token punctuation\">]</span>\n<span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">pa</span> <span class=\"token operator\">&lt;*></span> <span class=\"token hvariable\">many</span> <span class=\"token hvariable\">pa</span>\n\n<span class=\"token hvariable\">between</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">b</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">c</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Parser</span> <span class=\"token hvariable\">c</span>\n<span class=\"token hvariable\">between</span> <span class=\"token hvariable\">pl</span> <span class=\"token hvariable\">pr</span> <span class=\"token hvariable\">pc</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">pl</span> <span class=\"token operator\">*></span> <span class=\"token hvariable\">pc</span> <span class=\"token operator\">&lt;*</span> <span class=\"token hvariable\">pr</span>\n\n<span class=\"token hvariable\">jsonNull</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonNull</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONNull</span> <span class=\"token operator\">&lt;$</span> <span class=\"token hvariable\">str</span> <span class=\"token string\">\"null\"</span>\n\n<span class=\"token hvariable\">jsonBool</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonBool</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONBool</span> <span class=\"token operator\">&lt;$></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token constant\">True</span> <span class=\"token operator\">&lt;$</span> <span class=\"token hvariable\">str</span> <span class=\"token string\">\"true\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;|></span> <span class=\"token punctuation\">(</span><span class=\"token constant\">False</span> <span class=\"token operator\">&lt;$</span> <span class=\"token hvariable\">str</span> <span class=\"token string\">\"false\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">jsonNumber</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonNumber</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONNumber</span> <span class=\"token operator\">&lt;$></span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">float</span> <span class=\"token operator\">&lt;|></span> <span class=\"token hvariable\">int</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">float</span> <span class=\"token operator\">=</span> <span class=\"token builtin\">read</span> <span class=\"token operator\">&lt;$></span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">digit</span> <span class=\"token operator\">&lt;*></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">char</span> <span class=\"token char string\">'.'</span> <span class=\"token operator\">&lt;*></span> <span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">digit</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token hvariable\">int</span> <span class=\"token operator\">=</span> <span class=\"token builtin\">read</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">many1</span> <span class=\"token hvariable\">digit</span>\n\n<span class=\"token hvariable\">jsonCapableString</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">jsonCapableString</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">doubleQuoted</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">many</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">noneOf</span> <span class=\"token string\">\"\\\"\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">jsonString</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonString</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONString</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">jsonCapableString</span>\n\n<span class=\"token hvariable\">jsonArray</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonArray</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONArray</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">brackets</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">commaSep</span> <span class=\"token hvariable\">json</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">jsonObject</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">jsonObject</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSONObject</span> <span class=\"token operator\">&lt;$></span> <span class=\"token hvariable\">curlies</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">commaSep</span> <span class=\"token hvariable\">entry</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">where</span>\n    <span class=\"token hvariable\">entry</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n      <span class=\"token hvariable\">key</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">jsonCapableString</span>\n      <span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">char</span> <span class=\"token char string\">':'</span> <span class=\"token operator\">>></span> <span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">>></span> <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token hvariable\">value</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">json</span>\n      <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">key</span><span class=\"token punctuation\">,</span> <span class=\"token hvariable\">value</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">json</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">JSON</span>\n<span class=\"token hvariable\">json</span> <span class=\"token operator\">=</span>\n  <span class=\"token hvariable\">jsonNull</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonBool</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonNumber</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonString</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonArray</span> <span class=\"token operator\">&lt;|></span>\n  <span class=\"token hvariable\">jsonObject</span></code></pre>\n",
      "date_published": "2025-05-14T00:49:25Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/monads-demystified/",
      "url": "https://blog.mnke.org/monads-demystified/",
      "title": "Monads, Demystified",
      "content_html": "<h1>M-Monads...? 😨</h1>\n<p>Whenever I bring up the topic of monads with my peers in a programming context, I get the impression that others would like to dismiss the topic immediately, possibly because they think monads are completely out of reach for them, or maybe they think I'm using obscurantist language to sound fancy.</p>\n<p>This is hardly the case though. In practice, monads very elegantly encapsulate certain programming patterns and I think it would be a shame to reject understanding them out of principle.</p>\n<p>In this article, I hope to give some motivation for monads by solving seemingly unrelated real-life problems in programming, and then stepping back to recognize and capture the underlying pattern and distilling it into the monad.</p>\n<p>I'll assume intermediate familiarity with programming, and I'll use JavaScript/TypeScript in my code examples.</p>\n<h1>Hunting The Monad 🤠</h1>\n<h2>Stripe Indexables</h2>\n<p>Stripe's API allows you to &quot;<a href=\"https://docs.stripe.com/expand\">expand</a>&quot; subobjects when retrieving objects. For example, a <code>Subscription</code> has this type signature:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">Subscription</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span>\n  latest_invoice<span class=\"token operator\">:</span> Invoice <span class=\"token operator\">|</span> <span class=\"token builtin\">string</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n  <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>Normally, <code>latest_invoice</code> will be a <code>string | null</code>, but if you request for <code>latest_invoice</code> to be &quot;expanded&quot;, then <code>latest_invoice</code> will take on a shape of <code>Invoice</code>. Similarly, <code>Invoice</code>s has properties that can be expanded itself,</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">Invoice</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span>\n  payment_intent<span class=\"token operator\">:</span> PaymentIntent <span class=\"token operator\">|</span> <span class=\"token builtin\">string</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n  <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>...and so on:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">PaymentIntent</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span>\n  payment_method<span class=\"token operator\">:</span> PaymentMethod <span class=\"token operator\">|</span> <span class=\"token builtin\">string</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n  <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">type</span> <span class=\"token class-name\">PaymentMethod</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span>\n  billing_details<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">...</span>\n    name<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span>\n    <span class=\"token operator\">...</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>The trouble with these type signatures is that it's really difficult to traverse nested properties. For example, if I had a <code>subscription: Subscription</code> and I wanted to get the name that paid for this subscription, I'd have to write something like this, if I wanted to be type-safe and runtime-safe:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> subscription <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> stripe<span class=\"token punctuation\">.</span>subscriptions<span class=\"token punctuation\">.</span><span class=\"token function\">retrieve</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> latest_invoice <span class=\"token operator\">=</span> subscription<span class=\"token punctuation\">.</span>latest_invoice\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>latest_invoice <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span>\n    <span class=\"token keyword\">typeof</span> latest_invoice <span class=\"token operator\">===</span> <span class=\"token string\">'string'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> payment_intent <span class=\"token operator\">=</span> latest_invoice<span class=\"token punctuation\">.</span>payment_intent\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>payment_intent <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span>\n    <span class=\"token keyword\">typeof</span> payment_intent <span class=\"token operator\">===</span> <span class=\"token string\">'string'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> payment_method <span class=\"token operator\">=</span> payment_intent<span class=\"token punctuation\">.</span>payment_method\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>payment_method <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span>\n    <span class=\"token keyword\">typeof</span> payment_method <span class=\"token operator\">===</span> <span class=\"token string\">'string'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> name <span class=\"token operator\">=</span> payment_method\n  <span class=\"token punctuation\">.</span>billing_details\n  <span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">;</span></code></pre>\n<p>What a chore!</p>\n<p>I know that at each step, the object I want to index may not actually be expanded: namely the case where it has a type of <code>string | null</code>. Let's build a wrapper around these objects and do that logic in there:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">MaybeExpanded<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">private</span> inner<span class=\"token operator\">:</span>\n    <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span>tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Expanded\"</span><span class=\"token punctuation\">,</span> value<span class=\"token operator\">:</span> <span class=\"token constant\">A</span><span class=\"token punctuation\">}</span>\n    <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span>tag<span class=\"token operator\">:</span> <span class=\"token string\">\"NotExpanded\"</span><span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">private</span> <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span>mx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>inner <span class=\"token operator\">=</span> inner\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">of</span><span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token constant\">A</span> <span class=\"token operator\">|</span> <span class=\"token builtin\">string</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>x <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span>\n        <span class=\"token keyword\">typeof</span> x <span class=\"token operator\">===</span> <span class=\"token string\">'string'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaybeExpanded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        tag<span class=\"token operator\">:</span> <span class=\"token string\">\"NotExpanded\"</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaybeExpanded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Expanded\"</span><span class=\"token punctuation\">,</span>\n        value<span class=\"token operator\">:</span> x\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>All we did was move the expanded check into this <code>MaybeExpanded.of</code> function. We still don't have a way of actually interacting with the expanded object even if we verified that it's expanded though. So let's add a method that allows us to transform the inner value if it's expanded, otherwise do nothing.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">MaybeExpanded<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span>\n  <span class=\"token generic-function\"><span class=\"token function\">map</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token function-variable function\">f</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>a<span class=\"token operator\">:</span> <span class=\"token constant\">A</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token constant\">B</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> MaybeExpanded<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>inner<span class=\"token punctuation\">.</span>tag <span class=\"token operator\">===</span> <span class=\"token string\">\"NotExpanded\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">this</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaybeExpanded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Expanded\"</span><span class=\"token punctuation\">,</span>\n        value<span class=\"token operator\">:</span> <span class=\"token function\">f</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>inner<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>Ah, but this isn't quite what we need! Take a look:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\">MaybeExpanded<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>subscription<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span>subscription <span class=\"token operator\">=></span> MaybeExpanded<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>subscription<span class=\"token punctuation\">.</span>invoice<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre>\n<p>This will give us a type of <code>MaybeExpanded&lt;MaybeExpanded&lt;Invoice&gt;&gt;</code>, when we really wanted <code>MaybeExpanded&lt;Invoice&gt;</code>!</p>\n<p>Let's upgrade <code>map</code> into <code>flatMap</code>, similar to how <code>Array.flatMap</code> &quot;flattens&quot; the types for us.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">MaybeExpanded<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span>\n  <span class=\"token generic-function\"><span class=\"token function\">flatMap</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token function-variable function\">f</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>a<span class=\"token operator\">:</span> <span class=\"token constant\">A</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> MaybeExpanded<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> MaybeExpanded<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>inner<span class=\"token punctuation\">.</span>tag <span class=\"token operator\">===</span> <span class=\"token string\">\"NotExpanded\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">this</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">f</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>inner<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>Before bringing it back to our example, let's define one more function to make life easier:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">mindex</span> <span class=\"token operator\">=</span>\n  key <span class=\"token operator\">=></span> obj <span class=\"token operator\">=></span> MaybeExpanded<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>obj<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span></code></pre>\n<p>All this function does is create a curried function that accesses an object and wraps it in <code>MaybeExpanded</code>. Don't worry if this doesn't make sense; it's much easier to understand it in action:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> name <span class=\"token operator\">=</span> MaybeExpanded<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>subscription<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">flatMap</span><span class=\"token punctuation\">(</span><span class=\"token function\">mindex</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"latest_invoice\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">flatMap</span><span class=\"token punctuation\">(</span><span class=\"token function\">mindex</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"payment_intent\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">flatMap</span><span class=\"token punctuation\">(</span><span class=\"token function\">mindex</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"payment_method\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span>payment_method <span class=\"token operator\">=></span> payment_method<span class=\"token punctuation\">.</span>billing_details<span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">)</span></code></pre>\n<p>This looks a lot better, right? This is runtime-safe and with a little bit of type plumbing, this is actually type-safe too.</p>\n<p>To wrap this problem up, we need a way to unwrap the value out of the <code>MaybeExpanded</code> wrapper. (There had to be a pun there, but the joke would be wrapped in too many layers of complexity. (Did I really choose the laziest way to make this pun? <em>Maybe</em>. (Or perhaps, by writing this, the pun <em>promises</em> to fulfill itself. (<em>Either</em> way, this is getting too far now.))))</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">MaybeExpanded<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span>\n  <span class=\"token function\">unwrapOrError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token constant\">A</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>inner<span class=\"token punctuation\">.</span>tag <span class=\"token operator\">===</span> <span class=\"token string\">\"NotExpanded\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>inner<span class=\"token punctuation\">.</span>value\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<h2>Logging</h2>\n<p>(This example is taken from by <a href=\"https://www.youtube.com/@DrBartosz\">Bartosz Milewski's</a> example in his series on category theory.)</p>\n<p>Suppose your manager tells you that they need to audit the program. Specifically, they want every function in the program to log that it was called. A quick-and-dirty approach might be to add a <code>console.log</code> at the start of every function:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">function</span> <span class=\"token function\">double</span><span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"double called\"</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> x <span class=\"token operator\">*</span> <span class=\"token number\">2</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>Ah, but your manager tells you that the program should actually log to a file sometimes. Other times, the program should accumulate the logs and send them over email. Okay, fine. How about this?</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">let</span> log <span class=\"token operator\">=</span> <span class=\"token string\">\"\"</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">double</span><span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">{</span>\n  log <span class=\"token operator\">+=</span> <span class=\"token string\">\"double called\"</span>\n  <span class=\"token keyword\">return</span> x <span class=\"token operator\">*</span> <span class=\"token number\">2</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token comment\">// Before the program exits, send the log somewhere</span></code></pre>\n<p>As a good programmer yourself, you can feel that there's something... very wrong about this.</p>\n<p>Part of what's wrong is that this code violates a principle I call &quot;locality of behavior&quot;, which states that code should only be able to affect code &quot;close&quot; to it. In other words, there should be no &quot;spooky action at a distance&quot;, so to speak. If I call <code>double</code>, I should be able to expect that <code>double</code> will not act on something &quot;far&quot; from it!</p>\n<p>So let's refactor this a little bit:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">Logged<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token constant\">A</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">double</span><span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Logged<span class=\"token operator\">&lt;</span><span class=\"token builtin\">number</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span>x <span class=\"token operator\">*</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"double called\"</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>Now, we're forced to pass in the log so that locality of behavior is preserved, and we return the new log after this function is done writing to it.</p>\n<p>However, the return type of the function was previously a <code>number</code>, but we now return a <code>Logged&lt;number&gt;</code>, which is a tuple of <code>[number, string]</code>! If we had some code like <code>double(square(double(x)))</code>, we'd have to change it to something like this:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">function</span> <span class=\"token function\">compute</span><span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Logged<span class=\"token operator\">&lt;</span><span class=\"token builtin\">number</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> log0 <span class=\"token operator\">=</span> <span class=\"token string\">\"compute\"</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>x1<span class=\"token punctuation\">,</span> log1<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">double</span><span class=\"token punctuation\">(</span>x<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>x2<span class=\"token punctuation\">,</span> log2<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">square</span><span class=\"token punctuation\">(</span>x1<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>x3<span class=\"token punctuation\">,</span> log3<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">double</span><span class=\"token punctuation\">(</span>x2<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span>x3<span class=\"token punctuation\">,</span> log0 <span class=\"token operator\">+</span> log1 <span class=\"token operator\">+</span> log2 <span class=\"token operator\">+</span> log3<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>This is pretty hard to work with and it's easy to imagine this getting out of hand really fast. So let's write some tooling to make this easier.</p>\n<p>The idea is that we want to way to &quot;upgrade&quot; functions of the form <code>(a: A) =&gt; Logged&lt;B&gt;</code> into a function with the signature <code>(la: Logged&lt;A&gt;) =&gt; Logged&lt;B&gt;</code> and concatenate the logs automatically through the upgrade.</p>\n<p>Let me demonstrate:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">upgrade</span> <span class=\"token operator\">=</span>\n  <span class=\"token punctuation\">(</span><span class=\"token function-variable function\">f</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>a<span class=\"token operator\">:</span> <span class=\"token constant\">A</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Logged<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token punctuation\">(</span>la<span class=\"token operator\">:</span> Logged<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Logged<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>a<span class=\"token punctuation\">,</span> log1<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> la\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>b<span class=\"token punctuation\">,</span> log2<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">f</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span>b<span class=\"token punctuation\">,</span> log1 <span class=\"token operator\">+</span> log2<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>Now, we can rewrite our computation like so:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> upgradedDouble <span class=\"token operator\">=</span> <span class=\"token function\">upgrade</span><span class=\"token punctuation\">(</span>double<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> upgradedSquare <span class=\"token operator\">=</span> <span class=\"token function\">upgrade</span><span class=\"token punctuation\">(</span>square<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">compute</span><span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Logged<span class=\"token operator\">&lt;</span><span class=\"token builtin\">number</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> x0 <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>x<span class=\"token punctuation\">,</span> <span class=\"token string\">\"compute\"</span><span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">const</span> x1 <span class=\"token operator\">=</span> <span class=\"token function\">upgradedDouble</span><span class=\"token punctuation\">(</span>x0<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> x2 <span class=\"token operator\">=</span> <span class=\"token function\">upgradedSquare</span><span class=\"token punctuation\">(</span>x1<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> x3 <span class=\"token operator\">=</span> <span class=\"token function\">upgradedDouble</span><span class=\"token punctuation\">(</span>x3<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> x3\n<span class=\"token punctuation\">}</span></code></pre>\n<p>This looks a lot better.</p>\n<h2>Interlude</h2>\n<p>By now, you might be thinking that there aren't any patterns to the examples I've presented so far, and may be worried that this is a waste of your time or that you aren't capable of abstracting the pattern.</p>\n<p>I will not fully assuage you of your concerns because part of the effect I want to create is the &quot;aha!&quot; moment where everything comes together gloriously. However, to plant the seed in your mind, I want you to direct your attention to the idea of &quot;background&quot; and &quot;foreground&quot; code.</p>\n<p>In the Stripe example, the &quot;background&quot; code was checking whether something was expandable and the &quot;foreground&quot; code was indexing the object at each step.</p>\n<p>In the logging example, the &quot;background&quot; code was concatenating the logs and the &quot;foreground&quot; code was doing the computations like we did before being forced to do logging.</p>\n<p>In general, put very informally, the &quot;background&quot; code is what you don't really care about: it describes <em>how</em> to achieve your goal. The &quot;foreground&quot; code is what you actually care about: it describes <em>what</em> your goal is.</p>\n<p>Keep this in mind as we move onto our final example.</p>\n<h2>Parsing</h2>\n<p>Suppose we need to implement a parser for a protocol our company created. The protocol is fairly simple, consisting of discrete plaintext messages containing commands to interact with a shopping cart.</p>\n<p>You're presented with examples of messages you may receive:</p>\n<ul>\n<li><code>clearcart</code></li>\n<li><code>modifycart increment item 103 by 2</code></li>\n<li><code>modifycart decrement item 883 by 1</code></li>\n</ul>\n<p>Your goal is to parse these commands into this structure for another team to handle:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">CartCommand</span> <span class=\"token operator\">=</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span>tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Clear\"</span><span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span>tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Modify\"</span><span class=\"token punctuation\">,</span> action<span class=\"token operator\">:</span> CartAction<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">type</span> <span class=\"token class-name\">CartAction</span> <span class=\"token operator\">=</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span>tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Increment\"</span><span class=\"token punctuation\">,</span> itemId<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">,</span> by<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span>tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Decrement\"</span><span class=\"token punctuation\">,</span> itemId<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">,</span> by<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">}</span></code></pre>\n<p>Our strategy will be to use a <a href=\"https://en.wikipedia.org/wiki/Parser_combinator\">parser combinator</a> approach to parsing. If you don't know what that is, don't worry. We won't go into too much depth on the theory of parser combinators in this post.</p>\n<p>The basic building block of the strategy is a <code>Parser</code>. For our purposes, a <code>Parser&lt;A&gt;</code> is something that reads an <code>A</code> from a string and returns <code>A</code> and the rest of the string. If the <code>Parser</code> failed to read an <code>A</code>, then it should return an error result, which will be <code>null</code> in our example.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">Parser<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">[</span><span class=\"token constant\">A</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span></code></pre>\n<p>For example, we might implement a <code>Parser&lt;true&gt;</code> like so:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> ptrue <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>x<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">'true'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> x<span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token string\">'true'</span><span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">else</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">ptrue</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"true\"</span><span class=\"token punctuation\">)</span>     <span class=\"token comment\">// [true, \"\"]</span>\n<span class=\"token function\">ptrue</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"tru\"</span><span class=\"token punctuation\">)</span>      <span class=\"token comment\">// null</span>\n<span class=\"token function\">ptrue</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"true bar\"</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// [true, \" bar\"]</span></code></pre>\n<p>The idea is that we can build parsers for each &quot;atom&quot; of our protocol and then compose them into a big parser that can then parse the entire protocol.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> integer<span class=\"token operator\">:</span> Parser<span class=\"token operator\">&lt;</span><span class=\"token builtin\">number</span><span class=\"token operator\">></span> <span class=\"token operator\">=</span> <span class=\"token operator\">...</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">str</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Parser<span class=\"token operator\">&lt;</span><span class=\"token builtin\">string</span><span class=\"token operator\">></span> <span class=\"token operator\">=</span> <span class=\"token operator\">...</span>\n<span class=\"token keyword\">const</span> whitespaces<span class=\"token operator\">:</span> Parser<span class=\"token operator\">&lt;</span><span class=\"token builtin\">string</span><span class=\"token operator\">></span> <span class=\"token operator\">=</span> <span class=\"token operator\">...</span>\n\n<span class=\"token function\">str</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"bar\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"bar   baz\"</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// [\"bar\", \"   baz\"]</span>\n<span class=\"token function\">whitespaces</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"   baz\"</span><span class=\"token punctuation\">)</span>   <span class=\"token comment\">// [\"   \", \"baz\"]</span>\n<span class=\"token function\">whitespaces</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"baz\"</span><span class=\"token punctuation\">)</span>      <span class=\"token comment\">// null</span></code></pre>\n<p>By having the primitive parsers, we can compose them into a bigger parser!</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> cartActionIncrement<span class=\"token operator\">:</span> Parser<span class=\"token operator\">&lt;</span>CartAction<span class=\"token operator\">></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> r1 <span class=\"token operator\">=</span> <span class=\"token function\">str</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"increment\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>x<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>r1 <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">,</span> rest1<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> r1\n  <span class=\"token keyword\">const</span> r2 <span class=\"token operator\">=</span> <span class=\"token function\">whitespaces</span><span class=\"token punctuation\">(</span>rest1<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>r2 <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">,</span> rest2<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> r2\n  <span class=\"token keyword\">const</span> r3 <span class=\"token operator\">=</span> <span class=\"token function\">str</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"item\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>rest2<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>r3 <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">,</span> rest3<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> r3\n  <span class=\"token keyword\">const</span> r4 <span class=\"token operator\">=</span> <span class=\"token function\">integer</span><span class=\"token punctuation\">(</span>rest3<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>r4 <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>itemId<span class=\"token punctuation\">,</span> rest4<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> r4\n  <span class=\"token operator\">...</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Increment\"</span><span class=\"token punctuation\">,</span>\n    itemId<span class=\"token operator\">:</span> itemId<span class=\"token punctuation\">,</span>\n    by<span class=\"token operator\">:</span> by\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>Okay, this is just awful to look at. All I want to do is parse the string &quot;increment&quot;, some whitespaces, the string &quot;item&quot;, some whitespaces, an item id, etc.</p>\n<p>Let's write a way to chain these parsers easily.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> chain <span class=\"token operator\">=</span>\n  <span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">B</span><span class=\"token operator\">></span>\n  <span class=\"token punctuation\">(</span>pa<span class=\"token operator\">:</span> Parser<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span><span class=\"token punctuation\">,</span>\n   <span class=\"token function-variable function\">f</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>a<span class=\"token operator\">:</span> <span class=\"token constant\">A</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Parser<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Parser<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span> <span class=\"token operator\">=></span> \n  <span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ra <span class=\"token operator\">=</span> <span class=\"token function\">pa</span><span class=\"token punctuation\">(</span>x<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ra <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>a<span class=\"token punctuation\">,</span> rest1<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> ra\n  <span class=\"token keyword\">const</span> pb <span class=\"token operator\">=</span> <span class=\"token function\">f</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">pb</span><span class=\"token punctuation\">(</span>rest1<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p><code>chain</code> takes a <code>Parser&lt;A&gt;</code>, runs it on the input, and then if it was successful, it feeds the result to create a <code>Parser&lt;B&gt;</code> and then returns the result of <code>Parser&lt;B&gt;</code>.</p>\n<p>Now we can rewrite our code easily like so:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> cartActionIncrement<span class=\"token operator\">:</span> Parser<span class=\"token operator\">&lt;</span>CartAction<span class=\"token operator\">></span> <span class=\"token operator\">=</span>\n  <span class=\"token function\">chain</span><span class=\"token punctuation\">(</span><span class=\"token function\">str</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"increment\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    <span class=\"token function\">chain</span><span class=\"token punctuation\">(</span>whitespaces<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> \n      <span class=\"token function\">chain</span><span class=\"token punctuation\">(</span><span class=\"token function\">str</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"item\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> \n        <span class=\"token function\">chain</span><span class=\"token punctuation\">(</span>integer<span class=\"token punctuation\">,</span> itemId <span class=\"token operator\">=></span> \n          <span class=\"token function\">chain</span><span class=\"token punctuation\">(</span>whitespaces<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> \n            <span class=\"token operator\">...</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre>\n<p>Ah. Maybe not.</p>\n<p>This is where we start to run up against the limits of the language. If you're familiar with older JavaScript, this might be giving you flashbacks to &quot;<a href=\"https://medium.com/dsc-srm/javascript-callback-hell-or-pyramid-of-doom-4f786d14b997\">pyramids of doom</a>&quot;. The solution to <code>Promise</code>-based code is to use <code>async/await</code>, but of course, we don't have that at our disposal for <code>Parser</code>s.</p>\n<p>There are actually workarounds provided by libraries like <a href=\"https://medium.com/dsc-srm/javascript-callback-hell-or-pyramid-of-doom-4f786d14b997\"><code>fp-ts</code></a>, but let's just imagine a world where we had similar keywords <code>parser/parse</code>, where we could transform our code to look like this:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> cartActionIncrement<span class=\"token operator\">:</span> Parser<span class=\"token operator\">&lt;</span>CartAction<span class=\"token operator\">></span> <span class=\"token operator\">=</span> parser <span class=\"token punctuation\">{</span>\n  parse <span class=\"token function\">str</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"increment\"</span><span class=\"token punctuation\">)</span>\n  parse whitespaces\n  parse <span class=\"token function\">str</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"item\"</span><span class=\"token punctuation\">)</span>\n  parse whitespaces\n  <span class=\"token keyword\">const</span> itemId <span class=\"token operator\">=</span> parse integer\n  parse whitespaces\n  parse <span class=\"token function\">str</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"by\"</span><span class=\"token punctuation\">)</span>\n  parse whitespaces\n  <span class=\"token keyword\">const</span> by <span class=\"token operator\">=</span> parse integer\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    tag<span class=\"token operator\">:</span> <span class=\"token string\">\"Increment\"</span><span class=\"token punctuation\">,</span>\n    itemId<span class=\"token operator\">:</span> itemId<span class=\"token punctuation\">,</span>\n    by<span class=\"token operator\">:</span> by\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p><em>Now</em> this is starting to look really nice. In fact, if we were working in Haskell, we'd be able to do something like this for free using its <code>do</code> notation, which produces some incredibly elegant code.</p>\n<p>As for parsing the rest of the protocol, I'll leave that as an exercise to the reader in our pseudo-JavaScript language 😃</p>\n<h1>No Game...? 😵‍💫</h1>\n<p>You might be wondering where the monads are still. Well, we've been working with them the entire time!</p>\n<p>I'll give a formal definition of a monad and you, dear reader, should take this opportunity to go back to the previous examples and see how it applies. Afterwards, we'll have a little more discussion on monads before wrapping it up.</p>\n<p>A monad is a generic type <code>Monad&lt;A&gt;</code> supporting the following operation:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">Bind<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">B</span><span class=\"token operator\">></span></span> <span class=\"token operator\">=</span>\n  <span class=\"token punctuation\">(</span>ma<span class=\"token operator\">:</span> Monad<span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span><span class=\"token punctuation\">,</span> <span class=\"token function-variable function\">f</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>a<span class=\"token operator\">:</span> <span class=\"token constant\">A</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Monad<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Monad<span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span></code></pre>\n<p>Technically, if we're following <a href=\"https://hackage.haskell.org/package/base-4.21.0.0/docs/Control-Monad.html\">Haskell's definition of a monad</a>, there needs to be a little bit more, and the operations should satisfy <a href=\"https://wiki.haskell.org/Monad_laws\">certain monad laws</a>, but we won't complicate things in this post.</p>\n<p>So in our case, <code>MaybeExpanded</code>, <code>Logged</code>, and <code>Parser</code> all formed monads with their respective <code>flatMap</code>, <code>upgrade</code>, and <code>chain</code> operations (modulo classes implicitly inserting the <code>this</code> argument and modulo operation order).</p>\n<p>This probably feels incredibly unsatisfying. We picked some seemingly unrelated property that happened to be common to each example and called it a monad. I mean, we very well could have defined a &quot;SuperMonad&quot; to be a generic type that has the letter &quot;e&quot; in its name, and all of the examples would trivially have formed SuperMonads. How is this even helpful?</p>\n<p>Monads <em>are</em> a very abstract concept, and it's really hard to pin down what ties all monads together without losing generality or being too abstract. However, like I hinted at before, one way to understand monads is through the &quot;background&quot; and &quot;foreground&quot; idea: <em>the <code>Bind</code> operation is precisely where we put all the &quot;background&quot; work, allowing us to focus on the &quot;foreground&quot; work &quot;inside&quot; the monad</em>, operating on the <code>A</code> in <code>Monad&lt;A&gt;</code>.</p>\n<p>Another way to think of it may be clearer if we translate our examples into pseudo-Haskell using its <code>do</code> notation. Don't worry if you don't understand Haskell, we're mostly focusing on the overall shape of the code, not the details, and I'll modify some syntax to feel more familiar to JavaScript developers.</p>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token comment\">-- MaybeExpanded</span>\n<span class=\"token hvariable\">getSubscriptionPayer</span> <span class=\"token operator\">::</span>\n  <span class=\"token constant\">Subscription</span> <span class=\"token operator\">-></span>\n  <span class=\"token constant\">MaybeExpanded</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">getSubscriptionPayer</span> <span class=\"token hvariable\">subscription</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token hvariable\">latestInvoice</span> <span class=\"token operator\">&lt;-</span> <span class=\"token keyword\">of</span> <span class=\"token hvariable\">subscription</span><span class=\"token punctuation\">.</span><span class=\"token hvariable\">latestInvoice</span>\n  <span class=\"token hvariable\">paymentIntent</span> <span class=\"token operator\">&lt;-</span> <span class=\"token keyword\">of</span> <span class=\"token hvariable\">latestInvoice</span><span class=\"token punctuation\">.</span><span class=\"token hvariable\">paymentIntent</span>\n  <span class=\"token hvariable\">paymentMethod</span> <span class=\"token operator\">&lt;-</span> <span class=\"token keyword\">of</span> <span class=\"token hvariable\">paymentIntent</span><span class=\"token punctuation\">.</span><span class=\"token hvariable\">paymentMethod</span>\n  <span class=\"token builtin\">return</span> <span class=\"token hvariable\">paymentMethod</span><span class=\"token punctuation\">.</span><span class=\"token hvariable\">billingDetails</span><span class=\"token punctuation\">.</span><span class=\"token hvariable\">name</span></code></pre>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token comment\">-- Logged</span>\n<span class=\"token hvariable\">upgrade</span> <span class=\"token operator\">::</span>\n  <span class=\"token constant\">String</span> <span class=\"token operator\">-></span>\n  <span class=\"token punctuation\">(</span><span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token hvariable\">b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span>\n  <span class=\"token hvariable\">a</span> <span class=\"token operator\">-></span> <span class=\"token constant\">Logged</span> <span class=\"token hvariable\">b</span>\n<span class=\"token hvariable\">upgrade</span> <span class=\"token hvariable\">msg</span> <span class=\"token hvariable\">f</span> <span class=\"token hvariable\">a</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token hvariable\">tell</span> <span class=\"token hvariable\">msg</span>\n  <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token hvariable\">f</span> <span class=\"token hvariable\">a</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token hvariable\">double'</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Logged</span> <span class=\"token constant\">Int</span>\n<span class=\"token hvariable\">double'</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">upgrade</span> <span class=\"token string\">\"double\"</span> <span class=\"token hvariable\">double</span>\n\n<span class=\"token hvariable\">square'</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Logged</span> <span class=\"token constant\">Int</span>\n<span class=\"token hvariable\">square'</span> <span class=\"token operator\">=</span> <span class=\"token hvariable\">upgrade</span> <span class=\"token string\">\"square\"</span> <span class=\"token hvariable\">square</span>\n\n<span class=\"token hvariable\">compute</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Logged</span> <span class=\"token constant\">Int</span>\n<span class=\"token hvariable\">compute</span> <span class=\"token hvariable\">x</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token hvariable\">tell</span> <span class=\"token string\">\"compute\"</span>\n  <span class=\"token hvariable\">double'</span> <span class=\"token hvariable\">x</span> <span class=\"token operator\">>>=</span> <span class=\"token hvariable\">square'</span> <span class=\"token operator\">>>=</span> <span class=\"token hvariable\">double'</span></code></pre>\n<pre class=\"language-haskell\"><code class=\"language-haskell\"><span class=\"token comment\">-- Parser</span>\n<span class=\"token hvariable\">string</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n<span class=\"token hvariable\">integer</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">Int</span>\n<span class=\"token hvariable\">whitespaces</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">String</span>\n\n<span class=\"token hvariable\">cartActionIncrement</span> <span class=\"token operator\">::</span> <span class=\"token constant\">Parser</span> <span class=\"token constant\">CartAction</span>\n<span class=\"token hvariable\">cartActionIncrement</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">do</span>\n  <span class=\"token hvariable\">str</span> <span class=\"token string\">\"increment\"</span>\n  <span class=\"token hvariable\">whitespaces</span>\n  <span class=\"token hvariable\">str</span> <span class=\"token string\">\"item\"</span>\n  <span class=\"token hvariable\">whitespaces</span>\n  <span class=\"token hvariable\">itemId</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">integer</span>\n  <span class=\"token hvariable\">whitespaces</span>\n  <span class=\"token hvariable\">str</span> <span class=\"token string\">\"by\"</span>\n  <span class=\"token hvariable\">whitespaces</span>\n  <span class=\"token hvariable\">by</span> <span class=\"token operator\">&lt;-</span> <span class=\"token hvariable\">integer</span>\n  <span class=\"token builtin\">return</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">Increment</span> <span class=\"token hvariable\">itemId</span> <span class=\"token hvariable\">by</span><span class=\"token punctuation\">)</span></code></pre>\n<p>The point of writing it this way is to <em>really</em> make the foreground pop out. Within the <code>do</code> block, we forget that when we &quot;bind&quot; <code>by</code> using <code>integer</code>, what we're doing under the hood is actually parsing a string for digits and if we fail, then we short circuit the rest of the computation.</p>\n<p>Put another way, <em>within the context of a monad, we get to focus our attention on the important &quot;foreground&quot; values inside the monad, letting the monadic <code>Bind</code> operation take care of the underlying nasty &quot;background&quot; work</em>. This lets us do magical things like saying <code>itemId &lt;- integer</code>. <code>integer</code> has the type <code>Parser Int</code>, but within the monadic context, we get to &quot;penetrate&quot; into the monad and pretend as if we're with the underlying <code>Int</code>.</p>\n<p>For example,</p>\n<ul>\n<li>Within the context of values maybe being expandable, getting the payer from a subscription is getting the <code>subscription.latestInvoice</code>, and then getting the <code>latestInvoice.paymentIntent</code>, and then...</li>\n<li>Within the context of logging, my computation is telling the log the function that the computation is being run, and then running each function, which each tells the log a message.</li>\n<li>Within the context of parsing, a <code>cartActionIncrement</code> is a <code>str &quot;increment&quot;</code> and then some <code>whitespaces</code>, and then a <code>str &quot;item&quot;</code> and then an <code>integer</code> that we &quot;bind&quot; to <code>itemId</code>, and then...</li>\n</ul>\n<p>I can sympathize with concerns the imperative programmer may be feeling right now. &quot;I want to know exactly what my code is doing! These darn monads are obscuring all the work!&quot;, or &quot;This is so confusing! You people are just trying to make things complicated on purpose! I just want to go back to my good ol' clear, imperative code!&quot;</p>\n<p>Hush, hush. Yes, to some degree, the &quot;darn monads&quot; <em>are</em> obscuring the work, and this <em>is</em> confusing at first sight.</p>\n<p>However, the work that's being obscured is well-defined and can easily be translated by essentially placing the contents of the monadic <code>Bind</code> operation between every line of code. Is that so desirable? If so, I can make no further of an aesthetic argument for my case.</p>\n<p>Regarding the code looking confusing, it totally can seem that way. However, this is <em>not</em> a good reason against learning or adopting monads. It can take some work to speak the language of monads, but the payoff is well worth it, in my humble opinion.</p>\n<p>In general, that sort of mindset is horribly defeatist. Imagine if you made a similar argument when learning how to program for the first time: &quot;This is so confusing! What are these 'variables' and 'functions'? I just want to go back to speaking good ol' clear, natural language!&quot;. (Funnily enough, some programmers seem to actually believe this these days by overusing LLMs.) Or imagine if you applied this mindset to learning a new subject in general: &quot;Protons? Neutrons? Electrons? Screw that, I'm only going to keep working with my atoms!&quot; But I digress.</p>\n<h1>Hidden In Plain Sight 🥸</h1>\n<p>I want to leave you, the JavaScript developer, with something surprising to think about. Remember how I earlier talked about how JavaScript solved their &quot;pyramid of doom&quot; problem with <code>async/await</code> on <code>Promise</code>s? It's a bit of a coincidence that <code>async/await</code> ended up with pretty similar syntax to Haskell's <code>do</code> notation with monads in general, isn't it? It's almost as if...</p>\n<p><strong><code>Promise&lt;A&gt;</code> was a monad all along?! 😱</strong></p>\n<p>That's right. <code>Promise.then</code> has an (implicit) signature of:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\">then<span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>pa<span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">A</span><span class=\"token operator\">></span><span class=\"token punctuation\">,</span> <span class=\"token function-variable function\">f</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>a<span class=\"token operator\">:</span> <span class=\"token constant\">A</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">B</span><span class=\"token operator\">></span></code></pre>\n<p>This is exactly the signature for the monadic <code>Bind</code> operation we required. (Well, <code>Promise.then</code> is overloaded with other signatures, and again, there's a bit more monads formally require that <code>Promise</code>s do satisfy but we haven't proven, but bah!)</p>\n<p>We can now recontextualize all our <code>async/await</code> code as &quot;working within the monadic context of <code>Promise</code>s&quot;, or a way to &quot;ignore the background work&quot; and &quot;focusing on the foreground work&quot;. In fact, this is literally true; there's a lot of work going on behind <code>Promise</code>s that's put into the background through the <code>Promise</code> monad.</p>\n<p>There's another common monad in vanilla JavaScript people use regularly, probably without realizing it's a monad. This one requires you to bend your mind a little bit to appreciate its monadic structure, which is why I haven't talked about it. I'll leave it as an exercise for the reader to figure out what it is.</p>\n<h1>Monads, Schmonads 😒</h1>\n<p>This was a bit of a long read, but I'm glad you made it all the way to the end. Hopefully I've convinced you that monads aren't so scary.</p>\n<p>Really, <em>they're just a programming pattern to separate the background from the foreground</em>.</p>\n<p>Put another way, <em>working in a monadic context lets you focus on the values within the monad – the</em> what_,_ <em>while letting the monad handle the</em> how <em>with its <code>Bind</code> operation.</em></p>\n<p>So really, monads are nothing special. The fact that the average JavaScript developer has worked with monads their entire JavaScript career without realizing it shows that monads can be understood by anyone and the mystique around monads is really fueled by a self-fulfilling prophecy.</p>\n<p>Well, kind of. There is certainly more to monads and category theory that I didn't mention. Rather than an end-all-be-all post on monads, this post should serve as a jumping board to dive into the wonderful wacky world of functors, monads, monoids, and category theory in general.</p>\n<p>I'll leave some further resources for wanting more:</p>\n<ul>\n<li>Bartosz Milewski's &quot;<a href=\"https://github.com/hmemcpy/milewski-ctfp-pdf\">Category Theory for Programmers</a>&quot;</li>\n<li><a href=\"https://www.youtube.com/playlist?list=PLbgaMIhjbmEnaH_LTkxLI7FMa2HsnawM_\">An accompanying lecture series</a> also by Bartosz Milewski</li>\n<li>HaskellWiki's <a href=\"https://wiki.haskell.org/All_About_Monads\">All About Monads</a> article</li>\n<li>Another take on the monadic pattern, &quot;<a href=\"https://youtu.be/VgA4wCaxp-Q?si=df62gV4lSlDwc0m_\">What is a monad? (Design Pattern)</a>&quot; by Byte of Code</li>\n<li>Yet another take on the monadic pattern, &quot;<a href=\"https://youtu.be/C2w45qRc3aU?si=NsK2TJUMAVq8i2t3\">The Absolute Best Intro to Monads For Software Engineers</a>&quot; by Studying With Alex</li>\n</ul>\n<p>If you're wondering if you should read more mathematically-focused books on category like Saunder MacLane's classic &quot;<a href=\"https://link.springer.com/book/10.1007/978-1-4757-4721-8\">Categories for the Working Mathematician</a>&quot;, or Emily Riehl's &quot;<a href=\"https://www.amazon.ca/Category-Theory-Context-Emily-Riehl/dp/048680903X\">Category Theory in Context</a>&quot;, the answer is <strong>NO!</strong><em>.</em> These aren't bad books by any means, but these are geared for the mathematician and are simply not suitable for the programmer wanting to figure out how to apply category theory to their programming.</p>\n<blockquote>\n<p>The recognition of the coming and going of things is a first step in training and practice.<br>\n– Dogen</p>\n</blockquote>\n",
      "date_published": "2025-05-13T04:16:59Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/homelab-tour-2025/",
      "url": "https://blog.mnke.org/homelab-tour-2025/",
      "title": "Homelab Tour (2025)",
      "content_html": "<h1>Preface</h1>\n<p>I ran into a lot of trouble figuring out how to write this post. On one hand, I plan to use this post to demonstrate some of the skills and techniques I've accrued over years of homelabbing, especially to recruiters or managers. On the other hand, I want to write faithfully to my usual rambling and colorful writing style for the small group of friends that may read my posts – and to myself, who truly is the primary intended audience of these posts.</p>\n<p>In particular, writing this post has made me realize how far I've developed my skills in systems administration, system infrastructure design, DevOps, etc. I really wanted to reflect with great sentiment on my long journey: where I started and the arduous paths I took. I wanted to pay respects to everything that lead me to where I am today.</p>\n<p>After much deliberation, I have decided to reserve such sentiments for a separate post. I want to dedicate this post to being as technical and straight-to-the-point as possible, as if I were writing a project specification or documentation on my systems for future generations to maintain. That being said, I will not stop to explain each and every concept. To do so would be to document my history in homelabbing, systems design, networking, etc., which is much outside the scope of this post. I will assume the reader to be already familiar in a wide variety of areas so that I can cover as much ground as possible.</p>\n<p>In addition, for those unfamiliar with my writing style, I annotate sections of text that are intended as asides to provide additional context or exist simply for flavor with the word <em>Chaff</em>, alluding to the popular idiom, &quot;<a href=\"https://www.merriam-webster.com/dictionary/separate%20the%20wheat%20from%20the%20chaff\">to separate the wheat and chaff</a>&quot;. This is inspired from Leary and Kristiansen's <a href=\"https://milneopentextbooks.org/a-friendly-introduction-to-mathematical-logic/\">A Friendly Introduction to Mathematical Logic (2015)</a> textbook, where they use the same label in a similar fashion.</p>\n<p>Ultimately, I hope to appeal to the recruiters or technical representatives or perhaps even novice homelabbers that will read this post. In this post, I hope to demonstrate and transfer much knowledge to you, dear reader.</p>\n<h1>Hardware</h1>\n<p>My primary Proxmox virtualization host is a Dell Poweredge R730XD, with a 48-core Intel Xeon 4116 processor running at 2.10GHz over 2 sockets with 256GB of RAM and ~10TB of logical storage onboard, connected to a Perc H730P adapter on RAID6, all encased in the 2U chassis the R730XD comes in.</p>\n<p>I also have a separate TrueNAS machine with a Intel Xeon E5-2650 processor running at 2.60GHz with 64GB of RAM and ~40TB of logical storage using a M5015 RAID card flashed into HBA mode, connected to a JBOD enclosure, hosting 15 hard drives. This machine additionally has a GTX 1650 connected to it for transcoding media and a 4-port 1Gb/s NIC.</p>\n<p>Connecting these two machines is a HP Procurve 2900 switch. While this topic exceeds the present discussion on hardware, I find nowhere more appropriate than here to mention that both the Proxmox host and the TrueNAS host are network-bonded (Linux bond) to the switch in LACP (802.3ad link aggregation) mode, for a total bandwidth of 4Gb/s – though unfortunately still limited to 1Gb/s per TCP connection due to the limitations of LACP bottlenecking each TCP connection to the speed of a physical link.</p>\n<h1>TrueNAS</h1>\n<p>Starting with the simpler TrueNAS host, this machine (mostly) acts as a dumb storage controller for my Proxmox host to store large files and for cold storage (e.g. filesystem backups of my old machines). Historical factors have lead me to creating my ZFS pool with 2 data vdevs in RAIDZ2 with differing capacities: one with 6 physical disks, and one with 9 physical disks. While obviously not optimal, this was set up in a time when I was less familiar with ZFS. I have enough data in this pool at this point that it would be impractical to re-optimize in-place. I hope to build an SSD storage array in the future and migrate my data off this pool in that time so I can re-balance the vdevs.</p>\n<p>You may be wondering why this TrueNAS host isn't also running Proxmox, so that I can form a cluster with the other Proxmox host. This, too, is a matter of historical contingency rather than a carefully-planned decision. While I knew I could pass through necessary PCI slots on QEMU+KVM so that a TrueNAS VM had direct access to the HBA controller (probably, assuming my IOMMU groups were valid), I thought that TrueNAS would be much heavier than it actually was and needed the full power of the host, given that I had previously run TrueNAS on a severely bottlenecked machine. This turned out to be very incorrect, and I am now unfortunately stuck with TrueNAS instance that does basically nothing else except export storage shares to the network, wasting a ton of resources.</p>\n<p>This isn't entirely true though, because I ended up running another VM in TrueNAS and passing through a spare GTX 1650 I had. Given that my media was stored on this host and that I wasn't making full use of its power, I decided to run my Jellyfin server in a VM under TrueNAS for lower latency to the block storage (well kind of, the VM still unfortunately has to connect to the media ZFS dataset on the TrueNAS host through a network share, bleh!) as well as being able to easily access another x16 PCIE slot for transcoding. I've pinned about half of the CPUs to the VM according to the L1 cache topology to further optimize the VM.</p>\n<p>Given that I was still far from making full use of the TrueNAS host, I also put some databases/storage on it through TrueNAS Scale's built-in applications: MySQL, Postgres, Redis, Minio. These act as general-purpose databases that I use in other applications I deploy. In fact, this Ghost deployment is backed by a MySQL database on the TrueNAS host.</p>\n<p>While I could deploy more applications on the TrueNAS host, I've decided to limit it to just these. Deploying applications on the TrueNAS host – especially those not officially supported – feels fairly finnicky and I dislike the fact that these applications are not stored as IaC (infrastructure as code). There are just better options for deployments, which we'll touch on later. I only chose the TrueNAS host for the database deployments because it had direct access to the fast ZFS pool and I was fine with having my databases outside of IaC.</p>\n<h1>Proxmox</h1>\n<p>This is where the bulk of my infrastructure lies. Almost all of my configuration can be found in my <a href=\"https://git.mnke.org/tony/homelab\">Gitea repo</a>. To offer original content, this post will summarize the configuration, describe the configuration on a higher-level, as well as document the configuration missing from the repo.</p>\n<p>I will first describe my infrastructure in the rough before explaining how it's managed.</p>\n<h2><em>DNS Server – Technitium</em></h2>\n<p>Before a large migration carried out a few months ago, I used <a href=\"https://pi-hole.net/\">PiHole</a> as my primary internal DNS server, allowing me to split DNS and allowing SNI for private, local services. During the migration, I decided to upgrade to a different DNS server, <a href=\"https://technitium.com/dns/\">Technitium</a>, which offered much more powerful DNS management capabilities.</p>\n<p>I couldn't quite abandon my PiHole immediately though, because it contained a ton of DNS records that would be cumbersome to import and I had hard-coded my DNS server IP in multiple places. I had simply never imagined the DNS server IP ever changing. At the time, I also didn't know much about <a href=\"https://en.wikipedia.org/wiki/Virtual_IP_address\">virtual IPs</a>, so that was out of the question. If I shut down the PiHole, I didn't know what services would go down.</p>\n<p>Instead, I created my new Technitium server, set my PiHole as a forwarding server to maintain backwards compatibility with my old records, and changed the PiHole server IP to the Technitium in wherever I could find.</p>\n<p>Even so, I'm somewhat paranoid to fully destroy the PiHole, and I don't want to reconfigure all the blocklists, so the PiHole still sits alive to this day, as a companion to the Technitium DNS server. The DNS server(s) sit as a fundamental pillar of my internal networking stack.</p>\n<h2><em>Jumper</em></h2>\n<p>I have a VM running Docker standalone that runs basic workloads, workloads that require fast, persistent storage – particularly with high random R/W like databases – or workloads that other infrastructure softly depend on, like my <a href=\"https://about.gitea.com/\">Gitea</a> server that I use for GitOps. I call it <em>Jumper</em> because it acts to &quot;jumpstart&quot; the rest of my infrastructure without the rest of my infrastructure strictly relying on it to run. Apart from the Gitea server, I run <a href=\"https://www.portainer.io/\">Portainer</a> for GitOps and I run my media stack on <em>Jumper</em> because the SQLite database some of the applications in the media stack use are best bound to the locally-attached disk rather than a remote share.</p>\n<h2><em>Stingray</em></h2>\n<p><em>Stingray</em> is the name I give to my Docker swarm, with a 3-node manager and 3-node worker topology.</p>\n<details>\n<summary><strong><i><em>Chaff</em></i><span>: Why </span><i><em>Stingray</em></i><span>?</span></strong></summary>\n<p>I don't have a particular reason for this naming, unlike my reasons for <strong>Jumper</strong>. Really, I just wanted to avoid naming things exactly for what they are because I've found such a naming scheme to be surprisingly confusing. For example, my previous Docker swarm was just named <strong>Swarm</strong>, with VMs in the swarm being named <code>swarm-master-1</code>, <code>swarm-slave-1</code>, etc. The problem arises if I want to introduce another Docker swarm. What would the naming convention be? Something like <code>swarm-2-&lt;role&gt;-&lt;n&gt;</code>? I find it harder to remember what <strong>Swarm-2</strong> does, as opposed to just <strong>Swarm</strong> or <strong>Swarm-1</strong>. Instead, I find it much easier to remember what things do when I give them proper names, like <strong>Jeremy</strong>, or <strong>Joel</strong>. Sure, someone new to these resources may find the naming scheme confusing at first, and wouldn't be able to tell what these resources are, but I find this tradeoff more than worth it.</p>\n</details>\n<p><em>Stingray</em> is in a bit of a strange spot. It's doomed to its fate as a Docker swarm, being an awkward middleground between a single-node Docker instance and a full-blown Kubernetes cluster. Currently, <em>Stingray</em> runs workloads that don't require fast, persistent storage. Notably, it runs a phpMyAdmin and pgAdmin instance as a UI to my databases, and <a href=\"https://infisical.com/\">Infisical</a> as a secrets management replacement to <a href=\"https://www.hashicorp.com/en/products/vault\">Hashicorp Vault</a>. It does run some other workloads like <a href=\"https://www.portainer.io/\">Portainer</a>, <a href=\"https://doc.traefik.io/traefik/\">Traefik</a>, <a href=\"https://www.truenas.com/truecommand/\">TrueCommand</a> and small apps I write, but nothing really interesting.</p>\n<details>\n<summary><strong><i><em>Chaff</em></i><span>: Why is </span><i><em>Stingray</em></i><span> so useless?</span></strong></summary>\n<p>I originally wanted <strong>Stingray</strong> to act as my <strong>Jumper</strong>: a lightweight resource that could help jumpstart the rest of my infrastructure as well as run general workloads that aren't suitable for Kubernetes, like my media stack. Unfortunately, <strong>Stingray</strong> became heavier than I thought, and Docker swarm still has no great CSI (container storage interface), even after all these years, making it difficult to justify putting persistent, IO-heavy workloads on it. Yes, I've tried GlusterFS. Yes, I've tried storage plugins. For different reasons, these solutions are simply not as convenient as running the workload on the single-node Docker instance or using Kubernetes' CSI to allocate storage.</p>\n<p>I believe the fact that <strong>Stingray</strong> is in such an awkward spot really speaks to the state of Docker swarm as a feature. It seems like not many people want to keep improving Docker swarm when Kubernetes exists as a better large-scale solution and when a single-node Docker instance exists as a better small-scale solution, making Docker swarm forever doomed to be left in the dust by both parties.</p>\n</details>\n<h2><em>Moirai</em></h2>\n<p><em>Moirai</em> (pronounced &quot;mee-reh&quot;) is the name given to a group of nodes used in my <a href=\"https://pterodactyl.io/\">Pterodactyl</a> cluster. The Pterodactyl cluster could have an entire blog post dedicated to it, so I won't go into too much detail here. Put simply, I have a Pterodactyl panel container running on <em>Jumper</em> that interfaces with agents installed on the <em>Moirai</em> nodes to manage game servers like Minecraft, Don't Starve Together, Project Zomboid, etc. The Pterodactyl panel allows me to securely give access to some of my computing resources to friends who want to manage game servers. If you're curious on how these <em>Moirai</em> nodes are accessible via the internet, see <a href=\"https://blog.mnke.org/no-compromises-how-i-exposed-services-securely-through-a-secondary-router/\">my other post</a>.</p>\n<h2><em>Dolo</em></h2>\n<p><em>Dolo</em> is my premier resource, a (currently) 9-node k3s cluster, including my 3-node HA control plane. It runs with a <a href=\"https://github.com/flannel-io/flannel\">flannel</a> CNI, <a href=\"https://kube-vip.io/\">kube-vip</a> in ARP mode for the HA control plane, and <a href=\"https://metallb.io/\">MetalLB</a> on L2 mode for LoadBalancer support. This has been made easily automate-able through <a href=\"https://github.com/techno-tim/k3s-ansible\">Techno Tim's k3s Ansible playbook</a>.</p>\n<p><em>Dolo</em> runs all of my other workloads, including, but not limited to:</p>\n<ul>\n<li><a href=\"https://fluxcd.io/\">FluxCD</a> for GitOps (more on this later)</li>\n<li><a href=\"https://prometheus.io/docs/introduction/overview/\">Prometheus</a>, <a href=\"https://grafana.com/docs/loki/latest/\">Loki</a>, <a href=\"https://grafana.com/\">Grafana</a>, <a href=\"https://grafana.com/docs/loki/latest/send-data/promtail/\">Promtail</a> for monitor tooling</li>\n<li><a href=\"https://longhorn.io/\">Longhorn</a>, my primary storage driver</li>\n<li><a href=\"https://external-secrets.io/latest/\">External Secrets</a> to fetch secrets from <a href=\"https://infisical.com/\">Infisical</a></li>\n<li><a href=\"https://cert-manager.io/\">cert-manager</a> for TLS certificates</li>\n<li><a href=\"https://doc.traefik.io/traefik/\">Traefik</a> as my reverse-proxy/load balancer</li>\n<li><a href=\"https://ghost.org/\">Ghost</a>, the CMS platform for blogging (the very thing you're interacting with right now!)</li>\n<li><a href=\"https://nextcloud.com/\">NextCloud</a>, an community-driven, open-source, self-hosted alternative to the Google suite</li>\n<li><a href=\"https://goauthentik.io/\">Authentik</a>, a relatively new identity provider (IdP) like Keycloak</li>\n<li><a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/\">Cloudflared</a>, the tunneling software that connects Cloudflare to my home securely</li>\n<li>...<em>many more</em></li>\n</ul>\n<p>Additionally, <em>Dolo</em> serves as the main entrypoint to all of my HTTP services from the internet. All ingress HTTP traffic passes through Traefik via the cloudflare tunnel and ends up getting logged into Loki, making it easy to audit access. The cloudflare tunnel (and other tunnels) have allowed me to avoid opening any port on my router and very strictly and explicitly control and audit access to my home network. <a href=\"https://blog.mnke.org/no-compromises-how-i-exposed-services-securely-through-a-secondary-router/\">Read about how I exposed non-HTTP services in my other post</a>.</p>\n<p>While I could tirelessly expound on all of these services and their configurations, it would be easier if I just showed them to you. Fortunately, I can do just that, now that I've adopted a GitOps workflow for my homelab. So, instead of going through all of the configuration, I'll leave you to check out the <a href=\"https://git.mnke.org/tony/homelab\">Git repo</a> yourself, while we talk about the structure of the repo as a whole.</p>\n<h1>Git</h1>\n<p>Git is the primary method by which I manage my configuration. This gives me a centralized location to reason about all my infrastructure as well as a way to reproduce it, should anything happen to it.</p>\n<p>Let us dissect the anatomy of the <a href=\"https://git.mnke.org/tony/homelab\">Git repo</a> to gain insight into how this is done.</p>\n<h2>Terraform/OpenTofu</h2>\n<p>The <code>tf</code> directory is where I keep my Terraform (OpenTofu) configuration. That's right, Proxmox can be interfaced with through Terraform: I manage the lifecycle of all of my VMs through the Terraform configuration now. The details of how to get this set up are provided in the <a href=\"https://git.mnke.org/tony/homelab#proxmox\">README</a> and <a href=\"https://blog.andreasm.io/2024/01/15/proxmox-with-opentofu-kubespray-and-kubernetes/\">Andreas' post</a> and I shall not repeat them here. I created Terraform modules for my architecture components, notably the clusterized components like Docker Swarm and Kubernetes. This allows me to scale up and down my infrastructure easily, by simply changing a number and running a script, and even create test clusters if I so choose.</p>\n<details>\n<summary><strong><i><em>Chaff</em></i><span>: GitOps?</span></strong></summary>\n<p>If you are asking whether the Terraform configuration is applied automatically, the answer is no.</p>\n<p>Maybe this is violating some GitOps principle, but like any idea, we should take what we find useful from it. In my case, I find that the most valuable part of GitOps is the versioned, centralized, and declarative management approach to infrastructure, not necessarily the automation part.</p>\n<p>I prefer leaving vital configuration like Terraform infrastructure outside of automation and given that I'm the only one working on this, I won't have any problems with state management.</p>\n</details>\n<details>\n<summary><strong><i><em>Chaff</em></i><span>: OpenTofu?</span></strong></summary>\n<p><a href=\"https://opentofu.org/\">OpenTofu</a> is a FOSS fork of <a href=\"https://www.terraform.io/\">Terraform</a> maintained by the Linux Foundation. You can read more on <a href=\"https://opentofu.org/faq/#why-was-opentofu-created\">OpenTofu's FAQ</a> for why they forked Terraform. For the end user, OpenTofu provides a much less restrictive license while doing a great job of remaining compatible with Terraform. As an avid FOSS enjoyer myself, making the move to OpenTofu was obvious.</p>\n<p>That being said, since Terraform is still more &quot;official&quot;, I'll be referring to all the work I do with OpenTofu as &quot;Terraform&quot;. The reader should keep in mind that in actuality, I have completely replaced Terraform with OpenTofu though.</p>\n</details>\n<h2>Ansible</h2>\n<p>While Terraform does a great job of managing the lifecycle of VMs, I use Ansible to provision the VMs in my <code>ansible</code> directory. This connection is made incredibly easy with Terraform's <a href=\"https://registry.terraform.io/providers/ansible/ansible/latest\">Ansible provider</a> as well as <a href=\"https://github.com/ansible-collections/cloud.terraform\">Ansible's Terraform inventory plugin</a>. Simply put, I can define a VM resource and add it to an Ansible group via the Ansible provider in Terraform, and then use Ansible's Terraform inventory plugin to generate an inventory dynamically based on the Terraform resources.</p>\n<p>Some of the Ansible roles I employ are ones to set up a k3s cluster and set up a Docker swarm. I try to keep Ansible automation to a minimum, preferring to use a GitOps-based approach for as much as I can, so most of my Ansible playbooks are simply for initial provisioning of VMs.</p>\n<h2>DNS Servers</h2>\n<p>The <code>dns</code> directory is really simple. I manually version control the records by saving them to the Git repo and committing whenever I make a change on the Technitium server.</p>\n<details>\n<summary><strong><i><em>Chaff</em></i><span>: bUt thAt's NoT VerY gITOps Of yOU!</span></strong></summary>\n<p>I thought about using a Terraform provider to apply DNS configuration, using some scripts to automatically synchronize DNS configuration to and from Technitium, or using something like <a href=\"https://www.isc.org/bind/\">bind9</a> as my DNS server, which works with <code>.zone</code> files directly to make this easier, but I ultimately decided against it. I definitely could not use a Terraform provider to apply DNS configuration in the same Terraform project, because I'd end up creating a circular dependency. Scripts to synchronize the DNS configuration seemed overkill.</p>\n</details>\n<h2>Docker</h2>\n<p>The <code>docker</code> directory in my repository simply contains a repository of Docker compose files (for a standalone Docker instance) or Docker stacks (for a Docker swarm). Portainer uses these manifests to keep my configuration up-to-date, following a GitOps approach. Not all of the manifests are used, but I let them remain in case I ever want to bring them back up in Portainer.</p>\n<p>My current workflow deploying applications is admittedly a bit awkward:</p>\n<ol>\n<li>I set my <a href=\"https://docs.docker.com/engine/manage-resources/contexts/\">Docker context</a> to my standalone Docker instance or the Docker swarm</li>\n<li>Write a compose file, leaving configuration options into a <code>.env</code> file</li>\n<li>Deploy the compose file and make modifications as needed</li>\n<li>Once I see that it's working, I destroy the application</li>\n<li>Commit and push</li>\n<li>On Portainer, re-deploy the application through Git</li>\n</ol>\n<details>\n<summary><strong><i><em>Chaff</em></i><span>:</span> <span>GitOps Woes</span></strong></summary>\n<p>If I chose to go with the &quot;pure&quot; GitOps approach and commit a potentially broken initial compose file and iterate fixing over commits, I would be left with a really ugly commit history unless I did some squashing.</p>\n<p>I've read from some sources that the solution is to just thoroughly test the configuration, use things like pre-commit hooks or some sort of validation before committing, but the same fundamental problem remains in that I can't actually know if some configuration works until I actually try applying it.</p>\n<p>The way I approach this currently is to iteratively work on the configuration (perhaps on a different environment initially) and deploy it outside of GitOps to ensure it works before committing it and letting it be managed by GitOps. In the case of Portainer, I have to have the additional step of first deleting the configuration because Portainer doesn't know how to manage stacks deployed outside of Portainer.</p>\n</details>\n<h2>k8s</h2>\n<p>Last, but certainly not least, we have the <code>k8s</code> directory where I keep my Kubernetes manifests. As mentioned earlier, I use <a href=\"https://fluxcd.io/\">FluxCD</a> as my GitOps tool for my Kubernetes cluster. The reason why I chose FluxCD and not another GitOps tool like <a href=\"https://argoproj.github.io/cd/\">ArgoCD</a> is frankly because FluxCD was a lot easier to set up, learn about, and FluxCD seemed a lot more lightweight than ArgoCD.</p>\n<p>The workflow I employ to manage my cluster is now:</p>\n<ol>\n<li>Test out changes locally and possibly apply some manifests to ensure they work</li>\n<li>Make sure these manifests are referenced by FluxCD</li>\n<li>Commit and push</li>\n<li>FluxCD receives a webhook notification, sends out alerts to notification channels, and starts reconciling the differences</li>\n<li>Manifests are now version-controlled</li>\n</ol>\n<h1>Wrap-up</h1>\n<p>Admittedly, there is still much detail of my homelab left out of the tight confines of this post. I haven't talked about my snapshotting and backup strategy, ZFS dataset layout, subnet configuration, etc. Unfortunately, covering the ground fully is impossible in a single blog post, in part due to the legitimate complexity of the years-old homelab, and in part due to the unrelenting passion of the homelabber, possessing them to proudly overindulge in their lengthy explanations. Blame them not though, for how could one similarly blame a child when they present a realized product of their imagination, excitedly babbling senselessly about it? But I digress.</p>\n<p>I hope to have presented a small taste of my homelabbing experience: the technologies I've come into contact with, some skills I employ, etc. As I have incessantly reminded the reader throughout this post, much has been left out – certainly all the pain and suffering learning and debugging, despite this process being arguably the most valuable. If you're curious about certain aspects of the homelab – whether you're a representative seeking to ascertain my ability or an aspiring homelabber to understand it for yourself – I encourage you to ask.</p>\n<p>Happy hacking!</p>\n",
      "date_published": "2025-03-20T01:31:54Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/no-compromises-how-i-exposed-services-securely-through-a-secondary-router/",
      "url": "https://blog.mnke.org/no-compromises-how-i-exposed-services-securely-through-a-secondary-router/",
      "title": "No compromises: How I exposed services securely through a secondary router",
      "content_html": "<h1>Background</h1>\n<p>💡</p>\n<p>Do you not care about my background, motivation, stories, etc.? Fine, skip to the <a href=\"https://blog.mnke.org/no-compromises-how-i-exposed-services-securely-through-a-secondary-router/#diving-in\">Diving In</a> section, you heartless monster.</p>\n<p>For the better part of the past month, I've been working on migrating my homelab infrastructure to be much more scalable, maintainable, secure, and fault-resistant (look forward to a post on this once the migration is fully complete). One of the security goals I set for this migration was to forward zero ports on my home router.</p>\n<p>In the past, I didn't have any problem with opening a few ports on my router for things like HTTP/HTTPS. I only posted my domain (pointing to my router) on trusted or private places, like posting a blog post in my Discord server, or advertising my blog on resumes I'd only hand out privately.</p>\n<p>Eventually, the services I wanted to expose changed, most notably with a public-facing media server I host locally. Although I only advertised it in my Discord server, the nature of the service meant that it was more likely to be shared around and more susceptible to landing in the hands of malicious actors, who could easily do a <code>dig</code> to find my home IP.</p>\n<p>This is when I decided to switch to Cloudflare. I had already been on the hunt for a new domain registrar for the past while because I wanted wildcard certificates and I somehow came under the impression that Namecheap, the registrar I used at the time, didn't support wildcard certificates (they do, I was just dumb). So I came across Cloudflare, which offered a better price for my domain than Namecheap <em>and</em> offered a free HTTP proxying service on top of that!</p>\n<p>Well, color me impressed. I switched right away and posted my domain around much more freely, now that my mind was freed about securing my IP.</p>\n<h1>Not quite perfect</h1>\n<p>I had been using Cloudflare for a little while at this point when I decided to host a Minecraft server for my friends. As I was testing connectivity, I realized that the Cloudflare proxy wouldn't work for Minecraft. After all, Cloudflare proxies based on some hostname identification mechanism, and presumably, Minecraft didn't present hostnames, or at least Cloudflare didn't support Minecraft's hostname identification. At the time, I didn't really know of any solutions other than being forced to open a router port and disabling the proxy on a subdomain, which is what I ended up doing.</p>\n<p>This has been a thorn in my side for the longest time. Now that my domain was much more widely accessible, there would be a lot more potential malicious actors being able to find my IP. While I did limit open ports on my router as much as I could and realistically, there aren't gonna be that many people targeting me with advanced enough tooling to compromise my network or otherwise cause harm, I knew this was against best practices and it bugged me. But again, I didn't have a good idea of what to do about it.</p>\n<p>A while later, I came across <code>ssh</code>'s port-forwarding mechanism, where you could open a port locally and have <code>ssh</code> open a port remotely for you and forward the traffic over the SSH tunnel. So for example, if you had a Minecraft server listening locally on <code>127.0.0.1:25565</code> and you wanted to open it on a remote port <code>25566</code>, then you could do something like <code>ssh -R 25566:127.0.0.1:25565 user@example.com</code>, allowing you to connect to <code>example.com:25566</code> and forwarding the traffic to you locally.</p>\n<p>💡</p>\n<p>If you didn't know already, there's an accompanying flag to do the reverse: <code>ssh -L &lt;local_port&gt;:&lt;remote_ip&gt;:&lt;remote_port&gt; user@example.com</code>, which will let you connect to <code>&lt;remote_ip&gt;:&lt;remote_port&gt;</code> via <code>localhost:&lt;local_port&gt;</code> through the SSH tunnel.<br>\nThis is a killer command for managing remote servers.</p>\n<p>I was thinking of renting a cheap VPS to forward ports for once I found out about this, but to be frank, I was too lazy and pushed it off until a few months ago, when I felt like my homelab needed a facelift. Given that this feature was so easily available, I wondered if there were any more managed solutions so I didn't have to write system services to establish tunnels myself. Sure enough, I found <a href=\"https://github.com/fatedier/frp\">Fast Reverse Proxy (FRP, for short)</a>, which lets you declaratively do remote port-forwarding.</p>\n<p><em>Chaff:</em> How have I never heard of this? It's such an amazing tool for exposing your services securely, and it even comes with a feature to connect STUN clients for P2P connectivity. I searched for FRP on YouTube and only a few videos with only a couple thousand views covering this. Maybe I ought to cover it myself.</p>\n<p>So I set up a VPS to test around with FRP and sure enough, it really was as amazing as it looked. Except, of course, that FRP won't preserve true client IPs.</p>\n<p>This isn't really FRP's fault. The same &quot;problem&quot; will happen if you do an SSH remote port-forward. In case you don't know what I'm talking about, let's go through an example scenario.</p>\n<ul>\n<li>I have a server running locally on <code>127.0.0.1:25565</code>, which I'm forwarding to my VPS at <code>101.2.3.4:25565</code>.</li>\n<li>Candace, with public IP <code>201.4.5.6</code>, connects to my VPS on port <code>25565</code>.</li>\n<li>FRP/SSH receives packets like <code>src=201.4.5.6:9879, dst=101.2.3.4:25565</code> and pushes them through the tunnel to me.</li>\n<li>The FRP/SSH client I'm running receives the packet and sends a packet to the server, like <code>src=127.0.0.1:7389, dst=127.0.0.1:25565</code>.</li>\n</ul>\n<p>The &quot;problem&quot; is that when the packet reaches my server, the source IP has been NAT'd by FRP/SSH so the server no longer has Candace's public IP, <code>201.4.5.6</code>.</p>\n<p>Most of the time, this doesn't matter. I'd be willing to bet over 90% of game servers (which is my primary use case) would run perfectly fine with the NAT. I can only imagine servers using things like STUN/ICE might run into some problems – and even then, they really shouldn't because STUN/ICE is supposed to solve NAT traversal. The only thing that might be worrying is that a STUN binding with an ICE exchange might end up revealing your public IP over the ICE exchange (realistically, no one's going to be reading ICE exchanges to notice this, of course).</p>\n<p>Additionally, the reason I've been quoting &quot;problem&quot; is because this isn't actually a problem. This is intended behavior. To understand why, we have to think about the return trip of a packet.</p>\n<p>If Candace's public IP were preserved, my local server would send return packets to <code>201.4.5.6:9879</code>, completely bypassing FRP/SSH. A packet like <code>src=127.0.0.1:25565, dst=201.4.5.6:9879</code> would be:</p>\n<ul>\n<li>Routed to my router, with a public IP address of <code>202.6.7.8</code>.</li>\n<li>SNAT'd to something like <code>src=202.6.7.8:4723, dst=201.4.5.6:9879</code>.</li>\n<li>Routed to Candace over the internet.</li>\n</ul>\n<p>This would expose my public IP <code>202.6.7.8</code> to Candace, but more importantly, Candace is expecting a reply from <code>101.2.3.4:25565</code>, my VPS address, <em>not from my public IP.</em> As such, even a TCP handshake wouldn't complete if we preserved Candace's IP through the FRP/SSH tunnel to the server. The FRP/SSH NAT isn't actually a problem because it's what allows reversing the traffic to work at all.</p>\n<h1>No Compromises</h1>\n<p>Even though an FRP/SSH solution would probably have been fine for me, I wanted to do better, for no better reason than to simply satisfy my intellectual urge on finding out if and how preserving source IPs could be done securely. I didn't want NAT'd source IPs or being forced to open ports on my router to receive source IPs.<br>\n<em>I was looking for no compromises.</em><br>\n(Get it? &quot;No compromises&quot; is a double entendre for not reducing my standards <em>and</em> doing it securely so that I wouldn't be compromised. Ha.)</p>\n<p>I'd say my knowledge and practical skills in computer networking has become fairly mature for my age, but one of the things I'd always been afraid of touching was Linux <code>iptables</code>.</p>\n<p>Playing around with networking for many years, I've seen <code>iptables</code> commands here and there on StackOverflow and other forums as I was looking for solutions to networking problems, but I've avoided them if possible because they looked so daunting and it felt like I wouldn't be able to decipher what they did without spending a few days reading the manpages for <code>iptables</code> and its related entries.</p>\n<p>However, I knew enough theory on L2-L4 networking to know what should be possible. In fact, I knew in theory what could be done to meet my requirements. I had hoped that there were existing solutions that didn't involve me messing with <code>iptables</code>, but I couldn't find any. Knowing that <code>iptables</code> was likely the best way for me to turn theory into practice, I took the plunge and dived into <code>iptables</code> documentations for, surprisingly, just a few hours.</p>\n<p>I won't explain <code>iptables</code> in this post. There are plenty of other articles that do a better job than I possibly could. A lot of guides focus on using <code>iptables</code> for firewalling rather than for general-purpose networking, but here are articles that do focus on the latter:</p>\n<ul>\n<li><a href=\"https://sudamtm.medium.com/iptables-a-comprehensive-guide-276b8604eff1\">Sudam Muhagamage's comprehensive guide on iptables</a></li>\n<li><a href=\"https://wiki.archlinux.org/title/Iptables\">Arch Wiki's iptables entry</a></li>\n<li><a href=\"https://homes.di.unimi.it/sisop/qemu/iptables-tutorial.pdf\">A 459-page book on general L3/L4 mechanics by Oskar Andreasson</a> – possibly worth a read if you also need an intro on L3/L4</li>\n</ul>\n<p>Here's another diagram I found on <a href=\"https://serverfault.com/a/1107872\">ServerFault</a> that I find extremely helpful:</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/0kBwxslUrU-480.avif 480w, https://blog.mnke.org/img/0kBwxslUrU-647.avif 647w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/0kBwxslUrU-480.webp 480w, https://blog.mnke.org/img/0kBwxslUrU-647.webp 647w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/0kBwxslUrU-480.jpeg\" alt=\"\" width=\"647\" height=\"1100\" srcset=\"https://blog.mnke.org/img/0kBwxslUrU-480.jpeg 480w, https://blog.mnke.org/img/0kBwxslUrU-647.jpeg 647w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"></picture></p>\n<p>A flowchart of the iptables packet flow</p>\n<h1>Diving in</h1>\n<h2>Requirements</h2>\n<p>With the lengthy preamble out of the way, let's get down to business. First, we define what we want. We should be able to run a server (or servers) in a private home network (e.g. Minecraft) such that:</p>\n<ol>\n<li>Clients from the internet are able to connect to the server – e.g. Billy from across the globe can connect to the Minecraft server</li>\n<li>Extra configuration is not required for clients – e.g. we shouldn't require clients to connect to a VPN server</li>\n<li>We do not expose any ports on the router/we never have to share our home public IP address</li>\n<li>Client public IPs are preserved when reaching the server</li>\n</ol>\n<p>Let's remind ourselves of some of the options that would normally work and why they don't fully meet our requirements.</p>\n<h3>FRP/SSH/Tunneling mechanisms</h3>\n<p>While this meets requirements 1-3, a fundamental feature of this is that they effectively SNAT the client's public IP – which we discussed in detail in the preamble – and violates requirement 4. Without the SNAT, reversing the traffic path would not work.</p>\n<h3>VPN Server</h3>\n<p>This option would require us to set up a VPN server that's connected to the private network and ask clients to activate a VPN whenever they want to connect to our server. This definitely violates requirement 2, and kind of violates requirement 4. While we wouldn't be getting the client's IP as it would be seen by the internet, we could have a VPN topology where the client's VPN IP effectively acts as the client's public IP, for all intents and purposes.</p>\n<p>We might also violate requirement 3, depending where we place the VPN server. If we don't want to violate it, we could place a VPN server out of the private network that relays and acts as a router between peers external to the network and internal to the network.</p>\n<h1>The plan</h1>\n<p>As I mentioned before, I knew in theory what could be done to achieve the requirements. The theory is actually really simple: if you understand how your home router works, you already know the theory.</p>\n<p>The router's role in a typical home network is to connect devices on the private network to the internet through a shared public IP. If we want others to be able to connect to a device in the private network, we port-forward a public port on the router to an address-port pair in the private network. Our requirements dictate that we don't do this because of security concerns, primarily that of exposing the router's public IP.</p>\n<p>But what if we just had another router with disposable public IPs? Really, that's all this solution boils down to. We have a secondary router, Router B, that select devices on the private network connect to that does NAT for us and forwards traffic inbound to the router to devices on the private network. For security, we limit the devices that connect to Router B, limit the inbound traffic Router B forwards, and limit the inbound traffic to Router B in general. Since Router B has a different IP than our primary router, we never end up exposing our primary router's public IP. Additionally, since Router B is disposable, we can easily just toss it away and get a new public IP.</p>\n<p>Sound too good to be true? Well, it kind of is. To illustrate the problems, we need to be more specific.</p>\n<h2>Brass Tacks</h2>\n<p>Suppose Router B has a public IP of <code>172.232.188.40</code> and we have a server that need to be exposed running on <code>10.4.4.32:25565</code> in the local network. The typical round-trip traffic would look like:</p>\n<ul>\n<li>A packet arrives to Router B from the internet: <code>src=80.3.4.5:7890, dst=172.232.188.40:25565</code>.</li>\n<li>Router B DNATs to <code>10.4.4.32:25565</code> and forwards the packet to the local server, sending a packet like <code>src=80.3.4.5:7890, dst=10.4.4.32:25565</code>.</li>\n<li>The server receives and processes the packet.</li>\n<li>The server will generate a packet <code>src=10.4.4.32:25565, dst=80.3.4.5:7890</code>.</li>\n<li>With some proper routing rules, this packet is forwarded to Router B.</li>\n<li>Router B SNATs to <code>172.232.188.40:25565</code> and sends a packet like <code>src=172.232.188.40:25565</code> back to the internet, completing the round-trip.</li>\n</ul>\n<p>This works fine, but what if we had multiple servers listening on the same port? For example, I might have a Minecraft server listening on port <code>25565</code> on a rack-mounted server, and another Minecraft server listening on the same port on a Raspberry Pi. When Router B receives a packet on port <code>25565</code>, is it supposed to send it to the rack-mounted server or the Raspberry Pi?</p>\n<p>One solution would be to just avoid port collisions globally within the home network for services that I want to expose. In fact, this is what's required if I want to port-forward on my fairly dumb home router. For most use cases, this will be fine, since you probably won't have that many ports to manage in the first place.</p>\n<p>If you aren't able to avoid port collisions for whatever reason, here are some solutions:</p>\n<p><strong>Option 1: Port mapping</strong></p>\n<p>Supposing the rack-mounted server listens on <code>10.4.4.32:25565</code> and the Raspberry Pi listens on <code>10.4.4.33:25565</code>, you could make it so that <code>172.32.188.40:25565</code> forwards to <code>10.4.4.32:25565</code> like usual, and then have <code>172.32.188.40:25566</code> forward to <code>10.4.4.33:25565</code>.</p>\n<p>The downside of this is that you have to maintain a manual port mapping, which can be tricky. For example, what if your Raspberry Pi needed to open up a service at <code>10.4.4.33:25566</code>? It can't occupy <code>172.32.188.40:25566</code> because that's already forwarded to <code>10.4.4.33:25565</code>, so you'd have to choose another port to listen on.</p>\n<p>If you decide to go with this solution, I recommend &quot;namespacing&quot; port allocations so that for every IP device that you might want to expose, you allot a port range. So for example, my rack-mounted server might get the port range <code>25000-25100</code> on the public IP, and the Raspberry Pi might get the port range <code>25100-25200</code>. Whenever an IP device needs to expose a service publicly, you'd assign a port in its port range to map to its private port.</p>\n<p><strong>Option 2: Multiple IP Addresses</strong></p>\n<p>Another solution is to have have a public IP address for each device you want to expose, giving you an entire port space for each device. For example, <code>172.232.188.40:25565</code> could forward to my rack-mounted server at <code>10.4.4.32:25565</code> and <code>172.232.188.41:25565</code> could forward to my Raspberry Pi at <code>10.4.4.33:25565</code>. This is nice because it completely gets rid of port management, leaving you to only manage an IP mapping, which should be much easier. Additionally, a small side benefit of having multiple port spaces available is that it gives you a better chance of letting clients connect to services on their default ports.</p>\n<p>However, there's a pretty big problem with this option: where the hell are you gonna get all these IP addresses?</p>\n<p>We're <a href=\"https://en.wikipedia.org/wiki/IPv4_address_exhaustion\">running out of IPv4 addresses</a> and a lot of work has been put into place to slow IPv4 address exhaustion down, including NAT between private and public networks. You might convince a cloud provider to give you an extra IPv4 address or two per VPS, but I'd be really surprised if they'd lend out any more than that for this use case. Sure, you can always rent more VPS's, but that can obviously cost quite a bit of money, alongside being harder to maintain since you'd have more routers.</p>\n<p>IPv6, on the other hand, has a gargantuan address space. Cloud providers like Linode are willing to just hand out 64-bit wide IPv6 networks <em>per Linode instance.</em></p>\n<p><em>Chaff:</em> Interestingly, it was actually Linode's support team that pointed me in the IPv6 direction. I'd always known a bit about IPv6 but I've pretty much always stuck with IPv4 for most of my networking career because I was just more familiar with it. When I opened a support ticket describing my use case and asking them if I could assign many IPv4 addresses per Linode instance, they politely declined and pointed me to some options I had, IPv6 being one of them. Overall, Linode's support team has been amazing to me and they have servers at very competitive pricing near me, which is why I settled with them. I'd post a referral link here but I haven't yet spent the $25 quota needed to create one.</p>\n<p>Even though I had somewhat limited experience in IPv6 and was a bit skeptical because I wasn't sure how well-supported it was – even in 2025 – I decided I'd try option 2, managing multiple IPv6 addresses.</p>\n<h2>Brassier tacks</h2>\n<p>With a firm grasp on the theory and well-thought considerations on network topology, it's time to implement this.</p>\n<p>One of the things I left out in my explanation is how my devices in the home network are going to connect to Router B, which will live in the cloud.</p>\n<p>We need a virtual network that spans across the home network to the cloud, and it needs to be private.</p>\n<p>Well, that's what VPNs are for! Specifically, we'll use Wireguard, a relatively new VPN technology that has support in the Linux kernel and boasts to be more secure and faster than predecessors like OpenVPN.</p>\n<p>This is what our relevant network topology will look like:</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/ENjQ4XUYgz-480.avif 480w, https://blog.mnke.org/img/ENjQ4XUYgz-960.avif 960w, https://blog.mnke.org/img/ENjQ4XUYgz-1440.avif 1440w, https://blog.mnke.org/img/ENjQ4XUYgz-2000.avif 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/ENjQ4XUYgz-480.webp 480w, https://blog.mnke.org/img/ENjQ4XUYgz-960.webp 960w, https://blog.mnke.org/img/ENjQ4XUYgz-1440.webp 1440w, https://blog.mnke.org/img/ENjQ4XUYgz-2000.webp 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/ENjQ4XUYgz-480.png\" alt=\"\" width=\"2000\" height=\"1857\" srcset=\"https://blog.mnke.org/img/ENjQ4XUYgz-480.png 480w, https://blog.mnke.org/img/ENjQ4XUYgz-960.png 960w, https://blog.mnke.org/img/ENjQ4XUYgz-1440.png 1440w, https://blog.mnke.org/img/ENjQ4XUYgz-2000.png 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"></picture></p>\n<p>The VPS performs static NAT by listening on multiple IPv6 addresses and forwarding them to a VM and forwarding packets from a VM to the internet.</p>\n<p>Note that many of the IPv6 addresses have been truncated for brevity. Also note the similarity in topology to your home network and the differences in NAT to the NAT on your home network.</p>\n<p>To set this up, we need:</p>\n<ol>\n<li>Server(s) in our home network to forward to that you can SSH into</li>\n<li>A VPS with that ability to statically assign itself IPv6 addresses that you can SSH into</li>\n<li>An IPv6 address range for the Wireguard network (Wireguard being our VPN implementation of choice). Continue reading for recommendations on how to choose this.</li>\n</ol>\n<p>I've already set up a few VMs using OpenTofu with my Proxmox server and I chose Linode as my cloud provider. Spinning up a Nanode instance on Ubuntu 24.04 with 1vCPU and 1GB of memory was more than enough for me. Here's Terraform/OpenTofu configuration if you want to follow my setup. <strong>Make sure to note the IPv6 range that was assigned to you for later.</strong></p>\n<pre class=\"language-hcl\"><code class=\"language-hcl\"><span class=\"token keyword\">variable<span class=\"token type variable\"> \"ssh_public_key_file\" </span></span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\"># example = \"~/.ssh/id_rsa.pub\"</span>\n  <span class=\"token property\">type</span> <span class=\"token punctuation\">=</span> string\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\"># Get this from Linode:</span>\n<span class=\"token comment\"># https://cloud.linode.com/profile/tokens</span>\n<span class=\"token keyword\">variable<span class=\"token type variable\"> \"linode_pat\" </span></span><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">type</span>      <span class=\"token punctuation\">=</span> string\n  <span class=\"token property\">sensitive</span> <span class=\"token punctuation\">=</span> <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">terraform</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">required_providers</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">linode</span> <span class=\"token punctuation\">=</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">source</span>  <span class=\"token punctuation\">=</span> <span class=\"token string\">\"linode/linode\"</span>\n      <span class=\"token property\">version</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">\"2.34.1\"</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">provider<span class=\"token type variable\"> \"linode\" </span></span><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">token</span> <span class=\"token punctuation\">=</span> var.linode_pat\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">resource <span class=\"token type variable\">\"linode_sshkey\"</span></span> <span class=\"token string\">\"mykey\"</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">label</span>   <span class=\"token punctuation\">=</span> <span class=\"token string\">\"mykey\"</span>\n  <span class=\"token property\">ssh_key</span> <span class=\"token punctuation\">=</span> chomp(file(var.ssh_public_key_file))\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">resource <span class=\"token type variable\">\"linode_instance\"</span></span> <span class=\"token string\">\"embassy\"</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">image</span>           <span class=\"token punctuation\">=</span> <span class=\"token string\">\"linode/ubuntu24.04\"</span>\n  <span class=\"token property\">label</span>           <span class=\"token punctuation\">=</span> <span class=\"token string\">\"embassy\"</span>\n  <span class=\"token property\">region</span>          <span class=\"token punctuation\">=</span> <span class=\"token string\">\"us-sea\"</span> <span class=\"token comment\"># Choose whichever's closest to you</span>\n  <span class=\"token property\">type</span>            <span class=\"token punctuation\">=</span> <span class=\"token string\">\"g6-nanode-1\"</span>\n  <span class=\"token property\">authorized_keys</span> <span class=\"token punctuation\">=</span> <span class=\"token punctuation\">[</span>linode_sshkey.mykey.ssh_key<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\"># This assigns an IPv6 range to the instance, but not any</span>\n<span class=\"token comment\"># addresses. We add addresses later.</span>\n<span class=\"token keyword\">resource <span class=\"token type variable\">\"linode_ipv6_range\"</span></span> <span class=\"token string\">\"embassy\"</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">linode_id</span>     <span class=\"token punctuation\">=</span> linode_instance.embassy.id\n  <span class=\"token property\">prefix_length</span> <span class=\"token punctuation\">=</span> <span class=\"token number\">64</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">output<span class=\"token type variable\"> \"ipv6_subnet\" </span></span><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">value</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">\"<span class=\"token interpolation\"><span class=\"token punctuation\">$</span><span class=\"token punctuation\">{</span>linode_ipv6_range<span class=\"token punctuation\">.</span>embassy<span class=\"token punctuation\">.</span>range<span class=\"token punctuation\">}</span></span>/<span class=\"token interpolation\"><span class=\"token punctuation\">$</span><span class=\"token punctuation\">{</span>linode_ipv6_range<span class=\"token punctuation\">.</span>embassy<span class=\"token punctuation\">.</span>prefix_length<span class=\"token punctuation\">}</span></span>\"</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>For 3., <a href=\"https://www.digitalocean.com/community/tutorials/how-to-set-up-wireguard-on-ubuntu-20-04#step-2-b-choosing-an-ipv6-range\">DigitalOcean recommends basing the IPv6 network off of a hash of the current time and the server's machine ID</a>, which is what I did. For your convenience, I've shortened the steps into a single command to generate an IPv6 subnet.</p>\n<pre class=\"language-sh\"><code class=\"language-sh\"><span class=\"token builtin class-name\">printf</span> <span class=\"token string\">\"<span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">date</span> +%s%N<span class=\"token variable\">)</span></span><span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> /var/lib/dbus/machine-id<span class=\"token variable\">)</span></span>\"</span> <span class=\"token operator\">|</span><span class=\"token punctuation\">\\</span>\n  sha1sum <span class=\"token operator\">|</span><span class=\"token punctuation\">\\</span>\n  <span class=\"token function\">awk</span> <span class=\"token string\">'{ print $1 }'</span> <span class=\"token operator\">|</span><span class=\"token punctuation\">\\</span>\n  <span class=\"token function\">cut</span> <span class=\"token parameter variable\">-c</span> <span class=\"token number\">31</span>- <span class=\"token operator\">|</span><span class=\"token punctuation\">\\</span>\n  <span class=\"token function\">sed</span> <span class=\"token punctuation\">\\</span>\n    <span class=\"token parameter variable\">-e</span> <span class=\"token string\">'s/^/fd/'</span> <span class=\"token punctuation\">\\</span>\n    <span class=\"token parameter variable\">-e</span> <span class=\"token string\">'s/\\(.\\{4\\}\\)/\\1:/g'</span> <span class=\"token punctuation\">\\</span>\n    <span class=\"token parameter variable\">-e</span> <span class=\"token string\">'s/:$/::\\/64/'</span></code></pre>\n<p>You should get an output like this:</p>\n<pre class=\"language-sh\"><code class=\"language-sh\">fdb4:f371:2e6c::/64</code></pre>\n<p>DigitalOcean recommends to run this on your server for its machine ID, but it shouldn't really matter.</p>\n<p>For the rest of post, we'll assume that:</p>\n<ul>\n<li>The primary network interface on all of the machines is <code>eth0</code></li>\n<li>All of the machines are running Ubuntu or are Debian-based</li>\n<li>The Wireguard IPv6 subnet you generated was: <code>fdab::/64</code></li>\n<li>The public IPv6 subnet you're given is: <code>2600::/64</code></li>\n</ul>\n<h2>VPS Setup</h2>\n<pre class=\"language-sh\"><code class=\"language-sh\"><span class=\"token comment\"># Install wireguard</span>\n<span class=\"token function\">apt</span> update <span class=\"token operator\">&amp;&amp;</span> <span class=\"token function\">apt</span> <span class=\"token function\">install</span> <span class=\"token parameter variable\">-y</span> wireguard\n\n<span class=\"token comment\"># Create public-private key pair</span>\nwg genkey <span class=\"token operator\">|</span> <span class=\"token function\">tee</span> /etc/wireguard/private.key\n<span class=\"token function\">chmod</span> <span class=\"token assign-left variable\">go</span><span class=\"token operator\">=</span> /etc/wireguard/private.key\n<span class=\"token function\">cat</span> /etc/wireguard/private.key <span class=\"token operator\">|</span> wg pubkey <span class=\"token operator\">|</span> <span class=\"token function\">tee</span> /etc/wireguard/public.key</code></pre>\n<p>Create a file in <code>/etc/wireguard/wg0.conf</code>:</p>\n<pre class=\"language-systemd\"><code class=\"language-systemd\"><span class=\"token section\"><span class=\"token punctuation\">[</span><span class=\"token section-name selector\">Interface</span><span class=\"token punctuation\">]</span></span>\n<span class=\"token comment\"># First address on the IPv6 Wireguard network you generated earlier</span>\n<span class=\"token key attr-name\">Address</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">fdab::1/64</span>\n<span class=\"token key attr-name\">PrivateKey</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">&lt;output of /etc/wireguard/private.key></span>\n<span class=\"token key attr-name\">ListenPort</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">51820</span>\n\n<span class=\"token comment\"># Enable IP forwarding. Not sure if the ipv4/ip_forward is necessary,</span>\n<span class=\"token comment\"># but I have it as an artifact from when I was testing with IPv4 and</span>\n<span class=\"token comment\"># I now have a superstition that I need this.</span>\n<span class=\"token key attr-name\">PreUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 1 > /proc/sys/net/ipv4/ip_forward</span>\n<span class=\"token key attr-name\">PreUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 1 > /proc/sys/net/ipv6/conf/all/forwarding</span>\n<span class=\"token comment\"># Disable autoconf while running. We assign some IP addresses that</span>\n<span class=\"token comment\"># get removed by autoconf otherwise.</span>\n<span class=\"token key attr-name\">PreUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 0 > /proc/sys/net/ipv6/conf/eth0/autoconf</span>\n\n<span class=\"token comment\"># replace with a public IPv6 address you own</span>\n<span class=\"token key attr-name\">PostUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip -6 addr add 2600::10/64 dev eth0</span>\n<span class=\"token comment\"># we'll forward ports 10000-10100 for demonstration, but</span>\n<span class=\"token comment\"># **you should limit this**.</span>\n<span class=\"token comment\"># additionally, if you want UDP, copy these lines and replace</span>\n<span class=\"token comment\"># tcp with udp</span>\n<span class=\"token key attr-name\">PostUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip6tables -t nat -A PREROUTING -p tcp -d 2600::10 --dport 10000:10100 -j DNAT --to-destination fdab::10</span>\n<span class=\"token key attr-name\">PostUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip6tables -t nat -A POSTROUTING -p tcp -s fdab::10 -j SNAT --to-source 2600::10</span>\n\n\n<span class=\"token comment\"># these should be the reverse of the commands above.</span>\n<span class=\"token comment\"># so if you add/remove commands above, mirror them below, except replace</span>\n<span class=\"token comment\"># \"-A\" with \"-D\" on the ip6tables commands and \"add\" with \"del\" on the</span>\n<span class=\"token comment\"># ip commands.</span>\n<span class=\"token key attr-name\">PreDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip6tables -t nat -D POSTROUTING -p tcp -s fdab::10 -j SNAT --to-source 2600::10</span>\n<span class=\"token key attr-name\">PreDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip6tables -t nat -D PREROUTING -p tcp -d 2600::10 --dport 10000:10100 -j DNAT --to-destination fdab::10</span>\n<span class=\"token key attr-name\">PreDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip -6 addr del 2600::10 dev eth0</span>\n<span class=\"token key attr-name\">PostDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 1 > /proc/sys/net/ipv6/conf/eth0/autoconf</span>\n<span class=\"token key attr-name\">PostDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 0 > /proc/sys/net/ipv6/conf/all/forwarding</span>\n<span class=\"token key attr-name\">PostDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 0 > /proc/sys/net/ipv4/ip_forward</span>\n\n<span class=\"token section\"><span class=\"token punctuation\">[</span><span class=\"token section-name selector\">Peer</span><span class=\"token punctuation\">]</span></span>\n<span class=\"token key attr-name\">PublicKey</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">&lt;fill this in later></span>\n<span class=\"token key attr-name\">AllowedIPs</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">fdab::10/128</span>\n<span class=\"token comment\"># recommended if this peer is behind a NAT (it probably is)</span>\n<span class=\"token comment\"># consider lowering this if the tunnel breaks every once in a while</span>\n<span class=\"token key attr-name\">PersistentKeepalive</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">30</span></code></pre>\n<p>Again, I won't go into too much detail explaining the <code>iptables</code> commands. You can refer to the resources I linked earlier if you're interested. On a high level, all this does is:</p>\n<ol>\n<li>Makes your VPS listen on a IPv6 address <code>2600::10</code>.</li>\n<li>Forward TCP traffic on <code>2600::10</code> to a Wireguard peer on <code>fdab::10</code> (we'll set the peer up later).</li>\n<li>Forward TCP traffic from <code>fdab::10</code> to the public IPv6 address on <code>2600::10</code></li>\n</ol>\n<h2>Local VM Setup</h2>\n<p>Similar to the VPS, we need to install Wireguard and generate some keys.</p>\n<pre class=\"language-sh\"><code class=\"language-sh\"><span class=\"token comment\"># Install wireguard</span>\n<span class=\"token function\">apt</span> update <span class=\"token operator\">&amp;&amp;</span> <span class=\"token function\">apt</span> <span class=\"token function\">install</span> <span class=\"token parameter variable\">-y</span> wireguard\n\n<span class=\"token comment\"># Create public-private key pair</span>\nwg genkey <span class=\"token operator\">|</span> <span class=\"token function\">tee</span> /etc/wireguard/private.key\n<span class=\"token function\">chmod</span> <span class=\"token assign-left variable\">go</span><span class=\"token operator\">=</span> /etc/wireguard/private.key\n<span class=\"token function\">cat</span> /etc/wireguard/private.key <span class=\"token operator\">|</span> wg pubkey <span class=\"token operator\">|</span> <span class=\"token function\">tee</span> /etc/wireguard/public.key</code></pre>\n<p>Create a file in <code>/etc/wireguard/wg0.conf</code>:</p>\n<pre class=\"language-systemd\"><code class=\"language-systemd\"><span class=\"token section\"><span class=\"token punctuation\">[</span><span class=\"token section-name selector\">Interface</span><span class=\"token punctuation\">]</span></span>\n<span class=\"token key attr-name\">Address</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">fdab::10/64</span>\n<span class=\"token key attr-name\">PrivateKey</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">&lt;output of /etc/wireguard/private.key></span>\n<span class=\"token key attr-name\">ListenPort</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">51820</span>\n<span class=\"token comment\"># Outbound IPv6 traffic will by default by routed through the Wireguard</span>\n<span class=\"token comment\"># tunnel through the AllowedIPs configuration below.</span>\n<span class=\"token comment\"># In my case, I seemed to have a route already added that</span>\n<span class=\"token comment\"># routes local traffic properly, but if you don't, consider adding</span>\n<span class=\"token comment\"># these.</span>\n<span class=\"token comment\"># PreUp = ip route add &lt;local network> dev eth0 proto static</span>\n<span class=\"token comment\"># PostDown = ip route del &lt;local network> dev eth0 proto static</span>\n\n<span class=\"token section\"><span class=\"token punctuation\">[</span><span class=\"token section-name selector\">Peer</span><span class=\"token punctuation\">]</span></span>\n<span class=\"token key attr-name\">PublicKey</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">&lt;the public key on your *VPS*></span>\n<span class=\"token key attr-name\">AllowedIPs</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">::0/0</span>\n<span class=\"token key attr-name\">PersistentKeepalive</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">30</span>\n<span class=\"token comment\"># You can use an IPv4 address here if you want</span>\n<span class=\"token key attr-name\">Endpoint</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">&lt;VPS IP>:51820</span></code></pre>\n<p>Finally, don't forget to copy the public key you generated on your local VM and set it on the VPS configuration:</p>\n<pre class=\"language-systemd\"><code class=\"language-systemd\">...\n<span class=\"token section\"><span class=\"token punctuation\">[</span><span class=\"token section-name selector\">Peer</span><span class=\"token punctuation\">]</span></span>\n<span class=\"token key attr-name\">PublicKey</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">&lt;you can fill this in now></span>\n<span class=\"token key attr-name\">AllowedIPs</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">fdab::10/128</span>\n<span class=\"token comment\"># recommended if this peer is behind a NAT (it probably is)</span>\n<span class=\"token key attr-name\">PersistentKeepalive</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">30</span></code></pre>\n<h2>Remark on multiple VMs</h2>\n<p>If you have multiple VMs, you'd have to add more entries on the VPS's <code>/etc/wireguard/wg0.conf</code>:</p>\n<pre class=\"language-systemd\"><code class=\"language-systemd\">...\n<span class=\"token key attr-name\">PreUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 1 > /proc/sys/net/ipv4/ip_forward</span>\n<span class=\"token key attr-name\">PreUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 1 > /proc/sys/net/ipv6/conf/all/forwarding</span>\n\n<span class=\"token comment\"># === Copy and paste this block for each VM ===</span>\n<span class=\"token comment\"># ===   Don't forget to replace addresses   ===</span>\n<span class=\"token key attr-name\">PostUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip -6 addr add 2600::10/64 dev eth0</span>\n<span class=\"token key attr-name\">PostUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip6tables -t nat -A PREROUTING -p tcp -d 2600::10 --dport 10000:10100 -j DNAT --to-destination fdab::10</span>\n<span class=\"token key attr-name\">PostUp</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip6tables -t nat -A POSTROUTING -p tcp -s fdab::10 -j SNAT --to-source 2600::10</span>\n<span class=\"token comment\"># =============================================</span>\n\n<span class=\"token comment\"># =========== Same with this block ============        </span>\n<span class=\"token key attr-name\">PreDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip6tables -t nat -D POSTROUTING -p tcp -s fdab::10 -j SNAT --to-source 2600::10</span>\n<span class=\"token key attr-name\">PreDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip6tables -t nat -D PREROUTING -p tcp -d 2600::10 --dport 10000:10100 -j DNAT --to-destination fdab::10</span>\n<span class=\"token key attr-name\">PreDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">ip -6 addr del 2600::10 dev eth0</span>\n<span class=\"token comment\"># =============================================</span>\n\n<span class=\"token key attr-name\">PostDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 0 > /proc/sys/net/ipv6/conf/all/forwarding</span>\n<span class=\"token key attr-name\">PostDown</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">echo 0 > /proc/sys/net/ipv4/ip_forward</span>\n...\n\n<span class=\"token comment\"># =========== Same with this block ============ </span>\n<span class=\"token section\"><span class=\"token punctuation\">[</span><span class=\"token section-name selector\">Peer</span><span class=\"token punctuation\">]</span></span>\n<span class=\"token key attr-name\">PublicKey</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">&lt;fill this in later></span>\n<span class=\"token key attr-name\">AllowedIPs</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">fdab::10/128</span>\n<span class=\"token key attr-name\">PersistentKeepalive</span> <span class=\"token punctuation\">=</span> <span class=\"token value attr-value\">30</span>\n<span class=\"token comment\"># =============================================</span></code></pre>\n<h2>Bringing it together</h2>\n<p>On each of your hosts, run <code>systemctl start wg-quick@wg0</code>. For a post-flight checklist, I recommend doing the following tests:</p>\n<h3>Sanity check</h3>\n<p>Try pinging between your clients and servers: <code>ping6 fdab::10</code>. If this doesn't work, then Wireguard likely has not established a tunnel, which you can check with <code>wg show</code>.</p>\n<h3>Check outbound NAT works</h3>\n<p>On VPN clients, <code>curl -6 ifconfig.me</code>. It should match a public IPv6 address of your VPS (e.g. <code>2600::10</code>). If this doesn't work but Wireguard has established the tunnel, then something is wrong with the <code>iptables</code> configuration.</p>\n<h3>Check inbound NAT works</h3>\n<p>On a VPN client, continually listen on a TCP port with:</p>\n<pre class=\"language-sh\"><code class=\"language-sh\"><span class=\"token keyword\">while</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">do</span> <span class=\"token function\">nc</span> <span class=\"token parameter variable\">-vnlp</span> <span class=\"token number\">10000</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">done</span></code></pre>\n<p>On different machine (ideally one <em>not</em> in your home network to prevent potential routing issues),</p>\n<pre class=\"language-sh\"><code class=\"language-sh\"><span class=\"token function\">nc</span> <span class=\"token parameter variable\">-vn</span> <span class=\"token operator\">&lt;</span>public IPv6 matching that VPN client<span class=\"token operator\">></span> <span class=\"token number\">10000</span></code></pre>\n<p>If all goes well, you should be able to establish a TCP connection and start communicating with netcat.</p>\n<p>If you're unable to establish a connection, I don't have a quick solution other than to start monitoring traffic on both your VPS and on your VM with <code>tcpdump -ni wg0 -p tcp port 10000</code> and even <code>tcpdump -ni eth0 -p tcp port 10000</code> on your VM, in case it turns out that outbound packets on the VM are being routed incorrectly. You'll have to read the traffic flow and diagnose what's going wrong based on which packets are arriving on which interfaces – or if they aren't.</p>\n<p>Otherwise, if all of these checks succeed, you should be able to open arbitrary TCP ports on your VMs and expose it via the VPS! Of course, you should limit what's exposed on your VMs and limit what's forwarded/accepted on the VPS. If you want UDP (many games will probably need this), the <code>/etc/wireguard/wg0.conf</code> configuration on the VPS needs to be adjusted accordingly (I've already marked what needs to be changed).</p>\n<p>Finally, if you're sure that this configuration works, then you can run <code>systemctl enable wg-quick@wg0</code> on all your hosts. We didn't do this earlier in case somehow turning on the service blocked us from connecting to a host. By not enabling the service, we'd still be able to just restart the machine to wipe the ephemeral configuration in case things went wrong.</p>\n<p>Of course, if you plan to scale up/down the number of local VMs, you should probably write a script to do this for you. Personally, I've created a Terraform/OpenTofu module that chains into an Ansible playbook making use of a <a href=\"https://github.com/githubixx/ansible-role-wireguard/tree/master\">Wireguard role that Githubixx wrote</a>, which I may release in the future.</p>\n<h1>Next steps</h1>\n<p>We've configured a VPS as a secondary router that NATs traffic for us over a Wireguard network, which is pretty awesome. Of course, what I set up is <em>way</em> overkill for the average person who just wants to run a Minecraft server for their friends. At the same time though, this really wasn't <em>that</em> hard to implement, nor is it actually that complicated in theory once you break it down. At any rate, FRP would be a solid option for most people. Seriously, FRP looks awesome and I want to give it a try and write another blog post on it or even make a video giving it much-needed spotlight.</p>\n<p>The configuration I gave is mostly secure, but I'd definitely tighten up the ports that the VPS forwards for, as well as making sure a firewall is enabled – either by your cloud provider or on the VPS itself through something like <code>ufw</code> (or even through <code>iptables</code> 😉). I'd also be absolutely sure that the range of ports that the VPS forwards for on each VM are closed, except for the ones that explicitly should be open. Even better, you could forward individual ports, one for each that you need to open, rather than forwarding port ranges.</p>\n<p>It might be worth looking into traffic limiting as well. Linode only gives a certain amount of bandwidth per month, meaning an attacker could flood the VPS and take down networking for the remaining month. I currently don't know any great solutions to this. Whitelisting IPs is too inflexible and manual for my needs, and limiting bandwidth per connection seems like it would be either too restrictive per connection, or too loose to prevent any competent attacker from flooding the VPS. Fortunately, this isn't a problem I've run into yet.</p>\n<p>You could also set up some monitoring on the traffic that's being forwarded on the VPS with something like Promtail, Loki, and Grafana. You could even send it back into your home network, which shouldn't be too hard since you're already connected through the Wireguard tunnel.</p>\n<p>You could also set up dynamic policy-based routing, where your VMs will only send packets through the Wireguard tunnel as a response, rather than blindly sending all traffic through the tunnel. This may be advantageous in cases where you want your VMs to use your primary router by default, but only respond to client requests to your server. I personally wasn't able to get this working completely, only being able to support TCP, not UDP. It might be fun to do this as a challenge using <code>iptables</code> and <code>conntrack</code>, but I'm not sure I'd recommend this, since you're probably going to throw off STUN binding results and possibly expose your primary router's IP through STUN/ICE exchanges if you have this split policy.</p>\n<p>For me, I'm gonna be monitoring how well this works for the next while, focusing on the IPv6 aspect of it. While I haven't run into any problems so far, I can't be sure of its stability just yet.</p>\n<p>In any case, I hope you enjoyed this post and got something out of it. It was a blast playing around with networking like this and discovering the sheer power of <code>iptables</code>.</p>\n",
      "date_published": "2025-02-21T05:46:18Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/how-i-set-up-scalable-high-capacity-streaming-infrastructure/",
      "url": "https://blog.mnke.org/how-i-set-up-scalable-high-capacity-streaming-infrastructure/",
      "title": "How I set up scalable, high capacity streaming infrastructure",
      "content_html": "<h1>The idea</h1>\n<p>Recently, some of my friends decided to hold an online tournament, and the main tournament organizer wanted everyone to be able to stream their screens so the production crew could grab live feeds and multiplex/composite them, ultimately feeding an &quot;official&quot; production tournament broadcast to a streaming service like Twitch.</p>\n<p>Originally, they were planning to use a service like <a href=\"https://vdo.ninja/\">VDO.Ninja</a> for everyone to stream to. VDO.Ninja has inbuilt compositing capabilities, allowing someone to select an individual stream and show it, show two streams side-by-side, etc. Not only that, but contestants wouldn't have to set up any streaming software since VDO.Ninja uses WebRTC on the browser for streaming, making streaming really easy for contestants with less technical skills.</p>\n<h1>Problems</h1>\n<p>The only thing VDO.Ninja lacked was recording capabilities. Part of the reason is that WebRTC is fundamentally P2P, meaning streams aren't first delivered to a central server, where they can be saved, before delivering to clients. While there was <a href=\"https://github.com/steveseguin/browser-to-rtmp-docker/tree/master\">some tooling</a> to export VDO.Ninja broadcasts into another server (built by a VDO.Ninja maintainer, I think), I found it kind of finnicky to work with. We really wanted every stream to be recorded though since we might miss important highlights live, and we want the ability to dig through individual streams to compile a tournament highlights video after.</p>\n<p>I happened to have recently watched an old video by <a href=\"https://youtu.be/Y-9kVF6bWr4?si=9-jcChvBlrLgRUfn\">Nerd on the Street</a> where he set up a simple HLS streaming server with NGINX, powered by an RTMP module written for NGINX. Setting it up locally was extremely simple, taking me only 15 minutes or so to get a POC up. I got an NGINX instance compiled with <a href=\"https://github.com/arut/nginx-rtmp-module\">arut's NGINX RTMP module</a> running and I used OBS to send an RTMP stream locally and used OBS again to pull the RTMP stream. This demonstrated that we could pretty easily use an RTMP server for clients to send their streams to and then use OBS to pull streams from the RTMP server and use OBS to multiplex and composite the streams.</p>\n<p>Digging it into the <a href=\"https://github.com/arut/nginx-rtmp-module/wiki/Directives\">documentation for the RTMP module</a> more, I found that it supported <a href=\"https://github.com/arut/nginx-rtmp-module/wiki/Directives#record\">directives for recording</a>. Awesome! This looks like exactly what we needed.</p>\n<p>Emboldened, I started writing some configuration to deploy the RTMP server on a cloud service. I ended up chose AWS because it had servers fairly close to me, and I wanted to minimize latency as much as possible. I wrote some basic Terraform configuration for server provisioning, and an Ansible playbook to install Docker and deploy a compose file for an NGINX + RTMP image, alongside the NGINX configuration I needed.</p>\n<p>We deployed the server on a tiny 1vcpu 1GB server and I was able to send a stream for a friend to pull. However, while I was waiting for packages to install on the low-powered server, I realized there might be a problem. We were going to have up to 40 contestants participating in the tournament. Granted, it wasn't certain that everyone was going to stream, but I wanted to prepare for the worst. If all 40 contestants streamed to the server, would a single server be able to handle it? Just forwarding a stream shouldn't be too intensive, but recording 40 streams at once sounded like it might be a problem. While we <em>may</em> be able to get away with one really powerful server for just 40 streams, I wanted a solution that could scale as much as needed.</p>\n<p>My first move was deploying a Docker swarm, which allowed horizontal scaling really easily. I rewrote the Terraform and Ansible configuration to create a Docker swarm with an arbitrary amount of worker nodes and converted the compose file for the RTMP server into a stack with variable number of replicas. I deployed the infrastructure, sent some streams, and verified the load was being spread properly. However, I was only able to pull some of the streams. What was happening was that the streams I send (e.g. to <code>/stream-1, /stream-2, ...</code> were being load balanced on L4 by Docker's mesh network to some arbitrary RTMP container (as expected), but when I try to pull a stream, they might not be load balanced to the same container. Obviously, Docker's L4 load balancing isn't aware of the L7 stream paths. For some reason, I completely neglected to think about this. I'm too spoiled by HTTP, with its powerful L7 reverse-proxy ecosystem, I guess.</p>\n<h1>Infrastructure design</h1>\n<p>Taking a step back, I did some more research on the NGINX RTMP module and found some <a href=\"https://github.com/arut/nginx-rtmp-module/wiki/Directives#relay\">useful directives</a>. The <code>push</code> and <code>pull</code> directives gave the server powerful relaying capabilities, allowing me to easily compartmentalize and topologize the infrastructure as desired. I still wanted to go with Docker Swarm instead of Kubernetes because Kubernetes seemed overkill and honestly, I was rusty on my Kubernetes. It had been over half a year since I administrated my last cluster. Additionally, I wanted to play around with Docker Swarm a bit more since it seems like a good middle-ground between a single node VM serving Docker containers and an entire Kubernetes cluster.</p>\n<p>After some careful planning, I came up with this infrastructure:</p>\n<ul>\n<li>N ingestion nodes:\n<ul>\n<li>Clients publish to these nodes.</li>\n<li>These nodes forward streams to recording nodes and a playback node.</li>\n</ul>\n</li>\n<li>M recording nodes:\n<ul>\n<li>Receives streams from ingestion nodes.</li>\n<li>Records streams and uploads them to an S3 bucket.</li>\n</ul>\n</li>\n<li>1 playback node:\n<ul>\n<li>Receives streams from nodes.</li>\n<li>Serves streams to production crew for compositing.</li>\n</ul>\n</li>\n</ul>\n<p>Here's a diagram of the infrastructure:</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/01_9medzmP-480.avif 480w, https://blog.mnke.org/img/01_9medzmP-960.avif 960w, https://blog.mnke.org/img/01_9medzmP-1440.avif 1440w, https://blog.mnke.org/img/01_9medzmP-2000.avif 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/01_9medzmP-480.webp 480w, https://blog.mnke.org/img/01_9medzmP-960.webp 960w, https://blog.mnke.org/img/01_9medzmP-1440.webp 1440w, https://blog.mnke.org/img/01_9medzmP-2000.webp 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/01_9medzmP-480.png\" alt=\"\" width=\"2000\" height=\"1874\" srcset=\"https://blog.mnke.org/img/01_9medzmP-480.png 480w, https://blog.mnke.org/img/01_9medzmP-960.png 960w, https://blog.mnke.org/img/01_9medzmP-1440.png 1440w, https://blog.mnke.org/img/01_9medzmP-2000.png 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"></picture></p>\n<p>Diagram of streaming infrastructure. Multiple overlaid squares indicates variable number of nodes.</p>\n<p>You might be wondering why there's only one playback node. As mentioned earlier, we need a L7 reverse proxy to load balance between playback nodes. While we could actually write a L7 reverse proxy by writing a small HTTP server that's called using <a href=\"https://github.com/arut/nginx-rtmp-module/wiki/Directives#notify\">the RTMP module's <code>on_*</code> directives</a> alongside Docker Swarm's service discovery to poll internal nodes for whether they have a stream, I decided that would be overkill for this project. A single somewhat powerful playback node should be able to handle only forwarding streams for our purposes.</p>\n<p>However, since I <em>did</em> mention scalable infrastructure in the title and I want to stay true to my word, I'll go into a bit more depth on how this would be done.</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/8aM7-H4CR_-480.avif 480w, https://blog.mnke.org/img/8aM7-H4CR_-960.avif 960w, https://blog.mnke.org/img/8aM7-H4CR_-1440.avif 1440w, https://blog.mnke.org/img/8aM7-H4CR_-2000.avif 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/8aM7-H4CR_-480.webp 480w, https://blog.mnke.org/img/8aM7-H4CR_-960.webp 960w, https://blog.mnke.org/img/8aM7-H4CR_-1440.webp 1440w, https://blog.mnke.org/img/8aM7-H4CR_-2000.webp 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/8aM7-H4CR_-480.png\" alt=\"\" width=\"2000\" height=\"1888\" srcset=\"https://blog.mnke.org/img/8aM7-H4CR_-480.png 480w, https://blog.mnke.org/img/8aM7-H4CR_-960.png 960w, https://blog.mnke.org/img/8aM7-H4CR_-1440.png 1440w, https://blog.mnke.org/img/8aM7-H4CR_-2000.png 2000w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"></picture></p>\n<p>Diagram of revised streaming infrastructure. Multiple overlaid squares indicates variable number of nodes.</p>\n<p>Let's break the changes down:</p>\n<ul>\n<li>We added a set of <em>origin</em> nodes to the cluster, which arbitrarily receives streams from ingestion nodes (the term <em>origin</em> is inspired from <a href=\"https://ossrs.io/lts/en-us/docs/v6/doc/origin-cluster\">SRS</a>, but another appropriate name might simply be <em>relay</em>) and simply wait to for the streams to be pulled.</li>\n<li>When a playback node receives a request for a stream, say <code>/stream-k</code> from the production crew, we use the <a href=\"https://github.com/arut/nginx-rtmp-module/wiki/Directives#on_play\"><code>on_play</code> directive</a> to make an HTTP request to a <em>controller</em> node requesting for <code>/stream-k</code>.</li>\n<li>When a controller receives an HTTP request from the playback node, it uses Docker Swarm's service discovery (SD) to enumerate all origin nodes and poll each for whether <code>/stream-k</code> is available on them and responds with an HTTP redirect to the origin node with <code>/stream-k</code> available.</li>\n<li>When the playback server receives a redirect from a controller, it will start pulling from the origin node with <code>/stream-k</code> available and send it to the requester.</li>\n</ul>\n<p>With these changes, every component of the architecture can be horizontally scaled arbitrarily.</p>\n<p>Anyway, I didn't do that because it was too high effort 😛. Instead, I added some authentication to the ingestion servers by using <a href=\"https://github.com/arut/nginx-rtmp-module/wiki/Directives#on_publish\">the <code>on_play</code> directive</a> and writing a simple HTTP server that authenticated the stream path (what contestants would enter as the stream key on OBS) against a Redis database that mapped their stream key to a human-friendly path. For example, if I (Tony) streamed to the path<code>/333fe3cc19f4a6c80cefb146a28883eb</code>, I'd have a mapping from <code>333fe3cc19f4a6c80cefb146a28883eb</code> to <code>tony</code> on the Redis database, which would make the ingestion node relay the stream to playback and recording nodes on the path <code>/tony</code>. This made naming recordings easier as well as give the production crew human-friendly stream paths to pull from.</p>\n<p>Additionally, I added some monitor tooling with Grafana, Prometheus, cadvisor, node-exporter as well as a Redis admin panel to mess around with stream keys on-the-fly and Traefik as an HTTP reverse proxy for basic authentication. I also wrote a small web application that connected to a mod for the we were playing to expose an admin interface on the browser as well as gave live updates to the web application, but I won't go into detail for that in this post.</p>\n<p>The remainder of this post is about how the tournament turned out and my stories from it. If you're only interested in streaming architecture, there's no discussion of that beyond this point.</p>\n<h1>Pilot</h1>\n<p>We set a date to host a mini-tournament with a quarter of the contestants to test out the streaming infrastructure. I ran a few commands to set up the swarm, we distributed streaming keys, and it worked like a charm. I monitored the server loads on my Grafana dashboard and they were holding up amazingly with 10 concurrent streams. None of the servers were even hitting 50% load per CPU. I was feeling confident we'd be able to handle 4x the load as long as I scaled accordingly. After destroying the swarm, all there was left to do was wait for the day of the tournament.</p>\n<p>But of course I didn't do that. I knew there were some improvements to be made. Namely, the playback server didn't have any authentication mechanism, meaning malicious actors could potentially publish and override a contestant's stream. Obviously, no one was going to do that, but I had the programmer's itch to achieve perfection, against my best judgement.</p>\n<p>So, after making sure to freeze the configuration that worked during the mini-tournament in Git, I started hacking at a solution. Part of what made it nontrivial was that through Docker's ingress network, an SNAT is applied that masquerades the original client IP, making it difficult to <a href=\"https://github.com/arut/nginx-rtmp-module/wiki/Directives#deny\">deny publishing</a> using IP ranges. Without going into too much detail, I found a solution that seemed to work. After some more testing, I was happy with what I had and settled with this version for the tournament.</p>\n<h1>Mayday</h1>\n<h2>T-2h</h2>\n<p>The day of the tournament finally arrived. Two hours before the start of the tournament, I spun up the streaming infrastructure and the production crew started setting up their software. A few contestants arrived early and started streaming to the server. Some streams weren't showing up on the production crew's feed, but I gave it little thought because I knew it could take a while to connect and sometimes you just have to refresh the streams.</p>\n<h2>T-1h</h2>\n<p>There was one contestant who said he had been streaming to the server the entire time it was up, yet the production feed wasn't picking up on it. Strange. I started deeper investigation by trying to pull feeds from all the contestants that were streaming. Some of them weren't showing up. At this point, I was starting to get anxious, wondering if I should roll back to the version that worked during the pilot run or whether I could diagnose the problem before the stream started.</p>\n<h2>T-45m</h2>\n<p>More and more contestants were showing up and starting to stream now. Some streams still were not showing up and I hadn't found a diagnosis, much less a solution. Spinning up the entire infrastructure took about 15 minutes. There was no time left and the situation was getting dire. I decided to allot 15 minutes to finding and fixing the issue, otherwise it was time to roll back. I muted and deafened myself, blocking out the noise from the growing number of contestants joining the call and focused all my attention on making sure things worked. There were 40 contestants and a handful of production crew, all of who were depending on the streaming infrastructure. I had to do whatever I could to get it running.</p>\n<h2>T-30m</h2>\n<p>15 minutes of furiously issuing commands still left my empty-handed. There was no other choice than to roll things back. I sent a message to a tournament organizer telling them to notify the dozen or so contestants streaming that the server would be going down temporarily. I destroyed the infrastructure and reverted the configuration to the stable version. I ran the commands to spin up the new infrastructure and waited in apprehension, watching each command fly by. <em>What if even rolling back doesn't work?</em> I wondered. I shook the thought off. <em>It will work.</em> I reassured myself.</p>\n<h2>T-15m</h2>\n<p>The streaming server was back up. I began receiving notifications that some contestants started publishing to the server, while others waited for DNS records to propagate. I checked in with production. <em>We were receiving feeds for all publishing contestants</em>. I breathed a big sigh of relief. It seems like at least this problem was solved. But there were more problems to come.</p>\n<h2>T-5m</h2>\n<p>There were still a few contestants unable to connect to the server, and I was sent to diagnose the issue. After going through all the standard procedures like making sure the URL was used, the right stream key was inputted, etc., they still weren't able to connect. <em>It couldn't be a DNS issue, could it? I mean, I set a TTL of 600s on the records, the lowest my registrar allows, and it's been well over 600s</em>. I thought to myself. To make sure, I guided a contestant to do an <code>nslookup</code> on my domain. She was still hitting the cache for the old records, and I assume this was the case for the other few contestants that were experiencing similar issues. Frantically, I searched up how to flush the DNS cache on Windows and relayed the information to them. No luck. Maybe the records were still cached externally. My last resort was to ask them to edit their hosts file, which is seriously a pain on Windows. <em>Yet it still didn't work!</em></p>\n<p>At this point, I should have asked them to stream to the IP address directly. RTMP servers don't do any hostname identification, after all. However, I was too caught up in the chaotic atmosphere, so focused on making sure the domain resolved correctly as the solution that I didn't think of this. Out of ideas, I could only apologize to the contestants and ask them to try again in a few minutes.</p>\n<h2>T+10m</h2>\n<p>We were past the planned start time, but between contestants showing up late, tournament organizers having their hands full dealing with questions from the contestants and a general lack of preparation, we were running late on schedule. Fortunately, I had a chance to breathe because the technical issues seemed to have been resolved. I check up on my Grafana dashboards I had prepared to see how the server was holding up. <em><strong>400% system load per CPU</strong></em> on one of the servers was the first thing I saw. My face went white. We were 15 minutes past the start time. Scaling horizontally doesn't solve the issue because it seemed to be just the playback container generating this load. The only way to handle this load was scaling vertically, which meant destroying the infrastructure and rebuilding it again.</p>\n<p>My mind was racing. The importance of making the correct decision fast was not lost upon me. I didn't have time to think about why there was so much load compared to the test run. I thought hard about what I should do for a minute. If we continue under this extreme load, there could be problems with playback, causing problems with the broadcast. Since I also didn't distribute the placement of the containers well, there seemed to also be a recording container scheduled on the server with high load, meaning there could be recording issues too. However, If I vertically scale, this would cause the infrastructure to be rebuilt, causing everyone to disconnect and possibly never reconnect, given the DNS issues we had earlier.</p>\n<p>I made the hard decision to vertically scale. My plan was to scale up first, and then start re-balancing the containers after. There was no chance I could re-balance the containers with the current infrastructure, with just one container being under so much pressure. I also didn't configure a re-balance before I spun up the new infrastructure because I needed to get the new infrastructure up ASAP. So I destroyed the infrastructure and started rebuilding it with higher spec'd VMs. I didn't notify anyone of this decision since there was so little time to lose and contestants were left wondering why they disconnected.</p>\n<h2>T+20m</h2>\n<p>After a painful 10 minutes, the servers were up and contestants were connecting back to them. However, there was one server that just failed to start and the command to deploy the Docker stack was hanging, even though contestants were connecting. In retrospect, my guess is that all the contestants and the production crew constantly trying to connect to the servers at once affected the startup, causing it to behave strangely. As for why one server failed to start, I still have no idea. I guess I got unlucky.</p>\n<p>Even though almost all of the contestants connected back, I was still anxious. Looking at the dashboard, I saw that the server load per CPU was still around 220%. More importantly, the command to deploy the stack was still hanging. The second part of my plan to re-balance the containers after the initial deployment hadn't been executed yet, and this was vital for further reducing the load. The only thing I could do as I waited for the command to finish was read Docker documentation to see what else I could tune.</p>\n<h2>T+25m</h2>\n<p>The command still hadn't terminated. Even though the stream seemed to be going smoothly, I knew I had to apply the configuration to re-balance the containers. The tournament had officially started now and the production crew was broadcasting to Twitch. I held my breath as I <code>CTRL+C</code>'d out of the hanging command, waiting to see if any streams died.</p>\n<p>After a minute of waiting, nothing happened. The streams were continuing like normal. <em>Alright, there's just one last step of this plan.</em> I thought. <em>I just have to apply this configuration that should help re-balance the containers.</em></p>\n<p>I <code>ssh</code> into the manager node and carefully edit the compose file, making sure not to make any changes to the playback container. I was fine with ingestion and recording containers being rescheduled, since a new replica should take its place before the old one shuts down, meaning there would only be a brief interruption, but the playback container could not be touched since there could only be one of them.</p>\n<h2>T+30m</h2>\n<p>After double-checking and triple-checking the configuration, I run a <code>docker stack deploy</code>. The playback container goes down. The production crew's streaming software crashes from this interruption, and a fallback broadcast is sent to Twitch. I still don't really get why the playback container went down. Normally, if you don't make any changes to a service's configuration and re-deploy the stack, nothing happens to it. I think it must be because the service didn't start up properly earlier, as indicated by the command hanging.</p>\n<p>In any case, we were on code red. One of the casters had to take over and make some small talk during this downtime, and I couldn't do anything other than wait for the stack to come up.</p>\n<p>After an agonizing few minutes, it fortunately did. We re-launched the crashed software and the streams came back online one-by-one. We redirected the broadcast to the contestant's screens and things were back to normal.</p>\n<p>One of the servers was still under fairly heavy load (around 150% system load per CPU, spiking occasionally to 180%). Additionally, one of the containers still didn't seem to be rescheduled correctly (likely a misconfiguration on my end), but seeing that the streams were running smoothly after a few minutes, I relaxed. Plus, I was way too scared of making any changes, even if they <em>should</em> be safe, given how catastrophic it was earlier.</p>\n<h2>T&gt;30m</h2>\n<p>After a turbulent start, the rest of the stream went relatively according to plan. People seemed to have fun and the tournament turned out pretty well. Well, except for the fact that we never actually recorded the main broadcast. We had made a new Twitch account for this and forgot to enable recording broadcasts. So while we recorded the contestant's individual streams, we didn't have the main broadcast, which included at least the commentary of the casters, the stream chat, and the general atmosphere of the stream. 🤦</p>\n<h1>Reflections</h1>\n<p>So, what went wrong that day?</p>\n<p>To start, I shouldn't have deployed the unstable configuration. Even though I thought I tested it well and even though I <em>still</em> don't know why it didn't work, I can't really justify &quot;securing&quot; the playback servers against unauthorized publishing by using the unstable configuration. This didn't end up being that big of a factor though. After all, I did have my backup plan of reverting to the stable version, which was executed almost perfectly. The only thing I didn't account for was DNS taking too long to propagate, which did end up causing some issues.</p>\n<p>Really, the biggest factors were poor container balancing and the single playback container being a bigger bottleneck than I thought. While it's somewhat unreasonable to redesign the architecture to handle multiple playback containers as suggested above, I totally could've vertically scaled the node handling playback to have much higher specs as well as done a better job making sure no other containers were scheduled on the playback node.</p>\n<p>Even with a few hiccups here and there, this was a great experience. I learned about streaming architecture, tournament planning, and it was great to see everything come together. In preparation for the next tournament, I've ported the architecture to use <a href=\"https://github.com/bluenviron/mediamtx\">MediaMTX</a> containers, which has richer features than just an NGINX-RTMP container.</p>\n<p>Check out the <a href=\"https://github.com/tonyd33/gnomes-tournament-resources/tree/de2e4613d1b226b1858837c88607adbb49c8d69e\">repository on the stable commit</a> if you'd like.</p>\n<p>The MediaMTX port is still a WIP as the next tournament date is still in limbo, but you can check out my <a href=\"https://github.com/tonyd33/gnomes-tournament-resources/tree/ec4286f48f49185185716c5244f9bd9d0d09199f\">current version here</a>.</p>\n",
      "date_published": "2025-02-13T03:40:11Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/maplestory-hax-1337/",
      "url": "https://blog.mnke.org/maplestory-hax-1337/",
      "title": "Maplestory Hax 1337",
      "content_html": "<p>I started playing Maplestory in around 2006 upon the recommendation of my friend and my childhood is filled with fond memories of it. I played it on and off in my adolescent years but grew increasingly frustrated with the direction of the game so I quit permanently. In high school, I knew that Maplestory hacks existed and even how to get them running, but I had always wondered how they worked and aspired to create my own hacks.</p>\n<p>Well, after completing an entire degree in computer science, I came back to this question with renewed interest and newfound expertise. I had some experience in reverse engineering, but basically none in reverse engineering low-level binaries and I wasn’t confident I could crack Maplestory’s anti-cheat right off the bat, so I honed my skills on other singleplayer games without anti-cheat first. Then, with some practice under my belt, I directed my aim to Maplestory.</p>\n<p>The first part to hacking a game is to do research on the available hacks. How do they work? What can I learn from them? Unfortunately, Maplestory is a pretty unpopular game by today’s standards and the hacking resources out there were really out of date. Even worse, Maplestory switched from 32-bit binaries to 64-bit binaries relatively recently, which invalidated a ton of resources. Still, I pieced together what I could in the hopes of crossing the first great barrier: disabling their anti-cheat.</p>\n<p>I don’t think I can legally put most of this information online (hell, hacking the game privately may even be illegal — I mean… <em>allegedly</em> hacking the game privately). Although it’s unlikely Nexon would ever come after me for this especially since this information exists in other, more well-known websites, and I think in general, trying to hack games and publishing reverse engineered information online is morally permissible, I still feel some moral pull to not contribute to it here (at least in detail). Also, this project happened months ago and I’m too lazy to dredge up the details 😛</p>\n<h2>Anti-cheat bypassing</h2>\n<p>After I compiled Cheat Engine to hopefully prevent detection by the anti-cheat, one of the first things I tried was to modify some instructions in memory. Of course, an integrity check caught this and the game exited immediately. Integrity checks must scan the memory I modified, so I made some changes and tried to find what was reading that memory, which lead me to some obfuscated code in the Maplestory binary. I could only see a <code>repe movsb</code> instruction followed by a <code>jmp</code>, and instructions before were clearly mangled, so it seems like the instruction was reached through another <code>jmp</code>. In any case, I broke on that instruction to find the start and end region of the copy and found that it was just the region occupied by the Maplestory module. So, I copied that region into somewhere else in memory and then hooked the instruction to change the region to the copied one. I changed some instructions here and there and… it worked!</p>\n<p>Next, I knew that as part of the NGS (Nexon Game Security) anti-cheat suite, another process called BlackCipher64 was spawned. I wasn’t too sure of what that did, but I figured I needed to hijack it, considering I had seen some forum threads talking about how to disable it in previous versions. I guessed that it would scan through the Maplestory process for an extra integrity check and for possibly unknown modules. When I was looking for integrity checks earlier, I couldn’t find anything indicating BlackCipher was doing this though, nor could I find any calls to <code>ReadProcessMemory</code> to remotely read the Maplestory process’s memory though so I tried to catch BlackCipher64 trying to scan for modules in Maplestory. Sure enough, I found a call to <code>Module32Next</code>, which is used to enumerate modules. I tried to place a sleep in <code>Module32Next</code>, but BlackCipher64 and Maplestory would both exit after a while, which indicated that BlackCipher was subject to integrity checks as well. I knew that integrity checks might be difficult to perform across modules, so I guessed that the module containing <code>Module32Next</code> had an integrity check separate from the module running <code>BlackCipher64.aes</code>. I traced the <code>Module32Next</code> call back into the <code>BlackCipher64.aes</code> module and tried to place the sleep in there, and the game didn’t exit! It seems like the main module didn’t have any integrity checks that forced an immediate exit upon failure.</p>\n<p>I didn’t know how well these bypasses really worked. I mean, there could be another mechanism that detects my bypasses and silently reports it. The BlackCipher bypass, for example, really <em>shouldn’t</em> have worked: why wasn’t there an integrity check for the main module? I was too excited that my bypasses seemed to work that I didn’t think about this and continued onward.</p>\n<p>All of my work had been prototyped using Cheat Engine, but I wanted to transfer it into a DLL I could inject for ease of use. There was the potential that the DLL would be detected still, but this was a challenge I wanted to overcome. So, after a lot of head-scratching in C++, I transferred my work into DLLs to inject into the BlackCipher process and the Maplestory process. Sure enough, they worked.</p>\n<h2>Network protocol reversing</h2>\n<p>The pinnacle of a hacked MMO client, as I see it, is a headless client sending and receiving packets conforming to the network protocol of the game. This demonstrates complete mastery over what can be known about the client by the server and is likely to reveal attack vectors that would be otherwise difficult to find through simple client modifications. In practice, this is immensely difficult. Reverse engineering the network protocol is already a daunting task, but trying to do it through obfuscated code makes it difficult enough that it’s just not worth it. I was doing this for sport, not out of a desire to create competent hacks, though. So I set my eyes on understanding the network protocol.</p>\n<p>The first matter is that packets are encrypted, meaning we can’t MITM spy on packets past the application layer with Wireshark or something. We have to find hook and log the packets before they’re encrypted and after they’re decrypted. Finding the encryption and decryption functions took me almost a week. The decryption function isn’t too bad because I can just break on <code>recv</code> and monitor reads at the memory that the <code>recv</code> populates and follow it from there. The encryption function is really difficult because by the time the <code>send</code> function is called, you have no idea where the memory came from or how it was constructed. I’d normally backtrace by breaking on the <code>send</code> and tracing up the call stack, but some regions of the code is extremely obfuscated, so the stack during <code>send</code> looks completely messed up and I can’t reasonably follow it that way. As I mentioned before, it’s been months since I started this project and honestly, I have no idea how I even backtraced the encryption function. I’m looking through my notes and I think I pieced together a global structure I called <code>NetworkManager</code> which contained structures I called <code>MessageHolder</code> and used information on how they relate and how <code>MessageHolder</code>s are created/destroyed/modified in <code>NetworkManager</code> to find the encrypt function. I remember feeling like pulling my hair out doing this and jumping out of my chair in excitement when I finally found the encrypt function though.</p>\n<p>After hooking onto the encrypt and decrypt functions, I started logging the unencrypted packets as an array of bytes, hoping to maybe just eyeball some information. Yeah, that was incredibly naive. Packets were flying by so fast I couldn’t even read them before the next batch came in. I figured that there had to be a sort of opcode for the packets somewhere. I’d usually expect them as the first few bytes, but they were actually 2-byte opcodes starting on the 4th byte. I found this out later, but the first 4 bytes actually contained a 2-byte packet length that had to be decoded in a certain way. I’m not actually sure why they did this. Possibly for redundancy?</p>\n<p>For a while, I was grouping the packets by opcode and trying to connect them to an action I did, like opening a menu, moving, going to a map, etc. I could connect opcodes to actions, but the packet body was basically a complete black box. Great, I know that upgrading a skill send a packet with opcode <code>0x17c</code>, but what were the 6 bytes after? Skill ID? Amount of points invested? Maybe I could hazard I guess with such a small packet body, but I had no idea where to even start for longer bodies or variable length bodies.</p>\n<p>I was doing this inefficient, stupid method until I was reading the decompiled source of the binary and just happened to stumble upon a constant that matched an opcode I knew. I searched the surrounding functions and sure enough, I found the code for constructing the packet for that opcode. I didn’t find this earlier because I didn’t even know what to look for. I mean, sure I could try searching the entire binary for an opcode, but there would be way too many hits. I didn’t know what packet construction looked like structurally either and I couldn’t backtrace it easily from before the encrypt function because the code was passing through an obfuscation layer during the encrypt function. From there, I noticed a function that looked suspiciously like an initializer for something like a <code>Packet</code> struct, so I hooked onto it and verified that I was correct. Since the initializer was called without obfuscation, I was able to hook onto it and print out the opcode it was initialized with and the call stack to see the rest of the packet creation. Using this, I was able to efficiently reverse engineer many packets. Unfortunately the creation of many of the sensitive packets, like heartbeat packets and packets to send client information, were called through an obfuscation layer, but there still were a ton of useful packets that weren’t obfuscated.</p>\n<p>This applies to only outbound packets, of course. For inbound packets, I was never able to figure out how they were processed. I could find them being decrypted and I could make sense of some of the packets from just black-box reverse engineering, but after a considerable effort, I concluded that all of the inbound packet handling code on the client lived in obfuscation and there wasn’t any analog to the packet creation code for packet handling.</p>\n<h2>Real Hax</h2>\n<p>Eventually, I got a bit bored of playing with the network protocol and decided to play around a bit with writing useful hacks. This is where I ran into a lot of trouble. When I was scanning for pointers to things like HP, MP, player position, etc., I would frequently get disconnected for “suspicious behavior.” I wasn’t sure if my bypass was failing or if they had server-side checks with heuristics for suspicious client behavior. I couldn’t reasonably make anything useful when I was frequently getting disconnected and sometimes banned. So instead, I decided I’d try to update some of the cheats people had previously published. I did this by downloading older versions of the game and finding what the cheats were modifying in the old version and then trying to find the analogous code on the current version and mimicking it. This was pretty successful! With only some effort, I was able to dodge every attack, disable collision, jump multiple times, etc.</p>\n<p>Unfortunately, I would get banned a day or two after doing these more invasive cheats. Previously, I was just logging packets and I hadn’t been banned after I perfected the code. I was pretty confused at how I was getting caught because I figured that the packet logging should have been caught already. At a later time, I got into contact with another hacker who told me there was another integrity check I failed to bypass. I guess the basic bypass I had only allowed me to do certain things.</p>\n<h2>Disaster Strikes</h2>\n<p>As I was trying to figure out what was still getting me banned, my computer died, which lead to a wild goose chase lasting over a week trying to diagnose the problem through ordering new parts, testing them, failing, and repeating. The <em>last</em> thing I thought it would be was a CPU failure because I was booting into the BIOS fine and I could even boot into memtest. I replaced almost everything else — motherboard, power supply, GPU. The only things left of my original computer were my CPU and RAM. As I was testing my RAM to find out which ones I could keep in the new build and still crashing in the memtest, I noticed I was crashing on the same test. I searched up what that test did and it said it was a multi-core test. That’s when I finally realized it could be possible that my test results were consistent with one or more, but less than all CPU cores failing. Indeed, when I ran the memtest in single core mode, I didn’t crash.</p>\n<p>I have never even heard of this happening and it caused me such a headache because I couldn’t figure out what was going wrong with the computer. I was about to make the Computer of Theseus!</p>\n<p>During this period, I couldn’t continue on the project and I lost all of my momentum from the break. Later on, I got into contact with another hacker which is how I found out about what my bypass lacked. Unfortunately, I haven’t had the motivation to continue the project, so this is all I can write about for now. I wasn’t even going to write about this, but I figured I should complete the reverse engineering article series I started and give an update on where I was in regards to that. Be tuned for a part 2 in another… I don’t know, whenever I get bored enough again.</p>\n",
      "date_published": "2024-09-03T15:19:37Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/reversing-wings-of-vi-clickteam-fusion/",
      "url": "https://blog.mnke.org/reversing-wings-of-vi-clickteam-fusion/",
      "title": "Reversing Wings Of Vi/Clickteam Fusion",
      "content_html": "<p>In my last post, I mentioned how I had been reverse engineering Wings Of Vi for the past month. Part of this included digging into the decompilation output from Ghidra and figuring out how the game works. I left it out of my previous post because there was too much to discuss and postponed it for another post. Well, here’s that post. I was going to talk about my workflows and my overall journey to get where I am today, but I think that’d still be way too long. More importantly, I don’t even remember some of the earlier steps of my journey. Instead, I’ll be splitting this post into two parts: the first part will talk about what I discovered myself and the second part will talk about discoveries after I had some help.</p>\n<h1>Part 1: Solo Expedition</h1>\n<p>For the first couple of weeks, I spent almost all of my free time banging my head against the wall, making hypotheses on what some code did based on Ghidra decompilation output and iteratively refining my understanding. I went through a ton of the code and annotated what I thought things were doing, labelled function names, etc. until I was nearing my limit for what I could figure out. I’ll present what I found as I was nearing that limit. Keep in mind, I had no idea if any of this was really correct. After all, it’s not like there was any way I could truly verify any of this.</p>\n<p>Firstly, the game logic is run by what I called <code>UpdateObject</code>s. The game would iterate over these <code>UpdateObject</code>s and do various things commanded by them. Here’s what a typical <code>UpdateObject</code> looked like:</p>\n<pre><code>typedef struct {\n  short         size;  // add this to the address of the current UpdateObject to get the next\n  short         code1; // index into a jump table. usually it's 2\n  short         code2; // index into a second jump table.\n  char          unknown[2];\n  short         index; // index into an Entity[]\n  unsigned char flags1;\n  unsigned char flags2;\n  // rest is unknown. no defined size for this struct because it's variable length.\n  // minimum size is unknown, but is at least bigger than size up to flags2.\n  // often, there are &quot;Expression&quot;s at either offset 0x14 or offset 0x16 though.\n} UpdateObject;\n</code></pre>\n<p>Let’s go through how a typical <code>UpdateObject</code> might’ve been used. Using <code>code1</code>, a function from a jump table is called and the <code>UpdateObject</code> is passed in. Using <code>code2</code>, another function from another jump table is called and the <code>UpdateObject</code> is passed in. Let’s take a specific case where <code>code1 = 2</code> and <code>code2 = 3</code>. The resulting function after the two jump tables will:</p>\n<ul>\n<li>Find an <code>Entity</code> using the <code>index</code></li>\n<li>Compute some values using an <code>Expression</code></li>\n<li>Set the <code>Entity-&gt;y</code> to that value</li>\n</ul>\n<p>Actually, it’s a bit more complicated than that, but let’s focus on the <code>UpdateObject</code>s for now. The idea is that <code>code1</code> and <code>code2</code> encode a certain procedure for the <code>UpdateObject</code> and the <code>index</code> is what’s acted upon the procedure. The rest of the <code>UpdateObject</code> contains data on values (either directly stored or computed) that the procedure needs to act.</p>\n<p>Furthermore, <code>UpdateObject</code>s may not always be run. Let me give some pseudocode for what was happening:</p>\n<pre><code>void fun1(int, int) {\n  while (someCondition) {\n    UOChainHeader* header = ...;\n    UpdateObject* uo = header-&gt;updateObjects[0];\n    bool shouldExecute = ShouldExecuteJumpTable[uo-&gt;code1](uo);\n    if (shouldExecute) {\n      Globals-&gt;numUOInChain = header-&gt;numUOInChain;\n      ProcessUOChain(uo);\n    }\n  }\n}\n\nvoid ProcessUOChain(UpdateObject* uo) {\n  int i = 0;\n  UpdateObject* curr = uo;\n  while (i++ &lt; Globals-&gt;numUOInChain) {\n    UOJumpTable1[uo-&gt;code1](uo);\n    uo = (UpdateObject*)((int)uo + uo-&gt;size);\n  }\n}\n</code></pre>\n<p>Basically, <code>fun1</code> would iterate through <code>UOChainHeader</code>s, which contained contiguous chains of <code>UpdateObject</code>s. The first <code>UpdateObject</code> contained a condition (usually dynamically computed) that signals whether to continue executing the rest of the chain. It turns out this is actually slightly wrong, but it’s accurate enough for now.</p>\n<p>Let’s go back to how values are dynamically computed in <code>UpdateObject</code>s because this is fairly important. An <code>UpdateObject</code> may contain what I eventually called an <code>ExpressionHeader</code>. To compute the result of an <code>ExpressionHeader</code>, a global variable is set to the pointer of the start of the <code>ExpressionHeader</code> and then a function that I called <code>getMagicNumber</code> is called. <code>getMagicNumber</code> iterates through the payload of the <code>ExpressionHeader</code> and maintains a stack in a another global variable and operates on the stack. The payload within an <code>ExpressionHeader</code> contained <code>Expression</code>s which were variable length and iterated through a <code>size</code> field contained in the first few bytes. Using codes in these <code>Expression</code>s, we’d index into jump tables and operate on the stack. For example, an <code>Expression</code> with certain codes may contain an additional payload with information on how to index into the <code>Entity</code> array and find a certain value like an x coordinate and push it onto the stack. An <code>Expression</code> with another code may have a payload to index into a list of global values and push it onto the stack. Another <code>Expression</code> may compute the succeeding <code>Expression</code>, which will push a value onto the stack and then pop two values from the stack, and then add them. Other <code>Expression</code>s may even be used to compute sub-<code>Expression</code>s.</p>\n<p>That was maybe a bit confusing, so I think it would be really useful to illustrate with by transforming the <code>Expression</code>s into something more legible. We don’t want to think about the variable length aspect of <code>Expression</code>s and we’d like to transform the codes into something more semantically meaningful. Let’s represent some <code>Expression</code>s in JSON:</p>\n<pre><code>[\n// Get a variable from this entity, push it onto the stack\n{ name: &quot;GetEntityVar&quot;, entityIndex: 56, varName: &quot;health&quot; },\n// Compute the next Expression. Then, subtract the top two values on the stack\n{ name: &quot;Subtract&quot; },\n// Get this global variable, push it onto the stack\n{ name: &quot;GetGlobalVar&quot;, index: 12 }\n]\n</code></pre>\n<p>Much clearer, don’t you think? Much of reverse engineering software is transforming data into something with meaningful semantics and this is no exception. Obviously, this is a lot easier said than done. You should’ve seen me bang my head against the wall for days trying to figure this single function out.</p>\n<p>There are a lot more details on not only what I’ve talked about, but also things I haven’t talked about. I’m choosing to omit them because I think what I’ve discussed gives a clear enough picture of how the game operates at a high level: the game is actually like an interpreter. It processes directives through the form of <code>UpdateObject</code>s and <code>Expression</code>s and updates the game using those directives. This naturally leads us to ask: “Where are these directives?”</p>\n<h1>Part 2: Hitting my limit</h1>\n<p>So the hunt for where the <code>UpdateObject</code>s were stored began. The executable for the game was pretty large (90MB) and there wasn’t anything else in the installation directory, so I figured the data should be within the executable itself. Furthermore, I should be able to see where in the code this data is being loaded. I did find it, but it was part of a huge function that Ghidra had trouble decompiling and it seemed to do other things from a DLL that was also packed in the executable, extracted into a temp directory, loaded, and then used to transform the data.</p>\n<p>Long story short, I was unable to figure out how to take the data from within the executable and transform it to what’s used during the game’s runtime. During my weeks reversing the game, I had realized that the game was made with a tool called Clickteam Fusion. Desperate, I searched for tools to help with reversing Clickteam Fusion games and I found a tool called <a href=\"https://github.com/fnmwolf/Anaconda/tree/main\">Anaconda</a>. Honestly, I had my doubts about this tool because it was really poorly organized: copies of files, executables directly in the Git tree, build artifacts floating around, etc. After wrangling with getting it installed and changing some settings, I actually managed get some output though. It gave me a file that to open in the Clickteam editor but it wouldn’t load correctly. I wasn’t planning on continuing my reversing efforts in the Clickteam editor, but I wanted to confirm that this tool wasn’t giving me completely bad output so I got into contact with one of the authors of Anaconda and he very graciously gave me some tips to get it loaded. And… it actually worked. Well, I was still getting some errors when loading the file and there were clearly artifacts, but it was recognizable. Great, now that I had confirmation it mostly worked, I started digging through the Anaconda source code to see how it worked its magic.</p>\n<p>Wow. <em>I was right about so many things</em>. I mean, of course we had different names for the same thing, but it was clear that their <code>Event</code> referred to the <code>UOChainHeader</code> I talked about and their <code>Action</code>s corresponded to <code>UpdateObject</code>s. Hell, we both settled on the same name for <code>Expression</code>s! It’s hard to put into words how I felt reading that code. I had spent the better part of a month working blindly, unable to truly verify if <em>any</em> of my ideas were correct. Seeing confirmation made me so happy.</p>\n<p>In order to make sure I understood how the game was loaded (at least the <code>Event</code>s), I wrote a <a href=\"https://github.com/tonyd33/wingsofvi-reader/tree/master\">C program</a> to read the file that closely followed the Anaconda code. I think my code does a pretty good job of documenting how Clickteam games pack their data, so go read my code if you’re interested in it.</p>\n<p>The most trouble I had was figuring how to decoding and decompressing some parts of the data. In fact, I still don’t really understand how to decode some of the data, and neither does Anaconda. It just calls some C++ function that’s clearly the output of some decompiler (probably IDA). I couldn’t even manage to do that though. The decoding algorithm required a key that I was too lazy to figure out how to generate. I knew it was generated deterministically based on the contents of the executable, but I just hooked onto the key generation function and saved its output. Then, I tried to naively copy and paste the decoding algorithm from Ghidra’s output and use it in my function, but it just didn’t work. I tried to use <a href=\"https://godbolt.org/\">Compiler Explorer</a> and modifying the algorithm bit by bit to match the right assembly, but this was still really difficult because I don’t know what compiler was used, flags, etc. It would be really difficult to make it match. So I said screw it and just decided to copy/paste the assembly code as inline GCC assembly.</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/1jpYvUmvp--480.avif 480w, https://blog.mnke.org/img/1jpYvUmvp--659.avif 659w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/1jpYvUmvp--480.webp 480w, https://blog.mnke.org/img/1jpYvUmvp--659.webp 659w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/1jpYvUmvp--480.png\" alt=\"\" width=\"659\" height=\"405\" srcset=\"https://blog.mnke.org/img/1jpYvUmvp--480.png 480w, https://blog.mnke.org/img/1jpYvUmvp--659.png 659w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"></picture></p>\n<p>After wrestling with translating Intel assembly to AT&amp;T assembly and compiling my program in 32-bit, I checksummed my data and confirmed this actually worked.</p>\n<p>Anaconda contained another tool called Chowdren that was supposed to take a Clickteam game and transform it into C++. It didn’t work for Wings Of Vi even after I tried patching the errors that were popping up, but there was clearly something to it. I began to wonder how the author knew enough about the Clickteam runtime to build such a tool. I asked the author of Anaconda, but he said that he didn’t actually write Chowdren. I ended up finding another tool called <a href=\"https://github.com/CTFAK/CTFAK2.0/tree/master\">CTFAK</a> which seemed to be an updated rewrite of Anaconda alongside an active community working to decompile Clickteam games. I asked them if there was any documentation on the Clickteam runtime or if they knew anything about it and it turns out, <em>the source code for the runtime is just sitting in the Clickteam Editor’s install directory</em>.</p>\n<p>What the fuck? Are you telling me I’ve been working tirelessly on decoding bytecode piece by piece when there was perfectly legible source code out there?</p>\n<p>Well, okay. It’s not actually the source code for my runtime exactly, but it’s pretty damn close. It contained source code for the Android runtime in Java and XNA runtime in C#, so it wasn’t <em>exactly</em> the Windows runtime I was using, but all of the logic was clearly preserved when I compared the decompilation to the high level source. Furthermore, Clickteam published header files for some of their runtime structs. It’s not super easy to tell what the low-level structs look like from Java or C# code, so that supplementary material was extremely helpful. <em>Furthermore</em>, the person who gave this information even gave a CE structure file that represented most of the runtime headers!</p>\n<p>Well, I’ll be damned. That person also told me that I should’ve immediately asked around communities when I start reverse engineering projects. I probably will take that advice in the future but for this project in particular, I’m glad I didn’t do that. Staring at the disassembly and decompilation for hours and figuring things out that way is a skill I wouldn’t have learned otherwise.</p>\n<p>Now that I have the source code, how right was I about how the game ran? <em>Pretty fuckin’ right! I’m a BEAST!</em> Almost all of what I described above (before reading the source) was accurate. I didn’t describe a lot of other things, but those were accurate as well. Literally the only thing that I was wrong about was the conditions and it was a relatively minor mistake. It turns out, an <code>Event</code> (a.k.a a <code>UOChainHeader</code>) has multiple <code>Condition</code>s and <code>Action</code>s. I mistook <code>Condition</code>s and <code>Action</code>s for both being <code>UpdateObject</code>s. To be fair, they actually are the exact same, except a <code>Condition</code> has an extra <code>short id</code> at the end. That’s why I thought the <code>Expression</code> was either at offset 0x14 or 0x16. Additionally, I thought only a single <code>Condition</code> was checked before running the <code>Action</code>s. It turns out, there can be multiple <code>Condition</code>s within a single <code>Event</code>. The Ghidra output made that really unclear because there were tons of nesting, loops and <code>goto</code>s in the function.</p>\n<p>Out of the things I regarded as having figured out, I was almost always correct. There were plenty of things that I just didn’t get around to figuring out though, mostly because it took so much time. Either way, this gave me a lot of confidence that my methods for reversing binaries were effective and that the biggest optimization to my methods currently is to get faster at it.</p>\n<h1>Conclusion</h1>\n<p>So what now? I’ve more or less satiated my desire to figure the game out. I mean, I basically have access to the source code.</p>\n<p>I’ve been spending some time creating a <a href=\"https://github.com/tonyd33/RingsOfVi/tree/master\">modding API</a> by allowing hooking onto certain functions (e.g. action handlers, condition evaluators, etc.), and giving access to functions in the game like looking up an entity or getting a global variable, but I don’t think this is a great way to mod Clickteam games ultimately. Sure, I can get things done like this, but intercepting an action handler and deciding whether it should be run is not that easy. You have to figure out if you have the right action, which requires you to understand what the action does. Doing anything more than just blocking an action from running can be really difficult. Since the game is essentially an interpreter, the best way to mod the game will probably be to modify <em>what’s being interpreted</em>. My interest ends here though. I’m interested in reverse engineering, not making a modding tool for an out-of-date, obscure game engine.</p>\n<p>This post isn’t meant to be as informative as the previous post, so sorry if you made it all the way here and didn’t get much out of it. I just wanted to share my progress with low-level software reverse engineering. I’ve made strides in this area over the past month and a half, and I’m proud of how far I’ve come.</p>\n",
      "date_published": "2024-05-18T17:31:33Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/reverse-engineering-binary-applications/",
      "url": "https://blog.mnke.org/reverse-engineering-binary-applications/",
      "title": "Reverse Engineering Binary Applications",
      "content_html": "<h1>Puzzles, puzzles, puzzles!</h1>\n<p>I love taking things apart, playing around with what’s inside of them, and figuring out how it works. It’s like working on a giant puzzle, where you only know bits and pieces of the final picture based on how the assembled object behaves, and you have to fill in the missing parts based on deduction. Although my expertise in reverse engineering lies mainly in computer science, I think my love of the craft extends universally.</p>\n<p>Just last summer, I was taking apart my electric piano to restore color to its yellowed keys, and in doing so, I gained a pretty good understanding of how it worked. The piano could play notes softer or louder based on how hard I struck the key, and I’d wondered what the physical mechanism behind that was. Turns out, it was incredibly simple! Underneath the key, there were two ridges placed at difference offsets from the fulcrum with heights such that one ridge would strike a button before the next. These signals are sent to a controller that calculates the delta between the signal timings to calculate an amplitude, which is then sent to an audio interface.</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/A48il3XIPP-480.avif 480w, https://blog.mnke.org/img/A48il3XIPP-726.avif 726w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/A48il3XIPP-480.webp 480w, https://blog.mnke.org/img/A48il3XIPP-726.webp 726w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/A48il3XIPP-480.png\" alt=\"\" width=\"726\" height=\"935\" srcset=\"https://blog.mnke.org/img/A48il3XIPP-480.png 480w, https://blog.mnke.org/img/A48il3XIPP-726.png 726w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"></picture></p>\n<p>Well, presumably. It’s not like I pulled the machine code out of the controller and analyzed its disassembly. That might’ve been possible, but seeing the ridges and buttons underneath each key gave me enough confidence in my hypothesis.</p>\n<p>Anyway, I’m not actually here to talk about the mechanisms of an electric piano. I want to talk about my last month and a half disassembling a binary for an old game I used to play: Wings Of Vi. I’ll be talking about… well, a lot. I learned a lot. Methods to reliably find important addresses, how Windows loads and allocates memory in its virtual address space, assembly, function hooking, disassembling and decompiling, etc.</p>\n<p>My background in binary analysis had been pretty limited before this. The reverse engineering I’d done in the past involved looking at C# IL (intermediate language), minified JavaScript, or sometimes even code that other people had already reversed to a high-level language like C++. All of these were relatively easy to read and reason about. Reading disassembly is a huge step from that. But first, let’s go back to the start of my project.</p>\n<p>I’d like to say I had a concrete goal at the beginning like creating a cheat interface or a modding API for the game or something like that, but I really didn’t. At the very least, my goals fluctuated throughout the project.</p>\n<h1>Reversing Addresses</h1>\n<p>I began with a mini-goal to give myself infinite health. I launched Wings Of Vi, attached CE (Cheat Engine) to it, and started looking for my health address. This is done by:</p>\n<ol>\n<li>Getting a snapshot of the game’s memory. This snapshot must contain the character’s health.</li>\n<li>Changing my health by taking damage, healing, etc.</li>\n<li>Filter the snapshot of memory to increase/decrease/not change based on step 2. This snapshot also must contain the character’s health.</li>\n<li>Repeat from step 2 until there are few enough memory addresses. These memory addresses are candidates for my health address.</li>\n</ol>\n<p>I had a ton of trouble with this in practice. See, those steps assume that the memory address for your health doesn’t change throughout the iterative procedure. After all, if your health address changes from 0x12345 to 0x23456 during this, CE has no idea and is going to continue comparisons at 0x12345. The problem was that enemies hit too hard and there was so little healing that I couldn’t stay alive long enough. Once I respawned, I couldn’t be confident that my health address was still in the same place. How can we find the health address then?</p>\n<p>To answer this, we need to talk briefly about how things are actually stored in memory. As a programmer, I may define a struct like</p>\n<pre><code>struct Entity {\n  int x;\n  int y;\n  int health;\n}\n</code></pre>\n<p>When an <code>Entity</code> is instantiated, a region of contiguous memory is allocated to it, say at address 0x800. The <code>x</code> value is offset 0 from that address because it’s the first value in the struct. Then, since <code>int</code>s are 4-bytes long (yeah, this is technically decided by the compiler, but 4-byte <code>ints</code> are pretty typical), the <code>y</code> value is going to by at offset 4 from that address. Lastly, the <code>health</code> value is going to be at offset 8 from that address.</p>\n<p>Okay, so how does that help us find the health address? Well, we can hypothesize that the programmer might’ve defined the player entity in a similar way, meaning that the x and y coordinates of the player entity are close to the health address. If we can find the x or y coordinate of the player entity, we might be able to find the health address nearby, and the coordinates of the player entity are a <em>lot</em> easier to manipulate than the health!</p>\n<h2>Aside: Finding coordinates</h2>\n<p>Doing this can still be a little bit tricky in practice though, and I want to give a few quick tips.</p>\n<p>See, if you’re playing a 2D game, the y coordinate may increase as the player entity goes down because that coordinate system is often easier for computers to understand. This means you have to make some assumptions on the coordinate system if you’re trying to get the y coordinate. However, it’s almost always the case (from my experience) that x coordinates increase as the player goes right. For that reason, I recommend trying to find the x coordinate (which will often lead you to the y coordinate) for 2D games.</p>\n<p>For 3D games, I recommend the opposite. For convention, I’ll refer to the y coordinate to define the up and down of an object while the x and z coordinates define the planar position (the plane parallel to the ground). I’ve found that the y coordinate tends to increase as the player entity moves up in 3D games while x and z coordinates… well, there are too many degrees of freedom to begin making solid hypotheses. You can’t even figure out what directions are aligned with the planar axes most of the time, much less make any deductions on which direction increases the x or z coordinate.</p>\n<p>Since Wings Of Vi is a 2D platformer, I did a little dance left and right and eventually found my x coordinate. Browsing the memory region where the x coordinate lived on CE while moving around and taking damage, sure enough, my y coordinate and health were pretty close. They were less close than expected though. All of these values were defined as <code>doubles</code>, which took 8 bytes, but the values were spaced out 16 bytes instead, meaning there were an unknown 8 bytes in between each value. It was a bit odd and I took note of this, but didn’t think too much of it at the time (though I now understand why this is the case).</p>\n<h2>Static addresses and multi-level pointers</h2>\n<p>I “locked” my health with CE (really, it’s just CE rewriting the value over and over again really fast) and tried taking some damage. Nice! I wasn’t taking any damage. I’d already confirmed that my health address changes every time I respawn, meaning that I’d have to go through this whole ordeal every time to become invincible. Or do I?</p>\n<p>Let’s talk theory again. The addresses for player entity values does change on respawn, but the game clearly knows where these values live. This means the address for the player entity values itself has to be stored somewhere. Does that mean we should search in memory for the health address? Not quite. See, x86 (and probably other instruction sets) allow instructions to write at a memory address plus an offset. In the <code>Entity</code> example above, where the start of the struct is stored at 0x800, the instruction to set the entity health to 50 might look something like <code>mov [0x800+8], 50</code> rather than <code>mov [0x808], 50</code>. Furthermore, the 0x808 address for health is likely never stored anywhere and only stores the 0x800 address for the start of the <code>Entity</code>. If you think about it, this makes a lot of sense. The game knows that the <code>Entity</code> struct is at 0x800 and the health is at offset 8 from it. The game doesn’t need to remember the health address, only where the start of the <code>Entity</code> is, and that the health address is 8 bytes away from it.</p>\n<p>So, the idea is that we identify where our <code>Entity</code> struct starts and where the address to the <code>Entity</code> struct is stored, and hopefully this address holds the <code>Entity</code> address even after respawning. What if it doesn’t though? It could be that the player entity address is stored in a dynamically allocated array that’s reallocated on respawn. Okay, well something must hold the address to that array. We keep going up these level of pointers until we hopefully find a <em>static address</em>.</p>\n<p>Static addresses are exactly like they sound: static. Well, up to a certain point. Windows loads <em>modules</em> like an executable file or DLL by giving them a memory region for their data and code. Let’s say I have a program <code>MyApp.exe</code> that loads the <code>User32.dll</code> file to draw a window. When <code>MyApp.exe</code> is run, Windows will allocate a certain region of memory for it in the virtual address space, usually specified by a field in the PE (portable executable) headers of the executable. Then, it’ll load the <code>User32.dll</code> module in another region of memory. Static addresses refer to a fixed offset from the start address of a module, usually located in the <code>.data</code> section of the code. Often, this also translates to a completely static address because modules are loaded in the same way, but we shouldn’t rely on that. Fortunately, we don’t have to because the Windows API provides a function to get the start address of a module.</p>\n<p>This gives us a plan: follow the pointers and offset all the way up until we find a static address. Doing so will allow us to get the address of what we want through respawns and even through closing and reopening the game. Let’s take an example before we continue.</p>\n<ol>\n<li>I find my health address at 0x140008.</li>\n<li>I find instructions that access this address with <code>cmp [eax+8], 0</code> where <code>eax</code> is 0x140000. I conclude there is a struct at address 0x140000.</li>\n<li>I find that address 0x78100 holds the value 0x140000.</li>\n<li>I find instructions that access address 0x78100 with <code>mov eax, [ebx+0x100]</code> where <code>ebx</code> is 0x78000. I conclude there is a struct at 0x78000.</li>\n<li>I find that the <em>static</em> address 0x45500 holds the value 0x78000 and that 0x45500 is 0x5500 bytes away from the base address of the module.</li>\n</ol>\n<p>Then, we can find the health address by “undoing” the pointer chain we found. Here’s some C-style pseudocode to illustrate:</p>\n<pre><code>char* baseAddr    = GetModuleBase();\nchar* firstLevel  = *(char**)(baseAddr + 0x5500);  // dereference 0x45500, get 0x78000\nchar* secondLevel = *(char**)(firstLevel + 0x100); // dereference 0x78100, get 0x140000\nint   health      = *(int*)(secondLevel + 0x8);    // dereference 0x140008, get health\n</code></pre>\n<p>Putting this in practice, CE gives us a wonderful utility to see what instructions access a memory address, allowing us to figure out the offset, and CE lets us find exact values to see where addresses are being stored. It also highlights static addresses in green for us so we know when we’re at the end.</p>\n<p>Some tips around getting these multi-level pointers:</p>\n<ul>\n<li>Write the addresses and offsets down in the form of <code>addr2 + offset2 = &amp;addr1</code>. Seriously, it helps.</li>\n<li>The offsets given by the assembly instructions may be different for different instructions:\n<ul>\n<li>For example, one instruction might be <code>mov [eax+0x14], ebx</code> and another may be <code>mov [eax+0x38], ecx</code>. Note this and choose one offset to branch into. If it doesn’t work, come back and branch the other ways.</li>\n</ul>\n</li>\n<li>The offset given by the assembly instructions may be stored within a register:\n<ul>\n<li>For example, <code>mov [eax+ebx], ecx</code>. CE will tell you what was stored at <code>eax</code> and <code>ebx</code> on the first time the instruction was detected. The smaller number of <code>eax</code> and <code>ebx</code> will be the offset and the larger one will be the memory address. Usually, the first operand of the <code>+</code> is the memory address.</li>\n<li>To be sure, break on the instruction when the memory address is hit and confirm that the offset is always the same.</li>\n</ul>\n</li>\n<li>You may find multiple addresses that hold the address you want.\n<ul>\n<li>Try moving around, closing/opening menus, etc. to potentially filter out bad results and scan again.</li>\n<li>Otherwise, note this and continue branching one way. If it doesn’t work, come back and branch the other ways.</li>\n</ul>\n</li>\n</ul>\n<p>In reality, CE actually has this “pointermap” and “pointerscan” thing that’s supposed to help in this otherwise tedious process, but I haven’t really figured out how to use it. If this is something I’m going to do often, I’d probably look into that.</p>\n<h2>DLL injection</h2>\n<p>CE has built-in functionality to deal with these multi-level pointers so we can save the multi-level pointer to our health address and recover it upon closing/reopening the game/CE. This is kind of lame though: I want to be able to press a button to give myself +100 health. We can do this by creating an external or internal hack. An external hack is a program that uses the Windows API to read/write memory of another process. Comparatively, internal hacks run in the same process as the game and directly access the process memory. The advantage of internal hacks is that memory read/writes are a lot faster and hooking is a lot easier. I think a disadvantage of internal hacks is that games with anti-cheat can easily detect internal hacks. I haven’t written any hacks for games with anti-cheats yet, so I’m not sure. Either way, let’s talk about how to create an internal hack.</p>\n<p>The idea behind the internal hack I’m about to discuss is that, we make the game process call a function to load our code in another thread by loading a library — our DLL. Our DLL runs on its own thread and does whatever it wants to the game. This method is called DLL injection. The Windows API exposes the two important functions:</p>\n<ol>\n<li><code>CreateRemoteThread</code> — Creates a thread that runs in the virtual address space of another process.</li>\n<li><code>LoadLibrary</code> — Loads the specified module into the address space of the calling process. The specified module may cause other modules to be loaded.</li>\n</ol>\n<p>All we have to do is make <code>CreateRemoteThread</code> call <code>LoadLibrary</code> within the remote process and load our DLL. There’s a little bit of complication in that <code>LoadLibrary</code> needs to accept the path to our DLL as an argument, and we can’t just use a string that’s allocated in the program we’re injecting from because <code>LoadLibrary</code> only has access to the remote process’ memory space. A call to <code>VirtualAllocEx</code> to allocate some memory in the remote process and <code>WriteProcessMemory</code> to write our string to that remote process’ memory makes it work though. Altogether, our injector might look something like:</p>\n<pre><code>char* dll = &quot;path\\\\to\\\\dll.dll&quot;;\nunsigned length = strlen(dll) + 1;\n\n// find process\nDWORD procId = GetProcId(&quot;Game.exe&quot;);\nHANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, procId);\n\n// write the argument to pass to LoadLibrary in remote process memory\nvoid* remoteStr = VirtualAllocEx(hProc, 0, length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);\nWriteProcessMemory(hProc, remoteStr, length, 0);\n\nHANDLE hThread = CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, remoteStr, 0, 0);\nCloseHandle(hThread);\nCloseHandle(hProc);\n</code></pre>\n<p>I don’t actually use this because CE has a DLL injector built in, but I think it’s important to understand how it works. I’m pretty sure it’s more advanced than what I wrote, but it should follow the same idea: call <code>LoadLibrary</code> within the remote process.</p>\n<p>From within the DLL, we’d create a thread to run on, likely an <code>AllocConsole</code> in case we need to print any debug information, and likely some <code>GetAsyncKeyState</code> calls to respond to the keyboard. Then, we can use the code above to unravel the multi-level pointers to read/write whatever we want.</p>\n<p>All of this can probably get you decently far in modifying the game at runtime, but I wanted to <em>really</em> dig in deep. I wanted to figure out how the main game loop worked. I wanted to know what entities existed, where in memory they live, and how they run. To do this, we need a decompiler.</p>\n<h2>Decompilation</h2>\n<p>The only decompilation options I know of are IDA and Ghidra. IDA is a proprietary software made by HexRays and it comes in a free and a pro version, and some others. The free version is apparently lacking in tons of important features but the pro version costs literally thousands of dollars, which is way too expensive for a hobbyist like me. Ghidra is an open source decompiler created by the NSA. It’s apparently nowhere near as polished as IDA Pro though. I did try IDA Free for a little while, but honestly, I just didn’t know how to use it well and I was somewhat discouraged to learn because I knew I was missing important features and tutorials were usually made in IDA Pro. So I switched to Ghidra, and despite people’s criticisms, I found it incredibly easy to pick up.</p>\n<p>To get started, I decided to break on the instructions that wrote my coordinates in CE and follow the stack traces up until I could no longer step out of the stack because I was in a while loop. With the Ghidra decompilation on the side, I began to figure out the code by making hypotheses from more readable Ghidra decompilation output, test them in the game with CE and repeat. Ghidra’s auto-struct function, re-labelling, and re-typing started making the code more and more readable. Over the course of the next couple of weeks, I would read the disassembly/decompilation and figure out much of the code.</p>\n<p>Some of the tools that were incredibly helpful during this time were CE’s structure dissector and a tool called <a href=\"https://github.com/ReClassNET/ReClass.NET\">ReClass</a> to let me reverse structs. It would attach into the game and you could enter in memory addresses and give addresses types like declaring that offset 0x4 from an address is a <code>short</code>, or offsets 0x20 to 0x40 contained a string. Importantly, it let me visually see the data in multiple forms and made guesses to what type it thought an address was which might’ve been difficult in static analysis.</p>\n<p>Seeing how long this post is getting, I’m deciding to dedicate another post to purely my decompilation findings. Stay tuned for the next post. It’ll be packed to the brim with how the game is implemented.</p>\n<h1>Low-level Function Hooking</h1>\n<h2>A naive approach</h2>\n<p>Another tool I picked up during this time was function hooking. This tool extends far beyond just helping me figure out the code, so I’m going to spend a bit of time talking about it. You’ve probably at least heard of function hooking and have some idea of it might be: we “hook” onto a function A by executing our own function B whenever function A runs. This is the high level idea. This is really not too difficult to do in higher level languages: you’d probably just write a line at the start of the target function that calls your function:</p>\n<pre><code>void Target(int a) {\n  MyFunc(a);\n  // original function\n}\n</code></pre>\n<p>When you’re working with bytecode, you can’t just do this though. Let’s think this through on a x86 32-bit (this is what I was working with, but obviously everything translates pretty easily into 64-bit). Suppose we’ve created a valid assembly function within the process’ memory at address 0x12345. The start of the target function looks like this:</p>\n<pre><code>1: push ebp\n2: mov ebp, esp\n3: mov eax, dword ptr [ebp+0x8]\n4: cmp word ptr [eax+0x10], 0x18\n5: ...\n</code></pre>\n<p>We can’t just insert before the first line of the function to <code>call 0x12345</code> or <code>jmp 0x12345</code>! We’d have to shift <em>the entire rest of the code below this function down!</em> As if that wasn’t impractical enough, certain <code>jmp</code> instructions may rely on some code being at a certain offset, which a shift like this would completely destroy.</p>\n<p>Okay, let’s be a <em>little</em> smarter. How about we just overwrite the first instruction to jump to our code? So something like:</p>\n<pre><code>1: jmp 0x12345\n2: mov ebp, esp\n3: mov eax, dword ptr [ebp+0x8]\n4: cmp word ptr [eax+0x10], 0x18\n5: ...\n</code></pre>\n<p>There are multiple major problems with this. Firstly, x86 assembly instructions are variable length. A <code>push ebp</code> instruction is only 1 byte long while the <code>jmp 0x12345</code> is at least 5 bytes long. I’ll annotate the instruction lengths to demonstrate:</p>\n<pre><code>1: push ebp                       // 1 byte\n2: mov ebp, esp                   // 2 bytes\n3: mov eax, dword ptr [ebp+0x8]   // 3 bytes\n4: cmp word ptr [eax+0x10], 0x18  // 5 bytes\n5: ...\n</code></pre>\n<p>Let’s fix that first by writing exactly 5 bytes for the <code>jmp</code> instruction.</p>\n<pre><code>1: jmp 0x12345\n2: or byte ptr [esi-0x7d], ah\n3: js 0x1a\n4: ...\n</code></pre>\n<p>Wait, what happened? Well, we can’t write <em>only</em> 5 bytes! By writing exactly 5 bytes, we mangled the 3rd instruction! We have to write at least 5 bytes, plus however more bytes aligns us to the next instruction. Fortunately, a nop (no-operation) instruction is 1 byte, so we can use it as padding. If we write the 6 bytes correctly, we should get:</p>\n<pre><code>1: jmp 0x12345                    // 5 bytes\n2: nop                            // 1 byte\n4: cmp word ptr [eax+0x10], 0x18  // 5 bytes\n5: ...\n</code></pre>\n<p>Alright, but this still has one minor problem and one major problem. The minor problem is that we have to make sure that we jump back to the correct address after executing our function. Suppose we do that. Then, the major problem is that the stack is usually very delicately and meticulously handled by the compiler and the instructions we overwrote modify the stack. If we naively just ran our function we’d destroy certain stack invariants and crash the process almost immediately. Trust me, I know from experience. So before we transfer control back to the target function, we have to be absolutely sure that the stack is in the condition it would’ve been in if we didn’t have this <code>jmp</code> instruction. The easiest way to do that is (assuming the stack is as the same as when we initially jumped to our code) to execute the overwritten instructions.</p>\n<p>How do we actually write code for our function though? Sure, we can write raw assembly and make our program jump to that raw assembly, and the MSVC compiler even supports pretty decent inline assembly:</p>\n<pre><code>void __declspec(naked) FakeFunction() {\n  __asm {\n    mov eax, [ebx]\n    cmp eax, 1\n    // ...\n    // run the original instructions\n    jmp 0x98789 // jump back to where we came from\n  }\n}\n</code></pre>\n<h2>Refinement</h2>\n<p>This kind of sucks though. It’s super hard to get anything meaningful done with pure assembly. Not only that, it’s still easy to just mess up the stack! If we call a normal, non-naked function, chances are, we’re going to ruin the stack. After a bit of research and seeing how the <a href=\"https://github.com/microsoft/Detours\">MS Detours library</a> does it, this is what I came up with:</p>\n<pre><code>0x10: call TargetFunction\n0x15: mov [eax], ebx\n\n// Get the function signature through Ghidra decompilation. Usually reliable enough\n// int __cdecl TargetFunction(int, int)\n0x50: jmp 0x12345 // we wrote this. jumps to code we wrote\n0x55: nop\n0x56: cmp word ptr [eax+0x10], 0x18\n...\n0x99: ret\n\n// below is our code\n\n// match the original function signature\ntypedef int(__cdecl* TargetFunctionT)(int, int);\n\nTargetFunctionT RealFunction = 0x12349;\n\n// match it here too\nint __cdecl FakeFunction(int a, int b) {\n  printf(&quot;hooked before. args are %d, %d\\n&quot;, a, b);\n  int ret = RealFunction(a, b);\n  printf(&quot;hooked after. args are %d, %d\\n&quot;, a, b);\n  return ret;\n}\n\n// &quot;trampoline&quot;\n0x12345: jmp &amp;FakeFunction\n0x12349: push ebp\n0x1234A: mov ebp, esp\n0x1234D: jmp 0x56        // go back to where we came from\n</code></pre>\n<p>Wait, it might be better if I explained with a picture.</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/hlriwxN9VW-480.avif 480w, https://blog.mnke.org/img/hlriwxN9VW-960.avif 960w, https://blog.mnke.org/img/hlriwxN9VW-1271.avif 1271w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/hlriwxN9VW-480.webp 480w, https://blog.mnke.org/img/hlriwxN9VW-960.webp 960w, https://blog.mnke.org/img/hlriwxN9VW-1271.webp 1271w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/hlriwxN9VW-480.png\" alt=\"\" width=\"1271\" height=\"675\" srcset=\"https://blog.mnke.org/img/hlriwxN9VW-480.png 480w, https://blog.mnke.org/img/hlriwxN9VW-960.png 960w, https://blog.mnke.org/img/hlriwxN9VW-1271.png 1271w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"></picture></p>\n<p>… don’t look at me like that.</p>\n<p>Seriously, I do think this visualisation makes a good trade-off between showing what’s actually happening and minimizing the complexity. I’ve simplified the <code>jmp</code> and <code>call</code>s to be the near variants for illustration, but this is actually what’s happening.</p>\n<p>The idea is that we “hijack” the target function by jumping to our function immediately through a “trampoline”. The function we jump to immediately must match the target function’s calling convention <em>exactly</em>, including the calling convention so we don’t mess the stack up. The rest of the trampoline is dedicated to pretending to be the real function. If you follow address 0x12349 through, you’d be running the exact same code as the original target function. That’s what’s stored into <code>RealFunction</code>.</p>\n<h2>Decoration: Injecting arguments to hook</h2>\n<p>In the codebase I was reversing, there are many instances where a function is called through a <em>jump table</em>. I don’t actually know if this is a widely accepted term for what I’m going to describe, but it makes sense to me so I’m rolling with it. What often happens is that a “type” is specified in the first few bytes of a struct. This “type” is used to index into an array of function pointers located at a static address, and the appropriate function is called using the type. Naturally, each of these functions has the exact same function signature. I want to hook onto each of these functions. Sure, I could write <code>FakeFunction1</code>, <code>FakeFunction2</code>, etc., but I’d ideally like to have a single <code>FakeFunction</code> that handles all of these functions and calls the original function. How would <code>FakeFunction</code> know which one to call though? In some cases, the struct pointer with the type was passed into the function, so I could use the type and figure it out, but I didn’t want to rely on that in cases where I couldn’t do that.</p>\n<p>So the goal is to inject an extra argument for our <code>FakeFunction</code> to receive. This is actually <em>super hard</em> in practice and I haven’t pulled it off, but in theory, you’d obviously need to add another argument to your function signature, and then you’d have to do something like this at the start of the trampoline (assuming a <code>__cdecl</code> call with originally 1 argument):</p>\n<pre><code>push esp            // in case we mess up\npush eax            // save eax on the stack so we can use it worry-free.\nmov ebx, [esp+0x8]  // this will be the first argument to the function (32-bit stack entries)\npush something      // this is the argument that will be injected. second argument\npush ebx            // add the original argument. first argument\ncall FakeFunction\nadd esp,0x8         // get rid of the two arguments we used\npop eax             // restore eax\npop esp             // restore esp just in case\n...                 // run original code here\njmp returnAddr      // go back to where we came from\n</code></pre>\n<p>The downside of this is that we’re calling our <code>FakeFunction</code> instead of jumping to it, meaning we can’t just skip running the target function (jumping basically completely hijacks the target function and allows us to early return). Yes, we can set up the trampoline in a way that we rearrange the stack and push an extra argument and jump to our <code>FakeFunction</code>, but the call to <code>TargetFunction</code> (and thus <code>FakeFunction</code>) may expect the stack pointer to be at some location, and the extra <code>push</code> instruction causes it to be a couple bytes higher than expected and destroys stack invariants. Of course there are still other ways to do an early return like storing a boolean that the trampoline reads when it returns from the <code>call</code>, but this is already getting out of hand.</p>\n<p>Not only that, but handling different function signatures is a huge pain. I don’t know if that code works in the first place, but it <em>definitely</em> wouldn’t work if the target function had 2 arguments, or 3 arguments, or anything other than 1!</p>\n<p>Ultimately, the best solution I came up with was to:</p>\n<ol>\n<li>Create a global variable <code>int extra;</code></li>\n<li>Create N trampolines, one for each function in the jump table</li>\n<li>Each trampoline would additionally set the global variable to a key (this key can even be dynamic, but that’s not useful for our purposes)\n<ul>\n<li>For example, the first trampoline would set the global variable to 1</li>\n<li>The second would set the global variable to 2</li>\n<li>etc.</li>\n</ul>\n</li>\n<li>Save a list of where the real functions can be found</li>\n<li>Use the global variable and get the key to index into the list of real functions when <code>FakeFunction</code> is called so it can call the real function</li>\n</ol>\n<p>This actually works really well. I created a mini-framework for this, and it’s been holding up.</p>\n<h2>Mid-function hooking?</h2>\n<p>Aside from this, I’ve also messed around with mid-function hooking, but this is generally really difficult to get right and not even that useful most of the time. I originally wrote a hooking library tailored to mid-function hooking while still allowing start-of-function hooking because I thought it’d be cool, but I just didn’t find myself using the mid-function hooking functionality that often. If you want to do something like skipping an instruction mid-function or saving a variable to a global list you manage, you don’t get much privileges of being able to write high-level code because you risk mangling the stack — and even register values (although this can usually be made a bit safer with <code>pushad</code> and <code>popad</code> and pushing/popping FP registers before and after transferring control flow, respectively). Most of the time, start-of-function hooking is just good enough.</p>\n<h1>Conclusion</h1>\n<p>Well, I don’t know what to put here. I don’t really have a conclusion since this post was just an assortment of learnings I’ve had over the past month and a half. I’ve had a ton of fun with playing around with this stuff at such a low level. This knowledge unlocks so many options and I’m excited to put it to use. If it’s not clear, I don’t condone abusing this knowledge to cheat in online games. I think the tone and content of my discussion hinted toward my technical interest rather than an interest to cheat. Cheating in singleplayer games is totally okay (and I like doing it to understand the game), but if you cheat in online games to pretend like you’re good, you’re super lame.</p>\n",
      "date_published": "2024-05-16T16:57:03Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/problem-solving-with-vim-advent-of-code-day-1/",
      "url": "https://blog.mnke.org/problem-solving-with-vim-advent-of-code-day-1/",
      "title": "Problem Solving with Vim: Advent of Code Day 1",
      "content_html": "<p>A while back, I had seen someone do some <a href=\"https://youtube.com/playlist?list=PL6XBsLhMC_hmw8eKhlrC8k7FfmNzDLw80&amp;si=myKrKHufZH0l4Gfo\">insane things with Vim</a>: things that really should be done in a programming language but is somehow done in Vim. I was inspired, but not enough to go out of my way to find problems to solve in Vim. Fast forward to yesterday, when a friend messaged me his solution to the day 1 Advent of Code problem in an esolang. That gave me the idea to try solving it in Vim, trying to avoid deferring to vimscript or executing outside programs as much as possible — I wanted to solve things almost purely by manipulating the text.</p>\n<p>Today, I gave <a href=\"https://adventofcode.com/2023/day/1\">day 1</a> a shot. The first part was really simple, even in Vim. You were given a newline-separated file of alphanumeric strings and all you had to do was take each line, form the first and last numbers of the line into a two digit number and then add up all the numbers. In Vim, that just meant</p>\n<ol>\n<li>Delete all non-digit characters with <code>:%s/\\D//g</code></li>\n<li>Record a macro to copy the first and last digits into separate registers with <code>0v&quot;ay$v&quot;by</code> and then paste it with <code>&quot;ap&quot;bp</code></li>\n<li>Run the macro on each line</li>\n<li>Replace every newline character with <code>+</code> using <code>:%s/\\n/+/g</code></li>\n<li>Delete the trailing <code>+</code>, cut the entire line into the register with <code>0C</code>, and then use expression mode to evaluate the register content with <code>&lt;C-R&gt;=&lt;C-R&gt;&quot;</code></li>\n</ol>\n<p>Where things got a bit more difficult was in part 2. First, you had to convert spelled out digits like <code>one</code> into <code>1</code> before doing the same thing as part 1. I wanted to just run <code>%s/one/1/g, %s/two/2/g, ...</code>, but the example showed that words could overlap: you could get words like <code>xtwone3four</code>, which should get converted into <code>24</code>, but obviously doesn’t with the naive substitutions I wanted to do. Additionally for a more illustrative example, something like <code>1twone</code> should come out as <code>121</code>, and then <code>11</code>, which I didn’t realize until a while later.</p>\n<p>The first problem I was thinking about was how can we even know in Vim that we should substitute the <code>two</code> in <code>xtwonefour</code> first before the <code>one</code>. I didn’t end up exactly figuring that out, but it was no matter because I realized that ultimately, all we need to do is resolve the first and last spelled out word and we could ignore everything in between. This is what we do to get <code>xtwonefour</code> into <code>x2ne4</code>:</p>\n<ol>\n<li>Yank the line with <code>yy</code> and paste 8 copies with <code>8P</code></li>\n<li>On copy N, substitute spelled out digit N with the first occurrence of the actual digit and ignore errors for the macro (e.g. on copy 1, do <code>:s/one/1/e</code>)</li>\n<li>Select all 9 lines and sort them</li>\n<li>Either all 9 lines are the same or the first line will be the one with the digit replaced the earliest, which is what we want. So delete the 8 copies below.</li>\n</ol>\n<p>So now we’re at <code>x2nefour</code>. Resolving the last spelled out word is very similar to doing it for the first:</p>\n<ol>\n<li>Yank the line with <code>yy</code> and paste 8 copies with <code>8P</code></li>\n<li>On copy N, substitute spelled out digit N with the <em>last</em> occurrence of the actual digit and ignore errors for the macro (e.g. on copy 1, do <code>:s/.*\\zsone/1/e</code>)</li>\n<li>Select all 9 lines and reverse them (unfortunately, I did use <code>!rev</code> here)</li>\n<li>Select all 9 lines and sort them</li>\n<li>Either all 9 lines are the same or the first line will be the one with the digit replaced the <em>latest</em>, which is what we want. So delete the 8 copies below.</li>\n<li>The line we have is still reversed, so reverse it again to get it back to original order.</li>\n</ol>\n<p>Now we have <code>x2ne4</code> and so we can just delete non-digits and do what we did for part 1.</p>\n<p>This works for almost all lines, but there’s a special case. For lines with only a single set of two overlapping number words, this fails. Let’s take a look at <code>3twonep</code> for example, which should convert into <code>31</code>. With the first set of steps, we’d get <code>32nep</code> and then… oh we lost the <code>one</code> to replace to <code>1</code>. After scratching my head a bit on how to resolve this, I came up with this.</p>\n<ol>\n<li>Make two copies of the line (e.g. <code>3twonep\\n3twonep</code>)</li>\n<li>On the first copy, resolve the first word according to above (e.g. <code>32nep\\n3twonep</code>)</li>\n<li>On the second copy, resolve the last word according to above (e.g. <code>32nep\\n3tw1p</code>)</li>\n<li>Join the copies onto a single line (e.g. <code>32nep3tw1p</code>)</li>\n<li>Delete non-digits and do what we did for part 1 (e.g. <code>32nep3tw1p</code> -&gt; <code>3231</code> -&gt; <code>31</code>)</li>\n</ol>\n<p>This works! It works because we don’t care about what’s happening in the middle of the line. The first digit will always be either already a digit or resolved by us from the first set of steps, and we’re still guaranteeing that the last digit of the combined line will also either already be a digit or resolved by us from the second step of steps, and it doesn’t matter that we have all that junk in the middle.</p>\n<p>I haven’t felt so happy after solving a puzzle in a long time. From realizing the trick with sorting to extract the first replacement of a digit to realizing the trick with reversing to extract the last replacement to realizing I could just parallelize and then combine the two tricks, I was really forced to think out of the box in a dramatically different way due to my unusual and limited toolset, and I managed to do it. Here’s my full solution with some annotations.</p>\n<pre><code>Go^[                 &quot; add a trailing line to the file. \n                     &quot; For some reason, the macro below fails if there isn't another line\n                     &quot; below the one it's operating on.\ngg0\nqqyy9P               &quot; First set of steps\n:s/one/1/e           &quot; Replace first occurrences\nj:s/two/2/e\nj:s/three/3/e\nj:s/four/4/e\nj:s/five/5/e\nj:s/six/6/e\nj:s/seven/7/e\nj:s/eight/8/e\nj:s/nine/9/e\nV8k:sort            &quot; Sort\njV7jd               &quot; Delete copies\nyy8P                &quot; Second set of steps\n:s/.*\\zsone/1/e     &quot; Replace last occurrences\nj:s/.*\\zstwo/2/e\nj:s/.*\\zsthree/3/e\nj:s/.*\\zsfour/4/e\nj:s/.*\\zsfive/5/e\nj:s/.*\\zssix/6/e\nj:s/.*\\zsseven/7/e\nj:s/.*\\zseight/8/e\nj:s/.*\\zsnine/9/e\nV8k!rev             &quot; Reverse\nV8j:sort            &quot; Sort\njV7jdkV:!rev        &quot; Delete copies, reverse remaining line back to original\nkJr,jq              &quot; Combine lines, end macro\nVGk:norm @q         &quot; Run macro on rest of lines\n:%s/\\D//g           &quot; Delete digits\ngg0\nqw                  &quot; Record macro into w\nv&quot;ay$v&quot;by           &quot; Copy first character into register a, last character into register b\n0D&quot;ap&quot;bp            &quot; Delete the entire line, paste from register a, paste from register b\nj0q                 &quot; Go to start of next line, end macro\n:%norm @w           &quot; Run on all lines\n:g/^\\s*$/de         &quot; Remove all empty lines, ignore errors\n:%s/\\n/+/g          &quot; Replace newlines with +\n$x                  &quot; Delete trailing newline\n0C                  &quot; Cut line into default register\n&lt;C-r&gt;=&lt;C-r&gt;&quot;        &quot; Evaluate expression in default register \n&lt;ESC&gt;\n\n</code></pre>\n<details class=\"wp-block-details\"><summary>If you want to run it, here it is.</summary><pre class=\"wp-block-code\"><code>R28bZ2cwcXF5eTlQOnMvb25lLzEvZQ1qOnMvdHdvLzIvZQ1qOnMvdGhyZWUvMy9lDWo6cy9mb3Vy\nLzQvZQ1qOnMvZml2ZS81L2UNajpzL3NpeC82L2UNajpzL3NldmVuLzcvZQ1qOnMvZWlnaHQvOC9l\nDWo6cy9uaW5lLzkvZQ1WOGs6c29ydA1qVjdqZHl5OFA6cy8uKlx6c29uZS8xL2UNajpzLy4qXHpz\ndHdvLzIvZQ1qOnMvLipcenN0aHJlZS8zL2UNajpzLy4qXHpzZm91ci80L2UNajpzLy4qXHpzZml2\nZS81L2UNajpzLy4qXHpzc2l4LzYvZQ1qOnMvLipcenNzZXZlbi83L2UNajpzLy4qXHpzZWlnaHQv\nOC9lDWo6cy8uKlx6c25pbmUvOS9lDVY4ayFyZXYNVjhqOnNvcnQNalY3amRrVjohcmV2DWtKcixq\ncVZHazpub3JtIEBxDTolcy9cRC8vZw1nZzBxd3YiYXkkdiJieTBEImFwImJwajBxOiVub3JtIEB3\nDTpnL15ccyokL2RlDTolcy9cbi8rL2cNJHgwQxI9EiINGwoK\n</code></pre><p>It’s base64 encoded because I can’t paste things like ESC (<code>^[</code>). To run it, save that encoded file somewhere, run <code>base64 -d file &gt; solution</code> and then <code>vi -s solution input.txt</code>.</p></details>\n",
      "date_published": "2023-12-02T19:29:04Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/reverse-engineering-minesweeper-online-to-electrocute-myself-and-more/",
      "url": "https://blog.mnke.org/reverse-engineering-minesweeper-online-to-electrocute-myself-and-more/",
      "title": "Reverse Engineering minesweeper.online To Electrocute Myself — And More",
      "content_html": "<p>I had an insane Minesweeper phase where I could not stop binge-playing the game. I would literally see Minesweeper patterns in my head and imagine solving them when I wasn’t playing. I’ve since become less fanatical and now only play Minesweeper on occasion. In fact, my personal best (69 seconds on expert) was accomplished during a short return to the game, which tells me I’m far from reaching my true limit, so I’ll be back for more.</p>\n<p>In any case, during my Minesweeper escapades, I started playing on a site called Minesweeper Online due to its exclusive “No Flag” mode, where you’re only given boards that you can always deterministically solve. The site is simple with regards to the modern web, but maybe a bit much for Minesweeper with all the collectibles and gems and events and whatnot. I actually wanted to implement my own Minesweeper with my own No Flag mode and solver for fun. I tried doing that until it was no longer fun. Also, shoutout to Minesweeper Online for giving me premium for life after reporting a minor bug. I appreciate you guys.</p>\n<p>A couple of years ago, I had an idea to shock myself as punishment when I failed at something in video games, inspired from a video I watched. I thought it would be pretty funny to strap myself into a figurative electric chair while I played Minesweeper and shock myself whenever I clicked a bomb, and frame the entire thing as a “science experiment” on whether shock punishment was effective for as a conditioning mechanism for improving at Minesweeper. My friend and I jerry-rigged a dog shock collar with a Raspberry Pi to allow shocks to be delivered through an HTTP request on the Pi. I needed a Minesweeper client to hack around with so I could hook up clicking bombs to sending the request to the Pi, but the ones publicly available and easily findable and open source had some peculiarities that made it function slightly different from the way Minesweeper Online did, which I had gotten very used to. So, I figured I’d just mess with the Minesweeper Online browser client.</p>\n<p>I found that the Minesweeper Online client communicated with the server via a websocket, but the message formats they used were incredibly cryptic. Fortunately, it wasn’t too hard to identify the packets that signified the game was over, but I still had to figure out how to intercept their message and tell the Pi to shock me. I was still kind of new to web development at the time, so after a good amount of searching and head scratching, I found a way to override the default <code>WebSocket</code> class in JavaScript by loading some code before the page loaded using Tampermonkey and forcibly added custom event listeners to every new instance of <code>WebSocket</code> to handle message received events. I must have been really proud of the code because I posted this code into a PasteBin and named it <a href=\"https://pastebin.com/rVPvgCNi\">“fuck shit piss taser”</a> for some reason. You don’t even have to know anything about programming to understand how cursed this code is just from seeing these lines:</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/bx1fA0yris-426.avif 426w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/bx1fA0yris-426.webp 426w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/bx1fA0yris-426.png\" alt=\"\" width=\"426\" height=\"193\"></picture></p>\n<p>This actually worked. With much reluctance from me and much goading from my friend, I did a few test runs with the dog shock collar on my leg. It didn’t take many fails to come to a conclusion for the science experiment. By shocking yourself when you fail, you actively want to avoid failure at all costs. I mean, we can joke about shocking yourself for fun, but if you’ve ever had a shock device strapped to you and you have the controls to it, your body actively refuses to press the button to actually go through with it, and it takes a moment of pure resolve — and idiocy — to override your survival instincts and press the button. So, in terms of improving performance, your pace grinds to a complete halt as you double-check and triple-check every single tile before you make a move. <em>Maybe</em> you could say that this improves your accuracy at the cost of your speed, but frankly, it just made me scared to play Minesweeper because I knew I’d still have to make guesses on some boards. Did I need to tell you all that? Hopefully not. But now you know 🙂</p>\n<p>All of that was a little side story to bring us to where we are today. I’ve been learning iOS development with Swift recently and also wanted to improve my reverse engineering skills so I figured it might be a neat little project to make a mobile client for Minesweeper Online. While I haven’t made the mobile client yet, I have made very solid progress on reverse engineering the messages and I’ll be detailing them for my own reference as well as for educational purposes.</p>\n<p>To start, the client requests a session id with a <code>GET /authorize?session=</code>. The server replies with JSON message with the following format:</p>\n<pre><code>\n{\n    &quot;authKey&quot;: &quot;xxxx&quot;, // 128 bit hex string\n    &quot;session&quot;: &quot;xxxx&quot;, // 128 bit hex string\n    &quot;userId&quot;: 1234,\n    &quot;user&quot;: {\n        &quot;createdAt&quot;: &quot;2023-11-21T19:46:10.730Z&quot;,\n        &quot;country&quot;: &quot;CA&quot;,\n        &quot;id&quot;: 1234,\n        &quot;hidden&quot;: 0,\n        &quot;premiumTo&quot;: null,\n        &quot;patronState&quot;: null,\n        &quot;storage&quot;: null,\n        &quot;hasVIP&quot;: 0\n    },\n    &quot;geolocation&quot;: [\n        lat,\n        lng\n    ],\n    &quot;serverTime&quot;: 1700595970731,\n    &quot;isNewUser&quot;: true\n}\n</code></pre>\n<p>The session id is then stored on the browser. If we wish to retrieve these details again, we can just call the same endpoint and fill in the session id query.</p>\n<p>Next, I believe the client uses the geolocation from the <code>/authorize</code> endpoint as well as a list of servers in some JavaScript to determine the closest server, usually with a name like <code>los1</code> or <code>atl1</code>. You can find how the server is determined in their JavaScript, but we may as well hardcode it. After the server name is obtained, we call <code>GET https://${server}.minesweeper.online/mine-websocket/?authKey=${authKey}&amp;session=${session}&amp;userId=${userId}&amp;transport=polling</code>. The response will be something like</p>\n<pre><code>94:0{&quot;sid&quot;:&quot;xxx&quot;,&quot;upgrades&quot;:[&quot;websocket&quot;],&quot;pingInterval&quot;:300,&quot;pingTimeout&quot;:5000}2:40\n</code></pre>\n<p>We need the <code>sid</code>, which we can get here, or from the <code>io</code> cookie set from this endpoint. I’m guessing this is asking the server to create a new socket, hence <code>sid</code>. We call the endpoint again, with <code>&amp;sid=${sid}</code> appended to the query, and we have to do this within 5000 milliseconds, as per the <code>pingTimeout</code> field. If all is good, we’ll get a response back like:</p>\n<pre><code>19:42[&quot;authorized&quot;,[]]\n</code></pre>\n<p>Afterwards, we may initiate a websocket connection to <code>wss://${server}.minesweeper.online/mine-websocket/?authKey=${authKey}&amp;session=${session}&amp;userId=${userId}&amp;transport=websocket&amp;sid=${sid}</code>. Their protocol requires a 3 way handshake upon websocket initialization otherwise the connection will terminate.</p>\n<ol>\n<li>Client sends <code>2probe</code></li>\n<li>Server replies <code>3probe</code></li>\n<li>Client sends <code>5</code></li>\n</ol>\n<p>After the handshake is complete, we must ping the server with <code>2</code> according to <code>pingTimeout</code>, which is 5 seconds, to which the server acknowledges with <code>3</code>. If the server does not receive any pings in 5 seconds from us, it will terminate the connection with code 1005.</p>\n<p>Great, we managed to establish a connection after all of this! I think Minesweeper Online uses a library, socket.io, to manage these socket messages and I considered learning it to help this process, but I decided against it. I wanted to do this blind, to simulate reverse engineering a completely foreign network protocol. So, we carry on to encoding and decoding messages for playing a game.</p>\n<p>From what I can tell, all (outgoing and incoming) messages regarding Minesweeper games start with a <code>42</code>, followed by a JSON array with the following format:</p>\n<pre><code>[\n  direction,   // &quot;request&quot; or &quot;response&quot;\n  [\n    opcode/function,    // see below\n    [payload],          // depends on opcode/function\n    something           // the client always seems to include &quot;494&quot; here, while the server\n                        // doesn't have anything here. maybe version number?\n  ]\n]\n</code></pre>\n<p><code>opcode</code> is represented as a string when logging it to console, but when converting it back to binary, it seems to be 4 bytes long for outgoing messages and 7 bytes for incoming messages, which is weird because it’s not a multiple of 2. I don’t think this should be a byte alignment issue from converting it to a string and back, because the byte before <code>opcode</code> should correspond to <code>&quot;</code> in the JSON array.</p>\n<p>It doesn’t <em>really</em> matter how to make sense of these opcodes since you can just treat them as magic strings, but I kind of wanted to. I thought that maybe there would be a table of opcodes in the JavaScript or that it was actually some binary code converted to a string, but no, I could literally search the JavaScript for the opcode string and find results. Furthermore, the opcodes that were sent by the server <em>kind</em> of looked like symbols in the minified JavaScript, e.g. <code>G68.t18</code>. Sure enough, I found an object called <code>G68</code> with a function <code>t18</code> with the number of parameters matching the length of the <code>payload</code> array for the <code>G68.t18</code> opcode.</p>\n<p>Wow, what a discovery! So in fact, <code>opcode</code> isn’t really an opcode, but a function, which is what we’ll be calling it from now on. This makes referencing how to handle incoming messages so much easier. And to think I just stumbled upon this while satiating my curiosity. After thinking about it though, what I really should have done was to figure out where and how incoming messages were being handled in the client code, and I probably would have gotten to the same conclusion, though with much more headache while navigating the minified code.</p>\n<p>Anyway, back to business. To make things easier to read, I’ll only write the function name and payload, but you should keep in mind the full message format, especially that clients must send the additional <code>&quot;494&quot;</code>.</p>\n<details class=\"wp-block-details\"><summary>To initiate a game, we send a message like so:</summary><pre class=\"wp-block-code\"><code>// function: gn16\n[\n  gameType, // beginner=1, intermediate=2, expert=3\n  null, null, null, null,\n  number, // not sure\n  1, // seems to always be 1\n  null,\n  countryCode, // 2 letters, e.g. \"CA\"\n  null, null, null, null\n]</code></pre></details><details class=\"wp-block-details\"><summary>The server will reply with a long message giving lots of details.</summary><pre class=\"wp-block-code\"><code>// function: G69.i41\n[\n {\n    id, // game id\n    originalId,\n    server,\n    level,\n    sizeX,\n    sizeY,\n    mines,\n    state,\n    nf,\n    exp,\n    diff,\n    hints,\n    userId,\n    ping,\n    clickType,\n    mobile,\n    timeGameCreated, // Unix timestamp, milliseconds\n    timeStart, // same format\n    requests, // array of numbers for when requests were made\n    startX,\n    startY,\n    user {\n      username,\n      country\n    },\n    // other stuff\n  },\n  // board state. each is a key of numbers from 0 to sizeX*sizeY-1\n  // goes down and then right\n  {\n    t: {}, // picture id — 0-11\n    o: {}, // opened\n    f: {}, // flagged\n    g: {}, // not sure\n    g2: {}, // not sure\n  },\n  [click history]\n  // other stuff\n]</code></pre></details><details class=\"wp-block-details\"><summary>You also get the same reply format as above if you restore a game using this:</summary><pre class=\"wp-block-code\"><code>// function: \"gj4\"\n[\n  gameId,\n  null,\n  countryCode,\n  0\n]</code></pre></details><details class=\"wp-block-details\"><summary>Here’s the message format for client clicks.</summary><pre class=\"wp-block-code\"><code>[\n  \"request\",\n  [\n    \"gu57\",\n    [\n      numClicks,\n      gameId,\n      clickType, // probe=0, flag=1, chord=2\n      x, // 0 indexed, y increases going down\n      y,\n      timeElapsed, // since the start time, in milliseconds\n      [ [x,y] ], // array of coordinates to be changed\n      blackMagicString, // no idea, only non-empty when flagging. likely binary.\n      null, // usually, not sure what it is\n      null, // usually, not sure what it is\n    ],\n    \"494\"\n  ]\n]</code></pre></details><details class=\"wp-block-details\"><summary>And here’s the message format in response.</summary><pre class=\"wp-block-code\"><code>// function: \"G68.t18\"\n[\n  numClicks,\n  gameId,\n  {\n    x, y,\n    timeElapsed,\n    type,\n    touchCells: [\n      x, y,\n      numBombs, // picture id — 0-11\n      isRevealed, // 0 or 1\n      isFlagged, // 0 or 1\n    ]\n  },\n  null, // usually, not sure what it is\n  bool, // whether the server sent this message, I think\n]</code></pre></details>\n<p>The game complete message from the server is fairly similar to the initiate/restore game message, but I’ll omit it here because your eyes have probably already glazed over. If you’re interested, check out the repository at the end of the post.</p>\n<p>With this information, it’s not too difficult to build a custom client for the game. In fact, I built a simple terminal interface as a proof of concept.</p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/gW5vLrzMdr-480.avif 480w, https://blog.mnke.org/img/gW5vLrzMdr-960.avif 960w, https://blog.mnke.org/img/gW5vLrzMdr-1440.avif 1440w, https://blog.mnke.org/img/gW5vLrzMdr-1532.avif 1532w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/gW5vLrzMdr-480.webp 480w, https://blog.mnke.org/img/gW5vLrzMdr-960.webp 960w, https://blog.mnke.org/img/gW5vLrzMdr-1440.webp 1440w, https://blog.mnke.org/img/gW5vLrzMdr-1532.webp 1532w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/gW5vLrzMdr-480.gif\" alt=\"\" width=\"1532\" height=\"1308\" srcset=\"https://blog.mnke.org/img/gW5vLrzMdr-480.gif 480w, https://blog.mnke.org/img/gW5vLrzMdr-960.gif 960w, https://blog.mnke.org/img/gW5vLrzMdr-1440.gif 1440w, https://blog.mnke.org/img/gW5vLrzMdr-1532.gif 1532w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"></picture></p>\n<p><picture><source type=\"image/avif\" srcset=\"https://blog.mnke.org/img/lyM9agwHLh-406.avif 406w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><source type=\"image/webp\" srcset=\"https://blog.mnke.org/img/lyM9agwHLh-406.webp 406w\" sizes=\"(min-width: 40rem) 40rem, 100vw\"><img loading=\"lazy\" decoding=\"async\" src=\"https://blog.mnke.org/img/lyM9agwHLh-406.gif\" alt=\"\" width=\"406\" height=\"559\"></picture></p>\n<p>To wrap things up, here are some of the things I learned.</p>\n<p>Firstly, to reverse engineer any protocol, it’s incredibly important to set up a good sniffing tool. Without some way to sniff, filter, and match packets to actions, none of this would be possible. For websockets, this was fortunately very easy since I could just override the browser’s default <code>WebSocket</code> class by loading some code early on, and so I don’t even have to figure out how to decrypt messages. Thank you, JavaScript for having the jank to allow this to happen so easily.</p>\n<p>After the sniffer is set up, we have the difficult job of figuring out the client message format. I don’t think there’s any step-by-step process that will work every time, but generally, we want to repeat actions and see what changes or doesn’t change in the corresponding messages. After some baseline is established for the message format of an action, we should slightly change the action to see how the message responds. For example, what happens to the message format if I left click instead of right click an unopened tile? What will happen if I left/right click an opened tile? I was lucky enough to have messages be encoded as a JSON object, but I can imagine tools like <code>xxd</code> would be very helpful to view the message as hex/binary.</p>\n<p>For decoding server messages, it’s still a similar process. Repeat actions that you expect should produce similar results, find the kernels of similarity, etc. With server messages, however, you have access to the code that handles them, unlike client messages. Most of the time, the client code will be obfuscated so it’ll be difficult to read, but this is where we can break out our debugging skills and step through the code line by line. Even with JavaScript, it’s usually minified to the point where it’s completely unreadable and you have to trace call stacks and pause execution often to get any understanding of the code.</p>\n<p>And really, that’s it. Afterwards, you might want to build some tools to let you send and receive messages like I did with the terminal interface, but it can be a lot of work, especially for more complex protocols. I had a lot of fun with this project, and I hope you learned a thing or two. Happy hacking!</p>\n<p><a href=\"https://github.com/tonyd33/mso-api/\">Repository link</a> — contains all the tools I wrote for this project.</p>\n",
      "date_published": "2023-11-17T21:54:50Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/bang-bang/",
      "url": "https://blog.mnke.org/bang-bang/",
      "title": "bang bang!!",
      "content_html": "<p>For years now, I’ve been using POSIX shells as one of my primary interfaces to my computers, alongside a browser. In fact, for the past 3 years, I’ve probably spent over 80% of my time on computers using one or the other. Spending so much time with these programs, I try to master them to be as efficient as possible. While mastery over browsers is somewhat limited, there’s nearly boundless possibilities with the shell (really, the assortment of programs accessed through the shell). I continually find new things about programs I use everyday — anything from little tips and tricks to eye-opening, paradigm-shifting features. In fact, just yesterday, I learned about sessions in vim. How have I not known this for years?! And less than a month ago, I learned about brace expansion in bash. Now I can just do <code>mkdir /path/{dir1,dir2,dir3}</code> instead of running three separate commands. It really makes me wonder what incredible knowledge could be gleaned from a thorough read of entire documentations from programs I use often. Interesting as that is, what I want to talk about today is bangs (<code>!</code>) in bash.</p>\n<p>Let me first introduce to you the utility of bangs. Ever try to run a program that requires elevated privileges and forget to prepend the obligatory <code>sudo</code> in front? Me too, and too many times. I’d have to scroll up the history with ↑ or Ctrl+p and then shift the cursor to the start of the line with, say, Ctrl+a, and then type the <code>sudo</code>. What a waste of time! Instead of doing that, you can just be angry and tell the shell <code>sudo !!</code>. This is the exact same as if you had ran the command again, except prepending the <code>sudo</code>. For example, if I had ran <code>pacman -Sy abc</code> and then <code>sudo !!</code> right after, what I’d really be running is <code>sudo pacman -Sy abc</code>.</p>\n<p>If this isn’t already life-changing, I don’t know what I can tell you. Actually, I’ve known about this for years and it’s been cemented into my workflow. For every few times I remember to add the <code>sudo</code> to a package install command, there’s another time that I forget and <code>sudo !!</code>. Before we go any further though, let’s talk about this weird expression. <code>!!</code> is pronounced “bang bang”, with each exclamation mark being referred to as a “bang”. Why? I’m not sure, but this <a href=\"https://english.stackexchange.com/a/62920\">stackexchange</a> answer points to Unix nerds re-popularizing the name originating from the printing industry. I think it’s a pretty good name and much easier to vocalize than “exclamation point”, which is the same reason why the printing industry ostensibly used it. Enough with the interlude on etymology though, let’s get back to business.</p>\n<p>Since learning the <code>!!</code> trick a while ago, I wondered why <code>!!</code> expanded into the previous command and if there were a family of expansions that <code>!!</code> was part of. Sure enough, there is. Broadly, <code>!</code> is a special bash character for history expansion. From reading the <a href=\"https://www.gnu.org/software/bash/manual/bash.html#Event-Designators\">GNU bash docs</a>, <code>!</code> commands are meant to reference your command history. The way I like to think about it is that it’s an interface to select a previous command, or parts of a previous command. The interface needs to know which command to select and optionally, which parts of the command to select. I won’t go into detail on command selection, especially since the official docs are linked right above, but I’ll give some of the more useful ones.</p>\n<ul>\n<li><code>!-n</code> refers to the <code>n</code>th most recent command.</li>\n<li><code>!!</code> refers to the last command and is what powers the <code>sudo !!</code>. I use this all the time.</li>\n<li><code>!string</code> refers to the most recent command <em>starting</em> with <code>string</code>.</li>\n<li><code>!?string?</code> refers to the most recent command <em>containing</em> <code>string</code>. Omit the trailing <code>?</code> if you want to match <code>string</code> at the <em>end</em> of the command, rather than anywhere in the command.</li>\n</ul>\n<p>Word selection is where it gets interesting. Normally, you’d have to delimit the word selection part with a <code>:</code>, but there are exceptions, which we’ll talk about. For this part, let’s say we want to select our previous command, using <code>!!</code>. Keep in mind <code>!!</code> can be replaced with whatever command you want to select (e.g. <code>!-2</code> or <code>!ls</code>).</p>\n<ul>\n<li><code>!!:n</code> selects the <code>n</code>th word of the command, with the delimiting character being whitespaces, 0-indexed.</li>\n<li><code>!!:n-m</code> selects the <code>n</code>th to <code>m</code>th word, inclusive, 0-indexed.</li>\n<li><code>!!:^</code> selects the first word (same as <code>!!:1</code>).</li>\n<li><code>!!:$</code> selects the last word.</li>\n<li><code>!!:*</code> selects all but the zeroth word.</li>\n</ul>\n<p>Again, I’ve omitted a few word selection commands, but I think these are the most important ones. For special characters like <code>$</code> and <code>*</code>, we can remove the <code>:</code> delimiter entirely, abbreviating to <code>!!$</code> and <code>!!*</code>. In fact, these two word selections are incredibly useful. Ever had to do a <code>mkdir -p /a/very/very/long/path</code> and then <code>cd /a/very/very/long/path</code> right after? Well, you can just do <code>cd !!$</code> now! Or, have you ran something like <code>ffmpek --there --are --so --many --options</code> only to realize you spelt <code>ffmpeg</code> as <code>ffmpek</code>? No problem, just do <code>ffmpeg !!*</code>.</p>\n<p>Lastly, we should talk about modifiers. Add another delimiter <code>:</code> and modify your bang command with a <code>p</code> to just print, instead of execute the command. For example, <code>ls !!*:p</code> will just print the command that would have been run had you not added the <code>:p</code>. The modifier <code>s/old/new</code> is string substitution, where <code>/</code> can be replaced with any other character (obviously, choose one that doesn’t appear in <code>old</code> or <code>new</code>). For example, if I had ran <code>ls /path/to/long/path/with/spelligg/mistake/</code>, I can make a quick fix with <code>!!:s,spelligg,spelling,</code>.</p>\n<p>Whew, that was a lot. And there’s still so much more that I haven’t talked about. If you want to know more, I’ll kindly ask you to RTFM at the <a href=\"https://www.gnu.org/software/bash/manual/bash.html#Event-Designators\">official docs</a> 🙂</p>\n<p>For me, the biggest takeaways are <code>!!*</code>, <code>!!$</code>, and <code>!!:s/old/new/</code>. The other stuff is powerful, sure, but honestly, I probably wouldn’t notice the difference between taking the slightly less efficient path by Ctrl+r’ing into the previous command and moving by word to edit the command most of the time. Not only that, but remembering the more niche tricks and then learning applying them efficiently isn’t free! In fact, I must confess that much of the reason for writing this post is so I don’t forget it myself…</p>\n<p>Cheers, and happy hacking.</p>\n<p>Photo by <a href=\"https://unsplash.com/@jakewalker?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\">Jake Walker</a> on <a href=\"https://unsplash.com/photos/MPKQiDpMyqU?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\">Unsplash</a>.</p>\n",
      "date_published": "2023-09-27T14:25:23Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/storage-issues/",
      "url": "https://blog.mnke.org/storage-issues/",
      "title": "Storage Issues",
      "content_html": "<p>A while back, I purchased a Dell EMC KTN-STL3 storage enclosure so that I could switch off my janky storage setup. I’m embarrassed to even talk about it now, but it was seven 4TB SAS HDDs connected to an IBM M5015 on RAID0, directly passed into ZFS. If that wasn’t bad enough, I didn’t have enough 15 pin SATA power cables coming off the PSU connected to my motherboard, so I had to run another PSU to connect the rest. When I set it up, I didn’t understand much about storage configurations, and I was antsy to just get the system up and running, especially after dealing with much trouble with the RAID controller, but I knew it wasn’t great so I bought the enclosure, but I still didn’t have the equipment to interface with it correctly so I just let it sit there and collect dust for a while.</p>\n<p>A couple of months after the initial setup, I was alerted by my friend of just how <em>bad</em> my configuration was. I wanted to change it, but the storage wasn’t physically available to me at the time because I was studying away from home. At the same time, the setup seemed to be working fine at the moment, and with no other choice, I just let it be. Half a year later, the day of reckoning arrived. The TrueNAS instance the storage was connected to had suddenly powered off.</p>\n<p>I guided my family to diagnose the problem and restart the computer, and it did so just fine. I remotely verified that the data was intact and breathed a sigh of relief. I had documents and pictures from my old phones on there, backed up to nowhere else and I would’ve been very upset to lose it. This incident made me realize how urgent the upgrade and backup was. I was planning to at least back up my data over the next few days (I couldn’t do so immediately because I had to take care of my school duties), but the very next day, the computer had shut off again. I asked my family to do the same thing, and I remotely checked the data again. The ZFS pool was offline.</p>\n<p>Months later, I returned home and started digging at the issue. In the RAID controller interface, I found that a single drive had failed. There was no chance of recovery because RAID0 had striped everything across all the disks. I cursed my own stupidity. What I should have done months ago was to immediately ask my family to completely shut down the system to prevent any further damage. Alas, the damage was already done and I just had to accept my losses and move on.</p>\n<p>I got to work researching and consulting my friend on how to get the storage enclosure up and running. Months ago, the same friend had recommended to me and bought for me a HBA controller — an HP H221, flashed with some firmware that I didn’t note down — to pass through each disk separately for a proper ZFS setup. The back plane of the enclosure had four SFF 8088 ports, two on the top half and two on the bottom half. The controller had two SFF 8088 ports, so I bought two male to male connectors and hooked it up and loaded the disks into the enclosure.</p>\n<p>After some stupidity (I didn’t realize you had to unscrew some parts of the SFF cable to fit it correctly!), I connected the cables and tried to boot. I booted into the controller’s BIOS, like normal and waited on the “Initializing…” screen… and I waited… and I waited some more. I ate a meal and came back. Still no change. I was puzzled because the system was able to boot past the controller’s BIOS and even into the controller’s configuration interface just fine before I plugged the cables in. I unplugged the cables and tried booting again. Yup, for some reason, plugging in the cables was causing problems. <em>Maybe I was plugging in to the wrong ports</em>, I thought. After all, there were four ports on the enclosure. I played around with plugging into different combinations of the enclosure. Still no luck.</p>\n<p>I had tried searching up EMC KTN-STL3 manuals, guides, or just any relevant information that might be able to help, including general information about storage controllers and about the HP H221 card I had, but the information was incredibly sparse. Of the information I could find, it seemed like my setup should have worked. I further probed my friend for consultation, but we came up short. For the next few days, I did more research and fiddled around with settings. I tried disabling BIOS booting through the controller interface as per this <a href=\"https://www.reddit.com/r/DataHoarder/comments/o57pqf/hp_h221_stuck_on_initializing/\">Reddit thread</a> (take a moment to look at the upvotes and realize how uncommon my problem was). I was able to skip the controller’s BIOS and boot straight into TrueNAS, but it was failing to start up properly, giving me errors like “Root mount waiting for: CAM”. I could only find a few threads on this with much information I couldn’t even understand, but there was one <a href=\"https://www.truenas.com/community/threads/root-mount-waiting-for-cam.91238/\">TrueNAS thread</a> that gave me a lead before delving deeper to learn enough to parse the information in the other threads. It mentioned that this error was associated with an HBA failing to initialize under UEFI.</p>\n<p>When I came into this, I was hoping I wouldn’t have to flash my controller. Things can very easily go wrong if you’re not careful or knowledgeable, and I had neither of those attributes — horror stories of irreversibly bricking a controller were flooding through my mind. However, I soon realized flashing the controller to upgrade to UEFI support was the only lead. My friend had told me of the tools — megarec and sas2flash — but these were fairly specialized tools that I couldn’t easily find digestible documentation for, so I had been scared away in the past. I found a very comprehensive <a href=\"https://forums.serverbuilds.net/t/guide-updating-your-lsi-sas-controller-with-a-uefi-motherboard/131/6\">thread to update LSI SAS controllers with an UEFI motherboard</a>, and an even more detailed <a href=\"https://www.truenas.com/community/resources/detailed-newcomers-guide-to-crossflashing-lsi-9211-9300-9305-9311-9400-94xx-hba-and-variants.54/\">thread on the (cross)flashing process</a>, and with the go-ahead signal of my friend, I started hacking away.</p>\n<p>I was told to use megarec to wipe the BIOS of the controller before using sas2flash, so I did that after backing it up with the same tool. I then switched off the DOS shell containing megarec and onto the EFI shell containing sas2flash, created by following the first guide, and tried to follow it.  <code>sas2flash -listall</code> was giving me a controller with malfunctioning firmware, which was <em>not</em> what the guide expected, but I wasn’t fazed because I knew I had just wiped the firmware with megarec. I followed the rest of the guide, but I was getting errors trying to run <code>sas2flash -o -f xxx.bin -b x64sas2.rom -b mptsas2.rom</code>, telling me there was a mismatch in the chipset or something. <em>Ah, I’m flashing with the wrong firmware</em>, I realized. After a couple of trips up and down the stairs between my workstation and the storage location trying to figure out the right firmware, I was still getting mismatch errors. I scratched my head in confusion because I was fairly certain I had gotten it right, especially after pausing to understand the crossflashing process a bit more. As I read the second guide more closely, I found out that in newer versions of sas2flash, it doesn’t like it when you crossflash, even when you’re crossflashing a compatible card. I sat down and fully digested the second guide and found a hacked version of sas2flash given in the minimal USB files at the bottom of the guide. I made sure to find compatible firmware files with the HP H221 in <a href=\"https://forums.servethehome.com/index.php?threads/lsi-raid-controller-and-hba-complete-listing-plus-oem-models.599/\">this thread</a>, downloaded them off of <a href=\"https://www.broadcom.com/support/download-search?pg=Legacy+Products&amp;pf=Legacy+Host+Bus+Adapters&amp;pn=LSI+SAS+9205-8e&amp;pa=&amp;po=&amp;dk=&amp;pl=&amp;l=false\">broadcom</a>, and copied over the minimal USB files and the firmware files onto my USB and used the knowledge from the first guide to find and copy the right firmware files.</p>\n<p>Back into the EFI shell, I used the hacked <code>sas2flash to run sas2flash -o -f 9205-8e.bin -b x64sas2.rom -b mptsas2.rom</code> like the first guide mentioned and… the flash succeeded! I updated the SAS ID (I just made up a random ID, which was fine according to the second thread) and exited the EFI shell. Booting back up, the familiar text of the controller BIOS greeted me and I held my breath as the “Initializing…” text showed up again. <em>One mississippi, two mississipi, …</em> I counted in my head. After a short, but nervewracking pause, the screen flashed with new text listing the drives detected by the controller and it booted into TrueNAS with no problems. I checked on the web interface that the drives were indeed being detected — they were!</p>\n<p>This whole process of trial and error took over a week and a half, and I’m writing this only hours after the success. It was exhilarating seeing the pieces finally fall into place after much perseverance, and traces of the joy I felt can still be found as I write this. I wanted to write this post to document my struggles and serve as a guiding light for the few who find themselves in a similar situation and stumble upon this post. I omitted some details for sake of comprehensibility — none that I think are relevant — but for those who use this post to troubleshoot their own problems and are confused on some of the steps I took, contact me at <a href=\"mailto:tonydu121@hotmail.com\">tonydu121@hotmail.com</a> and include a reference to this post in the subject so I can find it (something like “Storage Help Required” will do). I’m by no means an expert and cannot guarantee that I’ll even be helpful, but I’ll be happy to provide the full details of what I did.</p>\n<p>Cheers.</p>\n",
      "date_published": "2023-09-05T18:02:00Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/death-roll/",
      "url": "https://blog.mnke.org/death-roll/",
      "title": "Death Roll",
      "content_html": "<p>I thought of some interesting questions based off of this game I saw. I don’t know what the game is called, but I call it death roll now. The rules of the game are simple:</p>\n<ol>\n<li>Start with a natural number $n$</li>\n<li>Roll a number $m$ from $1$ to $n$ uniformly randomly</li>\n<li>If $m=1$, then you lose.</li>\n<li>Otherwise, set $n=m$ and go back to step 2</li>\n</ol>\n<p>This turns into a game between friends by taking turns rolling the number. I figured I might be able to pull this out as a drinking game, but that made me wonder if there was any way to rig the game by choosing the initial number in a certain way. For example, if there are $k$ players, could I choose $n$ inconspicuously so that I can expect the game to end before the $k$th player even has a chance to roll? Or in general, how much shenanigans would I be able to get away with?</p>\n<p>So I began messing around with the numbers. Let $T(n)$ be the expected number of turns by starting with $n$. If we roll $n$, then the expected number of turns is $T(n)+1$, because we ended where we started, except we wasted a turn. In general, if we roll $i$, then the expected number of turns is $T(i)+1$. By properties of expectation, $T(n)$ follows this recursive formula:</p>\n<p>$$T(n) = \\mathbb{P}(n)(T(n)+1)+\\mathbb{P}(n-1)(T(n-1)+1)+\\ldots+\\mathbb{P}(1)$$</p>\n<p>where $\\mathbb{P}(i)$ is the probability to roll $i$, which should be uniformly distributed but is noted for generalization.</p>\n<p>Doing some algebra will yield that $T(n+1)=T(n)+\\frac{1}{n}$. I’d like to remark that this is an exceptionally simple and elegant formula to capture this game.</p>\n<p>I also wondered if there was a non-recursive definition for $T(n)$. I tried solving the recurrence by using advancement functions, but I had no luck. I turned to Wolfram Alpha to look for a computerized solution, and I found that $T(n)=H_{n-1}$, where $H_n$ are the harmonic numbers. I’m not very familiar with the harmonic numbers, so I’m not sure why it shows up here, but it was a curious connection to the uninformed me.</p>\n<p>I also have to look at the variance of the number of turns as a function of $n$. I haven’t done that yet, but it seems to have quite high of a variance, which is not surprising. I wrote a script to simulate the game and after a million samples of $n=50,000$, we get that the expectation is around $12.40$ and the variance is $13.03$. The variance is way too high for me to have any hope of rigging the game unfortunately. I’m too lazy to calculate the variance by hand, but if I ever do, I’ll come back and update the post to show the actual formula.</p>\n<p>Death roll will eventually complete, and my questions above were in regards to how long it would take. However, what if we modify death roll a little bit? Let $f: \\mathbb{N}\\to\\mathbb{N}$ and modify death roll into $f$-death roll by keeping the same rules as death roll except in step 4, set $n=f(m)$. I’ll also introduce another variance $f2$-death roll, where $f:\\mathbb{N}\\times\\mathbb{N}\\to\\mathbb{N}$ and in step 4, set $n=f(m,n)$. I’ll mainly be concerned about the first variant for now, but the second is something to think about.</p>\n<p>I wonder what are necessary conditions such that $f$-death roll doesn’t last forever? For example, I would guess that if $f(n)=n!$, then the game would never end. What’s the boundary between when the game ends and when it doesn’t though? Certainly a sufficient condition would be that $f(n)\\leq n$. I also posited of a necessary condition that $f(n)&gt;n$ for only finitely many $n$.</p>\n<p>Upon discussion with a colleague, we discovered that the posit is incorrect. Consider the function</p>\n<p>\\[<br>\nf(n) = \\begin{cases}<br>\nn+1, &amp;n \\text{ odd}\\\\<br>\n1, &amp;n \\text{ even}<br>\n\\end{cases}<br>\n\\]</p>\n<p>Certainly, $f(n)&gt;n$ for infinitely many $n$, but we’ve proven that $T(n)$ with respect to this $f$ is finite. It’s not quite constant, but taking $n\\to\\infty$ gives a bounded $T(n)$.</p>\n<p>Of course, $f$ lives among a family of functions where</p>\n<p>\\[<br>\nf(n) = \\begin{cases}<br>\nn+1, &amp;n\\not\\equiv0\\mod m\\\\<br>\n1, &amp;n \\text{ otherwise}<br>\n\\end{cases}<br>\n\\]</p>\n<p>I’ll call this family of functions the <strong>ideal functions</strong>, given by the behavior of “absorbing” elements from the ideals of ring theory, and of course by the fact that $f$ acts on the ring generated by modding out an ideal of the integers. It’s not hard to see that ideal functions do violate $f(n)&gt;n$ for infinitely many $n$, but also that $T(n)$ is finite and $\\lim_{n\\to\\infty}T(n)$ is finite (and although not proven yet, it seems likely that this limit is equal to $m$, the element generated by the principle ideal $f$ acts on).</p>\n<p>I think what makes the ideal functions special is not the fact that they act on the ideal generated, but that the limit of $T(n)$ is finite. Knowing the existence of ideal functions, it’s a simple exercise to construct a function living outside of the ideal functions that violate $f(n)\\leq n$ for infinitely many $n$ but still have finite expectation for all $n$. We’ll call functions with finite limiting $T(n)$ the <strong>limited functions</strong>, of which the ideal functions are a subset of. It may not be such a simple task to construct a non-limited functions violating $f(n)\\leq n$ for infinitely many $n$ with finite $T(n)$ for all $n$. Naturally, this is my next posit:</p>\n<p>Given $f:\\mathbb{N}\\to\\mathbb{N}$, if $T(n)$ is finite, then $f(n)\\leq n$ for infinitely many $n$ or $f$ is a limited function.</p>\n<p>If any more progress is made, I will update this post again.</p>\n",
      "date_published": "2023-03-13T01:00:00Z"
    }
    ,
    {
      "id": "https://blog.mnke.org/dns-splitting/",
      "url": "https://blog.mnke.org/dns-splitting/",
      "title": "DNS Splitting",
      "content_html": "<p>I had started working on migrating off of cloud services to self hosting everything a couple of months ago by getting some new gear to set up my server on (shoutout to [REDACTED] for the hookup). I started off by setting up a stub webserver and general environment for me to deploy games servers for my friends which is pretty much the exact same process as if you were to do it on an external hosting service, except I was a little bit more careful to not open myself up to security vulnerabilities since I’m hosting this in my own home.</p>\n<p>After I had set up a Minecraft server, I added a record to my domain provider pointing to my home network and tried to connect to it using that domain. No connection. That was a bit strange but maybe I forgot some configuration so I went through a checklist:</p>\n<ul>\n<li>The port was open</li>\n<li>The DNS record pointed to my public IP address</li>\n<li>Firewalls were fine</li>\n<li>Minecraft server was running</li>\n</ul>\n<p>Well, what the hell was the issue then??</p>\n<p>Turns out, it’s a much broader networking issue that’s usually already resolved by someone <em>somewhere</em>.</p>\n<p><a href=\"https://unix.stackexchange.com/a/282094\">This answer</a> to a stackexchange question explains a similar problem much better than I can, but I want to dumb it down and over-explain for myself in the future and for those who may stumble upon this and may find it helpful.</p>\n<p>When your computer wants to send a message to someone, it attaches an address to whom the message is sent to (the destination) and one for whom the message was sent from (the source). After your computer sends the message, it expects to get a response from the receiver with the source and destination addresses swapped.</p>\n<p>Your computer may sometimes know exactly where to send a message to, like in the case of sending a message to localhost (which should be set up for you automatically in almost all scenarios), but most of the time, it may need some help. This is done through a <em>gateway</em>. This is a device (likely your modem/router) that, figures out where to send messages among other things. So, when you connect to a local IP address, your computer doesn’t know where to send the message to, so it forwards it to the gateway. The gateway knows where to send the message, so it relays it to the receiver.</p>\n<p>Okay, but what if you try to connect to a non-local IP address? Well, it’s still a similar process. Your computer forwards the message to your gateway and your gateway realizes that this IP address belongs to none of the locally connected addresses in your network. It wants to send it to the internet, but it can’t quite do so yet. This is because the local IP address of the source is not unique among the internet IP addresses, and so your gateway rewrites the source IP address to your public IP address, which is unique, and sends it off. When your gateway eventually receives a message back, the destination IP address is your public IP address, as per the protocol described earlier, and to finally send the message back to the original computer, it rewrites the destination IP address as the local IP address that originally sent the message. This process is called <em>network address translation</em>, or NAT for short.</p>\n<p>Sending messages through a domain like <a href=\"https://web.archive.org/web/20230910180045/http://www.public.com/\">www.public.com</a> just adds one more simple step to the process (simple for our sake). When your gateway receives the message, it doesn’t know who <a href=\"https://web.archive.org/web/20230910180045/http://www.public.com/\">www.public.com</a> is. It only knows how to send a message to IP addresses. The extra step is to contact a DNS server to find out the IP address of <a href=\"https://web.archive.org/web/20230910180045/http://www.public.com/\">www.public.com</a>. Once it does that, the rest is the same.</p>\n<p>Now that we understand this, we’ll work through what happened when I tried to connect to my server through the public domain.</p>\n<p>Suppose my public domain is <a href=\"https://web.archive.org/web/20230910180045/http://www.public.com/\">www.public.com</a>, my public IP address is 1.2.3.4, and the local IP address of the computer my Minecraft server on is 192.168.1.2.</p>\n<ul>\n<li>My computer tries to send a message from 192.168.1.3 to <a href=\"https://web.archive.org/web/20230910180045/http://www.public.com/\">www.public.com</a></li>\n<li><a href=\"https://web.archive.org/web/20230910180045/http://www.public.com/\">www.public.com</a> is not an address that is recognized in my computer’s routing table (unlike localhost for example)</li>\n<li>My computer hands it off to the gateway on my router</li>\n<li>The gateway resolves the DNS lookup to be 1.2.3.4</li>\n<li>The gateway doesn’t know where to forward the message to! We know that it should be the local IP address 192.168.1.2, but the gateway doesn’t know that</li>\n<li>Sending the message fails, and your gateway doesn’t give any response.</li>\n</ul>\n<p>Now that we know how it fails, how can we solve this?</p>\n<p>The method I used is called <em>DNS splitting</em>. Really, all it is is telling your computer that when a message is supposed to be sent to X, send it to Y instead. So when we want to connect to <a href=\"https://web.archive.org/web/20230910180045/http://www.public.com,/\">www.public.com,</a> we just tell it, “We know where <a href=\"https://web.archive.org/web/20230910180045/http://www.public.com/\">www.public.com</a> is already. It’s at 192.168.1.2″. How this can be accomplished is by modifying your /etc/hosts file to include a line like</p>\n<pre><code>192.168.1.2 www.public.com\n</code></pre>\n<p>With that, it was fixed!</p>\n<p>This should work on all *nix systems (I’ve tried with Linux and macOS at least). If you’re on Windows, apparently doing something with the system drivers(?) might work. See this <a href=\"https://yunohost.org/en/dns_local_network\">article</a> by yunohost for more details.</p>\n<p>I think there are other ways of solving this problem like NAT reflection as mentioned in the stackexchange thread earlier, but this solution is dead simple and I’m happy with it.</p>\n<p>I think for some routers, this is configured for you by default in the gateway itself, or at least it’s configurable in the DNS settings in your gateway, so you don’t have to DNS split on every single machine that needs it, but my router, provided by Shaw, sucks and so I have to do this workaround instead.</p>\n<p>Hopefully this helps someone out and hopefully you learned a thing or two reading this and I wish you luck in your networking adventures!</p>\n",
      "date_published": "2022-06-25T00:36:00Z"
    }
    
  ]
}