Understanding CVE-2026-105483


Context for CVEs


It's been a big week for vulnerabilities. We found out that you can just have 4 as your private key for years and nobody noticed. We learned that PHP has had a horrific RCE for decades. More and better bloggers than I are going to cover those to death in detail that I can't even aspire to. In the onslaught there's a much funnier one, though: CVE-2026-105483. By the way, if you're running a Node app you do need to patch the other vulnerability: CVE-2026-111497. Seriously, go patch or mitigate that one right now then come back to read.

The vulnerability we're talking about only affects Node.js, and while it's nominally a security vulnerability it's not particularly exploitable. It's important to know how to come to that conclusion though, and for that matter what's a CVE anyway?

CVE, which stands for Common Vulnerabilities and Exposures, is in the simplest terms a database of potential security problems for programs. The database started in the late 90's and was a response to one of the hardest problems on the planet: naming things.

As long as programs have existed there have been security vulnerabilities, but before the CVE system's widespread adoption (and even after,) it can be difficult to talk about them. Every vendor maintained its own bug database but many vendors are going to share the same code. If there's a vulnerability in zlib, a popular compression library, and I'm an Oracle shop while you're an IBM shop, we might have a problem. I might know that vulnerability as Oracle Patch 48738321098. You might know it as IBM bug 898774231348427 (IBM has been in business a lot longer, after all.) These are the same vulnerability with the same fix but if we're talking to each-other we may not realize this. Even worse: most vulnerabilities fall under relatively few categories. This is why things like the OWASP Top 10 can exist. It's inconvenient if I tell you about my Oracle fix for that buffer overflow, you go Oh, I think that's this IBM number. I have that fixed already., and it's fixed. It can be catastrophic if you think that and it's a new vulnerability you haven't patched.

So in response to this, more or less, the US Government stepped in. It partnered with the MITRE Corporation, a non-profit here, and funded the creation of a single authoritative name and description format for security vulnerabilities. Whether I'm running on AWS and you're on Azure, or if you're self hosted, or if you don't have any servers at all, if I tell you about a CVE then we all have access to that database. Some of these names and words may ring a bell if you read the news because the CVE system underwent a funding crisis in 2025 for reasons which weren't ever fully disclosed, as far as I know. Luckily the program is now fully funded, so you don't have to worry about the system falling over any time soon.

The NPM Vulnerability


CVE-2026-105483 (I'll probably just call it The Bug from now on,) is a vulnerability in the TLS validation code of Node.js, now fixed. For reasons we're going to get into this is not a particularly exploitable vulnerability, but that raises a question: why does this have a CVE if it's not exploitable? This is mostly a case of better safe than sorry, plus some tricky social factors. It could possibly be that the person who found it can't think of a way to exploit it, but there is one. On the social side having a CVE looks better if you're a security researcher because this is a real, confirmed CVE, not just some bug. It also helps downstream: if I tell my boss I have to push back a deployment by a couple hours to fix something, it's easier if I can point to the CVE. Part of why I chose this bug is that it demonstrates an important skill that not a lot of people talk about: we have this vulnerability in code we're shipping. Are we actually vulnerable? Can we do anything to mitigate it? Sometimes, like here, the answer is probably that you shouldn't go out of your way to fix it. Sometimes we need to take the whole website down to prevent exploits, like many PHP sites are at time of writing.

In this case how do we know it's not something to worry about? We have to look at the bug itself. I would link the CVE here, but the CVE website appears to be down, presumably to make sure that mitigations are in place for the PHP bombshells. Instead the best we have is the patch which fixed it. Don't worry, we aren't diving too deep into code. There are just a few important things to know going in:

  1. When connecting securely to a website, this is done with a certificate. This certificate says that a website is who it claims to be and one of the ways it does this is with a hostname verification. Essentially the certificate says This certificate is valid for example.com and your browser makes sure that you're browsing to example.com.
  2. There are 2 primary ways of identifying a computer on the internet: its IP address, which is like a mailing address, or via DNS (Domain Name Service,) which is a more memorable name like Just Enough Technology. Your computer is capable of looking up Just Enough Technology and seeing its corresponding IP address, which is why you can connect to it. You have to turn that name into an IP address to connect.
  3. Unicode is hard. Unicode, a standard for representing text on computers (to hand-wave it a bit,) includes many entries which visually look very similar but are different to computers. Of particular interest here are the Double Width Digits. Can you spot the difference between these two 3s at a glance? 3 and 3. You probably only noticed because the latter rendered in a different font. They're both clearly 3.
  4. Next, Unicode is even harder than that. Because of the case above, it defines so-called normalization rules: how to transform a piece of text into its canonical form. An example in English is that you'll often see text on tickets and such translated to ALL CAPS.

The confluence of these 4 was the problem. In order to set up the attack, step 1 is that I have to connect to another computer specifically by its IP address. We'll call the IP address 3.3.3.3 for simplicity's sake. Step 2 is that you have to be intercepting my traffic to the other computer for some malicious reason. In this case you've inserted yourself into the network and are claiming to be 3.3.3.3.

With the scenario set up, my computer tries to connect to 3.3.3.3. Instead of connecting to the server I wanted, I've connected to you! Luckily, we have TLS protecting the connection. My computer asks yours for a certificate, which yours returns. That certificate says that it's for the host 3.3.3.3. Note the first 3 is double-width. The textual representation of an IP address does not have a double width character in it, so it's not an IP address. Therefore (according to the code) it must be a domain name. Domain names are normalized, so we apply the normalization rules. 3.3.3.3 becomes 3.3.3.3, then we perform our check: is 3.3.3.3 (the IP address I tried to browse to) the same as 3.3.3.3 (the post-normalization host in the certificate)? Yes it is! You've successfully convinced me that I'm talking with the real server while I'm talking with you.

So why isn't this a vulnerability? We've shown exactly how you could perform a Man-in-the-Middle attack. The problem is that it's actually not very practical to execute. It relies on connecting directly to an IP address without going through DNS, which is going to be relatively rare. Even within corporate networks that sort of direct connection is rare. The other part is that for this to work the attacker needs a certificate which your computer already trusts. Your computer ships with a set of trusted certificates, which you might know from the eMudhra disclosure this week (heck of a week for a Wednesday, isn't it?) When your computer retrieves a new certificate, it sees if one of the certificates it already trusts has vouched for it. The attacker needs to get a valid certificate for you to trust and, quite simply, they can't get that for a domain like 3.3.3.3 for many very good reasons. The only way an attacker could pull this off is if they had the ability to get you to trust random certificates, and that level of control means that they've already done better than intercepting your encrypted browser traffic. They could just install a keylogger, capture your screen, or any myriad of other things. You're almost certainly safe from this one, and if you're not you have much bigger problems.

Conclusion


So why bother talking about this at all? For one, it's an interesting bug. It hits a surprising number of intersections for a patch which amounts to fewer than 5 lines, ignoring comments. For another, it came out in context. Triage is an absolutely essential skill in bug hunting as well as security response. The bug wasn't going to get you hacked. If you were focused on this potential Man-in-the-Middle and missed the Remote Code Execution I told you to go patch first at the start of the article, then maybe your response to the bug got you hacked. Securing your servers is great. Make sure you secure them against the things that matter first.

What Is Plain Text Anyway?


Introduction


If you've spent enough time with software developers, you've inevitably heard someone talk about the virtues of Plan Text. If you're a little more buzzword-compliant, it may not have been phrased as that but rather as x As Code e.g. Infrastructure As Code. Technically these are different, but there's enough overlap that I feel comfortable talking with them in one post. So what are they? What makes something plain text? If this plain text is so great, why isn't every format a plain text format? If you only want the shortest answer I can give: plain text is a spectrum which hinges on how effectively tools which are not designed for the format can manipulate it. We're going to have plenty of examples all over.

What Is And Isn't Plain Text


Much of the time if you ask someone about plain text and as a format and why, you'll get references to things like pipes, the UNIX Philosophy, format preservation, all of which are relevant but none of which I feel capture the full spectrum. I actually asked in a Philadelphia Linux Group and received an answer which I think is worth repeating here not to make fun of him for being wrong or anything, but because I think it is a really good rule of thumb answer. But first, we have to determine what a file is.

A file, for our purposes here, is a series of bytes. A byte is simply a number from 0 to 255. We as humans chose this to be the basis of how our computers work, but it wasn't always like that. There are some computers where the number is from 0 to about 64,000. There are some where the number is about from 0 to 1 million. Additionally we can put more meanings on it: we can say that instead of from 0 to 255, that byte is from -128 to 127, and there's a clever way to do this so that you get the right answer with these negative numbers by doing the same math you would with that 0 to 255 range. The important part here is that a file is made up of these numbers and we impose meaning on them.

When we first made computers we only needed them to work with numbers, so this was fine. As we used computers for more things we found ourself having to represent more complicated stuff, like text. In order to do this, we had to agree on how some body of text was going to be represented in terms of bytes. There were a lot of solutions but for a long time the one which won out in the English speaking world was ASCII, the American Standard Code for Information Interchange. ASCII defined meaning assigned to numbers from 0 to 127, so it fit in a byte. It gave us all of the digits, uppercase letters, lowercase letters, most of the punctuation which you would need, but it included control points too. These control points included things which mattered to teletypes like carriage return, which sent the paper back to the start of the line, and some specifically for computers, such as end of file. The definition I was given earlier, by the way, was that a plain text file included only the characters that you can type with a keyboard. This isn't exactly a bad working definition, it mostly lines up with the ASCII printable characters, plus space, newline, and tab.

There are two places where that definition has problems, though less so now than before. The first is that some of those control codes were used in different ways by different computers. Line endings are the easiest to pick on: if you've ever opened a .txt file on Windows and seen that the whole thing is on 1 line, it was probably saved on something which isn't Windows. Windows uses a carriage feed then new line to represent the end of a line, Linux and current Macs use just new line, old Macs (until the 2000's) used carriage return by itself. Now pretty much everything can work with just new line, though. The second is that there are languages other than English and ASCII doesn't cover those languages, so they had to come up with different ways of representing their own languages. Many of them were based on ASCII, not all of them were exactly compatible. If you've ever seen a Japanese Windows PC and seen that folders were separated by the yen symbol instead of \, that's why: in the old Japanese encoding, the yen symbol used the same number which was shown as \. This has mostly been solved by Unicode, namely UTF-8 which is near-ubiquitous now. UTF-8 is a mapping of characters to numbers much like ASCII (in fact, every ASCII character is the same number in UTF-8,) except UTF-8 can also represent all of those other languages.

Those two break how we defined plain text before though, even with the fixes: what I can type on my US keyboard isn't the same as what you can type on your German or French keyboard. We can also represent any arbitrary data as a series of printable characters. One such example is base64 encoding. Beyond those doesn't tell us why plain text is useful. In my opinion a plain text format is defined by how well it behaves with tools that don't know anything about the format, just that a file is a series of bytes. In understandable terms: if you can open the file in Notepad, make changes, and save it by hand, it's probably plain text. If you tried to do it with, say, a .docx file, all you would see is garbage.

Why Plain Text Is Good


So why should you care about whether or not you can open a file in notepad, and where does X As Code come in? The answer is that software developers have gotten very good at writing tools which deal with these files. To pick on the Linux or Mac toolbox, I have tools like wc which will give me a word count of a file, sed which is like a super-powered search and replace tool, awk which is more powerful than even that. These will work on any file you pick.

Important to your engineers are tools like diff which will let you see what the differences between two files and git which stores files and all of their versions. That is why you want your infrastructure as code: you can see what changed, when, why, how often it's changed, all without specialized tools. You can create patches to share those changes with others, let them review the changes and let them change their version to match yours. It would be impractical to create specialized tools for every file format do these things, and even if we did it's unlikely that they would interoperate correctly. Plain text is best thought of as an engineering constraint: by limiting how my file format works to what these tools are good at doing, I gain access to all of these tools for free just like you can copy/paste, rename, and move any file, text or not. If you're just here because you want to know why the software developers in your life are obsessed with doing things As Code, you can stop reading now if you like. From now on we're going into format design.

Why Plain Text Is Bad


So if plain text is so great, why do we even have anything else? There are actually quite a few reasons. The first, and I think is most obvious, is size and space. You can define an image in text. We could say that there's an image format which only supports black and white pixels in plain text like this: it starts with a line with the image width and height in ascii separate by a space, then below that line are height lines of length width consisting of a 1 for white and a 0 for black. A smiley face might look like this:


8 8
10000001
01011010
01011010
01111110
00111100
01000010
01111110
10000001

But that's a lot of text for such a small smiley face. Even worse, what happens if we add color? Even worse, how would we define a sound in text? Some kinds of data are simply awkward to represent in text in a way that's easy to manipulate, which is the entire point of representing them in text. Another example here is a zip file: the whole point of compression is to make the file as small as possible, so compression into plain text is counterproductive outside of a specific set of circumstances (mostly e-mail, these days.)

Another reason to not use plain text files is because of the parsing involved. Because the point of a plain text file is that it can be manipulated by hand, if you're using plain text then you inevitably need to be a little more forgiving of what you accept. Some text editors add an extra blank line at the end of the file for example, and you need to deal with that. Maybe your program would always write out a decimal number to 8 places, but the person editing by hand got lazy and stopped at 5.3. Maybe for performance reasons you can't afford any transformations on the data at all and the file you need to read is exactly how it's represented internally in the program. By letting any tool manipulate the data, you have to accept that it may not be exactly how you left it.

The final reason is, quite frankly, lock-in. The whole point of a plain text format is that it's easy to understand and manipulate. If your product uses a binary format and your competitor wants compatible with Contiki Office Suite as a checkbox on their next release then they have to put a lot more work into reverse engineering Contiki's file format. That format might even be intentionally obfuscated to make this harder and buy you more time.

Like any choice there are a few tradeoffs. While a lot of file formats can sensibly fit into the constraints needed, when you can't meet those constraints at all it's luckily fairly obvious.

The Spectrum Of Plain Text


When I was first defining plain text I referred to it as a spectrum, what do I mean by that? I'm going to pick on a few famous file formats which I feel go from most to least plain text-ey and explain why in order for you to get a feeling for it.

Text


The good old .txt file. It has no structure at all, it's just as series of whatever bytes you turn it into. In some sense this is the most useful format with these tools, but it can also be a little harder to work with because there's no structure at all. A poorly planned search+replace could end up replacing much more than you expect. I can't think of what a specialized tool to view a plain text document would even be.

CSV


The venerable CSV file is probably the oldest database format you'll encounter in the wild. It is a series of lines, each of which consists of a series of columns, each of which is separated by a comma. Very easy to work with, the only real gotcha is the need to properly handle escaping (we'll get to this in the next section,) you can easily and freely reorder lines, and tons of tools either support it explicitly or implicitly. The only reason I reach for a specialized tool (I recommend csvq, for what it's worth,) when dealing with CSV is usually because the file is in the thousands of lines or bigger and is hard to manipulate in my head.

JSON


JSON is a good middle ground format to me. While it's relatively easily manipulated and written by hand in many aspects, there are a couple papercuts: mostly that commas are an explicit object separator and the nested nature mean that more often than not when dealing with JSON I reach for a dedicated tool. The best I've found there is jq or I write a short script using a Python or Perl JSON library. Sometimes I use a generic tool, but when using those I often find it easier to get the solution almost there and correct it by hand. In my opinion the slight loosening of requirements in JSON6 move that particular format close to the plan text end of the spectrum.

YAML


To keep it short and sweet: YAML combines most of the problems with JSON with a bunch more features which make it very hard to manipulate without dedicated tooling, and the YAML spec itself is so complicated that even that dedicated tooling often fails to consistently and correctly work with YAML between implementations.

ISO9660


You can consider this a lot of binary files which can contain plain text data. You probably won't manipulate it with plain text tools, but you can still search for text inside of them and maybe manipulate that specific text. It may be hard to add to what's there due to file offets or size limitations, but seeing if a substring is inside of the file or changing the contents of that string without changing the length is on the table.

Zip


The worst possible case for generic tools. Not only can you not manipulate it, it's often addressed by offsets, and because the contents are compressed you can't even meaningfully search for some text within the file itself. You really can't do anything to this (or any similar compressed file,) without specialized tooling.

Designing Plain Text Formats


Now that we have a good understanding of these formats, a few tips if you're designing one yourself.

First, consider escaping. If it's possible to represent anything meaningful to the format in the data itself, you need a good way to escape it. The traditional way to do this is with a backslash \, so try to follow the principle of least surprise here. Of particular note of how poorly HTML does it: &lt; is, in my opinion, an abomination. I'm not sure why it couldn't have just been \<, I'm sure there's some good historical reason though. Second, think very carefully about your delimiters. CSV is very convenient because it's well delimited, and also has 2 very strong and distinct delimiters (newline and comma.)

Don't rely on non-newline whitespace. It's hard to hand-write and hard to debug. It's very easy to accidentally insert or delete a space somewhere and not notice it, especially on large screens.

Make it easy to re-order within a conceptual block. The fact that a comma on the last key/value pair in JSON is a syntax error makes it annoying to script around. I often just always generate with the trailing comma and then fix it up manually after. Avoid nesting if possible: often you'd like to work with regular languages, which make it impossible to do things like paren/brace matching in a guaranteed way. If you can get away with this.key.nested.name instead of {this { key: { nested: { name: "value" }}}} your users will have an easier time. Finally: try to be line oriented. Most generic tooling does rely on the concept of lines as a separator. Use this to your advantage and try to avoid making programs working on your format look beyond the current line for context to do what they need to do.

Conclusion


Alright, it's 2 AM now and I've been thinking about this for a few hours. Hopefully this gives you a better grasp on some file format design, history, and pros/cons.

What Enough Technology Means


Introduction


It's traditional (at least for me) to spend my first post on a new blogging platform talking about, well, the program I wrote to be my new blogging platform. Right now it's very barebones, soon it's going to be a complete website. The platform itself is trivial, I'm going to include every line of source (as of right now,) in the post itself as well as talk about the infrastructure that makes it all run and it shouldn't take more than a few minutes, but first I'm going to introduce myself.

I'm Danny; I recently quit my job in order to try and make it on my own after nearly 8 years working on an Analytics platform which processed a lot of data. Not Google amounts, but a lot. If you live in an industrialized country (and a lot of developing ones,) your data has passed through software I worked on even though you almost certainly haven't heard of the company. Now I'm on my own and want to spread my skills around more. Is this blog an ad? Yeah, kinda I guess. If you need a hand with wrangling AWS, communicating with Kafka, fighting Flink, feel free to reach out. There's going to be a contact form on here when the business proper is set up. If that was all greek to you and you just need someone to help figure out what your business needs as far as the software, feel free to reach out too. I'll at least try to get you pointed in the right direction.

My core point on this blog and in my practice is that solutions and their scale have to be scoped to match their business. I could have written this blog in a high performance language like Rust. I could've deployed it onto a cloud platform load balanced, automatically scaled, running on top of a serverless database with pay-per-use functions. Quite frankly: I'm one person. We'll get into the details later, but that's a lot of lock-in and a lot of extra work for an amount of traffic that I'm not going to get in my wildest dreams. There are people who need that scale: if your data is measured in petabytes and your events in millions per minute those make sense as a business. I want to work with you to find out where you are, where you're going, and where on the spectrum is going to fit best going forwards without causing problems if you do find yourself bigger than you'd expected. With that out of the way, let's look at how the website is made as a case study, shall we?

Analysis And Code

The website is a professional blog for a professional software developer familiar with development and administration. It receives approximately 0 traffic as it's literally just started, but ideally it could scale up to hundreds or thousands of legitimate hits a day. If it's getting hits in the millions then something has gone horribly right and its owner won't need to worry about analyzing blog software anymore. Because this is 2026 that level of traffic may be possible due to an AI Distributed Denial of Service attack (aka crawling,) so it has to be considered if nothing else, though shedding that traffic is acceptable. Features such as comments frankly just become cesspools of spam and open up GDPR concerns, so there's no need for those. There are, however, people who would prefer to use feed readers rather than go to the website and we should support them. Additionally just as a best practice the website should conform to all reasonable accessibility requirements, allow linking to single posts, and provide a good experience in general to its visitors.

There's plenty of fantastic software out there for this sort of thing. You may have heard of Wordpress. If you're more technical maybe Hugo or Jekyll spring to mind. The latter are what's called static site generators and are (in my opinion) the correct answer here as a category. Rather than generating the page for the website on every visit, these site generators render the page once (because it's the same for every visitor,) then all the server has to do is read the file from the hard drive. Nothing in our needs requires dynamic behavior so we have the option here. It's cheap, it's fast, it's very easy to extend as needed, and there's even off-the-shelf software to do it. In this case I wrote my own, because I needed something to talk about. Accessibility, in a shocking number of cases, pretty much falls out for free if you write your web pages as web pages with meaningful HTML and accessible CSS.

If you want it, here's the complete source to the static site generator as it exists right now. If you're not interested in code, you don't need to understand it. I'm not going to go through line by line or anything.


#!/usr/bin/env rc
# RC supports multi-line strings rather than hacking them in with heredocs.
# I chose %VARIABLE% because it doesn't conflict with existing shell syntax.
# Some of these could be made defined more locally, but if I change them I'd
# like them to be right at the top.

domain=justenough.technology
blogname='Just Enough Technology'

htmlheader='<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>%TITLE%</title>
	<link type="text/css" rel="stylesheet" href="/style.css">
        <link rel="alternate" type="application/rss+xml" title=RSS href="feed.rss">
    </head>
    <body>
'

htmltail='</body>
</html>
'

rssheader='<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
	<channel>
		<title>' ^ $"blogname ^ '</title>
		<link>https://' ^ $"domain ^ '/</link>
		<description>' ^ $"blogname ^ '</description>
'

rsstail='	</channel>
</rss>
'

posts = `{ls posts/*.in | sort -r}

fn getid {
	cut -d '/' -f 2 | cut -d '.' -f 1
}

fn genhead {
	sed 's/%TITLE%/' ^ $1 ^ '/'
}

fn genpage {
	page = $1;
	id = `{echo $page | getid}
	title = `{head -n 1 < $page}
	rest = `{tail -n +2 < $page}
	echo '<div class="post"> <article id="' ^ $id ^ '" class="content">'
	echo '<a href="/' ^ $id ^ '.html"><h1>' ^ $"title ^ '</h1></a><hr/>'
	echo $rest
	echo '</article></div>'
}

fn genentry {
	# Duplicated from genpage but there's no nice way of doing this without weirding scope.
	page = $1;
	id = `{echo $page | getid}
	title = `{head -n 1 < $page}
	rest = `{tail -n +2 < $page}
 	echo '	<item>
		<pubDate>' ^ $"id ^ '</pubDate>
		<title><![CDATA[' ^ $"title ^ ']]></title>
		<description><![CDATA[' ^ $"rest ^ ' ]]></description>
	</item>'
}

cp style.css out/

# Generate each post.
for (i in $posts) {
	postid = `{echo $i | getid}
	outfile = 'out/' ^ $postid ^ '.html'
	title=`{head -n 1 < $i}
 	echo $htmlheader | genhead $"blogname ^ ' - ' ^ $"title > $outfile
	genpage $"i >> $outfile
	echo $htmltail >> $outfile
}

# Now make the feeds (index.html and the rss feed)
echo $htmlheader | genhead $"blogname ^ ' - Home' > out/index.html
echo $rssheader > out/feed.rss

for (i in $posts) {
	genpage $"i >> out/index.html
	genentry $"i >> out/feed.rss
}

echo $htmltail >> out/index.html
echo $rsstail >> out/feed.rss

That's it. Fewer than 100 lines. It is written in an obscure shell, but we'll get to that. This checks every box I have for this blog software right now. This is exactly what I need for now and all the growth I project in the forseeable future, and it's (in my opinion,) beautiful for that. The input files are one line at the top of a plain-text title, then the rest is just HTML.

Of course having the code is just a small part of the story. I also have to make this readable. Don't worry, we're getting to the whole stack. I want to address some of the thought process which went into why I came to this exact solution, though. I could've used Jekyll. I didn't have it installed and I'd have less to talk about.

Make is a natural choice, what we're doing is generating output files (html) which is directly derived from input files (my posts.) There were two problems there, in my estimation: first most of the nice features of make break when you're spread across directories. It has a hard time inferring that out/5.html is actually supposed to correspond to posts/5.in so I would need to define a rule for every post. Second is that as defined by POSIX, make is remarkably incapable. One of the biggest blockers to writing portable Makefiles for native code is that linker flags are managed by something called pkg-config, which has to be called as a program. GNU Make and BSD Make, the two main dialects, have mutually incompatible syntaxes for assigning the output of a shell command to a variable. Additionally in order to create the index and RSS feed, I'd need a special case which is semantically awkward and, I think, impossible portably.

I could've used Go or Rust, but most of what this program is doing is opening, closing, and reading verbatim from small files. What would that performance buy me? Maybe I could measure it in seconds when I rack up thousands of posts.

I could've used Perl, since it's very good at text manipulation. In fact, this codebase is inspired by Ube, a set of Perl scripts which I later rewrote into rc (the same language here,) when I migrated my personal server onto plan 9. That's why this software is named Yamaimo (another type of yam.) The manipulation isn't deep enough to justify it. Like I said above, I'm almost exclusively just concatenating canned strings and verbatim files.

So I came to a shell script. I just wrote it in rc because, quite frankly, it's my favorite shell for scripting and I had it installed. The contents themselves are boring and largely invisible, like a tool should be. If you really want me to take you through it step by step, ask for it. By the way, feel free to consider that source (and all of the scripts in this post) under the ISC license.

Generating the posts, like I said, is only half the problem. What else could we lean on to help us? In general I'm somewhat skeptical of the term Software Engineering. In my opinion most of what the industry does is kind of a joke compared to the rigor involved in real engineering with a stamp and personal liability if the bridge you designed collapses and kills people. However one of the things that this industry has gotten painstakingly correct is version control. If you ask anyone who's worked on both sides, what they miss from software is version control. Every version of every document is tracked, can be moved between computers, can be saved away to experiment with changes, can be compared with any other version to find every difference. It's an incredibly powerful tool. We want our code and posts to be in version control anyway, so why not use it to do some of the heavy lifting for us? This is the script which will handle making a new blog post for me:


#!/usr/bin/env rc

id=`{date -u -Iminutes}
infile='posts/' ^ $id ^ '.in'
tmpfile=`{mktemp}
$VISUAL $"tmpfile &&
mv $"tmpfile $"infile &&
git add $"infile &&
git commit -m $"id &&
./build.rc &&
cd out &&
git add $"id ^ '.html' index.html feed.rss style.css &&
git commit -m $"id &&
git push

It creates a new file with the post's ID (the current utc timestamp down to the minute,) lets me write it, adds it to version control, commits it, builds the html files, adds those to version control, commits them, and pushes them to the server. If I have to go back and edit a post I have to perform some of these steps manually, but that's a rare operation which can be done by hand.

We've pushed to our server, now what? Git (the version control system I'm using,) has a feature called hooks which allow yet more scripts to be run when certain events happen. I added a post-update hook to copy the files into the web server when an update is pushed. This one line is left as an exercise (it's actually surprisingly tricky. Look for GIT_DIR in documentation.)

That's absolutely everything. As I said all code here can be used under the ISC license, and you can always see the latest version here.