I’ve added a lot of new people to my blogroll recently, many of them new, but some being people who I discovered are still posting and used to read regularly. If you’re looking for more to read (mostly technical in nature but sometimes beyond that), there’s a lot of great writers on there.
At some point, Jekyll added previous and next properties to posts (or maybe it always had them and I wasn’t paying attention), so I made a change to use them when generating relative links in news posts. This doesn’t affect the final product you see, but it’s much less dumb than the old code I wrote.
I switched to using CSS Grid Layout which allows for more precise control. I don’t have any redesign plans or ideas, but if I ever feel like changing something, this will provide a much better base to start from.
Inspired by Utopia’s fluid responsive design, I used their type scale calculator to make font sizes more flexible. As screen size increases, you can use a greater range of font sizes to keep the same visual impact; or, thinking the other way around, as screen size shrinks, you can’t use as large a range of font sizes without them becoming unreadable or obnoxious. If you open the site on a PC and drag the window wider or narrower, you’ll see the dynamic scaling in action, but the end result is that whether you’re on a small screen or giant monitor, text should be a readable size and headings should feel like a good scale.
I also use modern font stacks (hat tip to OG Jeffrey Zeldman) to set fonts now, which should also be nicer and more readable without requiring custom fonts.
I won’t go as far as some people and tell you to disable JavaScript entirely, but I do try to keep it minimal on here, so I changed somefunctionality to run during the Jekyll build instead of as JavaScript in the browser. I also changed the menu to use an HTML popover so it doesn’t need any JavaScript either, and it still switches behavior conditionally on screen width: small screens (e.g. mobile) have a button to toggle the menu, large screens show it in the right column.
I finally got styling in place to support dark mode. I think pretty much everything works and looks how I want, across all the different pages and sections. I used the light-dark() CSS function to handle defining which colors to use in which mode, and some use of oklch() for computing colors more dynamically in case I ever want or need to tweak them. I also replaced the static background image with a linear gradient so it can also use light-dark() and respond to dark mode.
Overall, the site should be easier to read, on pretty much every screen size, device, operating system, and color scheme preference, than it was before, and I’m happy with the results.
I have a server at home running a few services that have web-based interfaces. They’re only available within my LAN, but I still want them to use HTTPS/TLS, because software increasingly wants to use HTTPS all the time, and it’s good defense in depth. For a while, I used self-signed certificates, but with the increasing prevalence of devices like phones and smart TVs that don’t provide convenient ways to install custom certificates, I wanted a different approach, and I recently implemented a solution that closes that gap.
The prerequisite needed to implement this is a real domain somewhere with DNS that you can control via an API. Fortunately I have this, as wadny.com uses Cloudflare DNS, which has an API.
Additionally, you’ll want stable LAN IP address(es) for your server. I’ll show a typical example for IPv4 and a unique local address for IPv6. If you don’t have stable addresses for some reason (maybe you’re in a college dorm and everything is dynamically assigned), there are dynamic DNS clients that can identify your address(es) automatically and set them in DNS, which again requires some sort of API support. If you go this route, make sure the client is appropriately configured to find your LAN address(es) if you don’t want things available on the public internet.
The first step involves DNS, but not necessarily via an API yet. Create a sub-domain and wildcard record underneath it for your local services, with A records for your IPv4 LAN address and, if you have IPv6, AAAA records for that address. For example:
Record name
Record type
Value
local.example.com
A
10.0.0.1
*.local.example.com
A
10.0.0.1
local.example.com
AAAA
fd12:3456:7890:1234::1
*.local.example.com
AAAA
fd12:3456:7890:1234::1
These domains will be visible on the public internet, but the LAN IP addresses they point to will be conveniently meaningless to anyone else, so services will remain inaccessible to anyone outside your LAN. If you do want things accessible on the public internet, you need to use your public IP address and you need to be reachable from the internet. This may already be the case with IPv6, but in some cases with IPv4 you might have a bigger problem to solve.
The second step is to install a web server, such as nginx. To start with, it can listen on regular HTTP port 80 with some static placeholder content. In my case, I just installed the package provided by Debian using sudo apt install nginx and the default configuration came with a placeholder page. At this point, you should be able to reach your server via http://local.example.com or http://anything-you-want.local.example.com.
The third step is to get a TLS certificate from a real certificate authority, such as Let’s Encrypt. There are tools that can automate this process, such as Certbot. These clients have different ways of proving to the authority that you own and control the domain you’re requesting a certificate for, and different ways of setting up the certificate with your web server. Debian comes with a package for Certbot, which I would prefer to use if possible, although I ended up installing it manually due to some confusion trying to get it working.
Since, in this case, the web server is not available on the public internet, you need to use a DNS-based challenge to prove to the authority that you control the domain, which is why your DNS needs an API. If you use Certbot, it has a number of DNS plugins for interacting with different DNS services; choose the appropriate one, and make sure to install it.
Certbot is not designed to just set up a configuration file and let it run as a service, which is a bit confusing. You need to use a complicated certbot run command with a lot of arguments to set things up, but once the certificate has been obtained the first time, Certbot saves records of it and the options needed to refresh it so you don’t need to run the full command again. Instead, a cron or timer job can run certbot renew and it will automatically renew any certificates that are about to expire.
To install the certificate, do not use the --nginx argument! This is a plugin for validating the domain, not installing the certificate, and since the server is not publically accessible, it will result in validation failing! Instead, you want -i nginx to specify installation to nginx.
As an example, you might end up with: sudo certbot run --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare-credentials.ini -d local.example.com -d "*.local.example.com" -i nginx -v
Certbot installed the certificate by modifying /etc/nginx/sites-available/default but since we want to run multiple sites with TLS, this is not a great approach. I pulled its changes out of there and made /etc/nginx/conf.d/certbot.conf instead, with the SSL certificate settings and the include /etc/letsencrypt/options-ssl-nginx.conf; statement. Note that that included file duplicated some settings that are in /etc/nginx/nginx.conf by default, which nginx considers a configuration error; I changed /etc/nginx/nginx.conf to remove the duplicate SSL settings, rather than modifying the Let’s Encrypt file, in case Certbot regenerates it during renew.
We want to end up with multiple server configurations: one for each service being proxied, a default for anything else, and a non-TLS one just in case someone or something uses http: instead of https:. The “right” way to do this is to create multiple files in /etc/nginx/sites-available/ (one for each server block) and symlink them into /etc/nginx/sites-enabled/. I removed the link for the default site and left it alone as an example, and created a new file for each server block.
The default server should listen on port 443 using SSL/TLS, and use the fallback server name so that everything directs to it unless there’s a more precise match:
server{listen[fd12:3456:7890:1234::1]:443ssldefault_server;listen10.0.0.1:443ssldefault_server;server_name_;# Other settings here, e.g. root and locations}
The non-TLS server will redirect everything to https: for convenience:
server{listen[fd12:3456:7890:1234::1]default_server;listen10.0.0.1default_server;server_name_;# Rewrite all requests to HTTPSrewrite^https://$host$request_uri?permanent;}
At this point you should now be able to reach https://local.example.com or https://anything-you-want.local.example.com from any sort of device on your LAN without having to install or deal with certificates. Also, if you use http: instead, it should automatically redirect you to the equivalent https: address. Awesome!
Now you can add more server blocks to nginx that will reverse proxy to other services. Each service may have its own guidance on what you need to configure, which could include special headers, multiple locations, different protocols, and all sorts of complexity that I can’t predict. A basic example would look like this:
Note that we no longer have default_server in the listen statements, and we also have a full domain in server_name so that this specific domain will proxy to a new service.
If your service was previously set up to listen on your LAN, you can change it to only listen on the lo loopback interface or loopback IP addresses (e.g. 127.0.0.1 or ::1) so that users have to go through the proxy. This is generally a good idea, as nginx is a safer HTTP server than what may be built in to any particular service.
If your service supports HTTPS, you can keep using it between nginx and the service for full end-to-end encryption. You may need to re-generate a self-signed certificate to use localhost as the domain. You’ll need to configure nginx to accept the self-signed certificate from the service, adding some settings in the location block after proxy_pass:
Finally, you should be able to access https://service.local.example.com from any device on your LAN and have it be fully trusted automatically, without needing to install custom certificates.
I previously added support for mobile browsers (for small screens in general, really), but it turned out to be pretty lacking. Images or fixed-width content (such as the picture in The Cloud or the code blocks in hammer and nails) would blow up the layout and result in horizontal scrolling, which sucked. I finally fixed it now so that images will scale down; code blocks still require horizontal scrolling, but only within the block, rather than breaking the entire page.
Prelude: I would love to write more on here, especially longer and more thought-out posts on complicated topics, but as is plainly evident, it’s not really possible for me to find the time. So instead, maybe I can manage to post some things that are just technical info dumps. Hence: a summary of my terminal and shell setup today. I recently made some changes and improvements, so now’s a good time to write it up, if nothing else for my own reference.
Which operating system you use heavily affects which tools and systems are available. Pretty much all of my personal systems have coalesced to Kubuntu, which is stable and well-supported, and I prefer KDE to Gnome. At work, I use Windows 11, which in Pro edition with nonsense turned off by group policy is tolerable, but has become unacceptable for personal use.
Since I have a few different personal systems (desktop, laptop, HTPC, server), I want a way to synchronize configuration between them. I use homeshick (a Bash port of homesick) to manage configuration files (“dotfiles”) stored in Git repositories. It’s pretty technical, but it has quite good documentation and means that all my configuration can be synchronized easily. Once a change has been pushed, all it takes is homeshick pull on another computer to get all the updates.
At work, I don’t use any configuration management, as I only have one workstation and never remote into any servers, although there are at least two PowerShell ports of homes(h)ick (posh-homesick and homepsick).
The first configuration is for the terminal program I run. I’ve just stuck with the default terminal, Konsole. I know there’s new hotness out there like Ghostty, but for the moment the default Konsole works well enough to not be worth the time dealing with more configuration setup.
In my Konsole configuration, I don’t change very much. The most important part is supplying and using a nerd font so that extra glyphs are available.
At work, I use Windows Terminal which is not bad and eons ahead of the old-school Windows command prompt.
The second configuration is for my shell, which is Bash. Again, it’s a convenient default; not the best, but not bad. There are lots of alternatives (zsh, fish, elvish, nushell, powershell, xonsh), but it’s hard to overcome inertia enough to try to migrate across a slew of various computers.
My Bash configuration has a bunch of settings to make Bash more tolerable, especially with history and tab completion. It also uses Oh My Posh to display prompts, which is actually supported across every shell I listed above. My prompt is pretty basic, but I don’t want it to be too distracting. It looks like this:
A screenshot of my shell prompt
At work, I use PowerShell, which is actually quite powerful and cross-platform. It also uses Oh My Posh with the same prompt configuration.
Most of my editing work is actually done in VSCode or sometimes KDE Kate, but sometimes it’s easier to do a quick edit from the terminal, or I’m SSH’ed into a server and don’t have a choice, in which case I usually use Vim. Again (you may notice a pattern here), it’s convenient and available and good enough for basic work, even though there are definitely alternatives like Neovim, Micro, and Helix.
My Vim configuration uses some plugins from Tim Pope as well as airline so that I don’t have to try to install Powerline and Python everywhere. Mostly my configuration adds visual information to make it easier to see what’s going on; my vim editing tends to be limited to basic configuration files, rather than software projects.
At work, I basically never edit anything from a terminal, so it’s just VSCode.
I was just reading yet another Twitter thread from someone1 trying to get started with programming without any prior experience, and documenting the extensive trials and tribulations they went through in the process. As a software engineer, I understand the value of being able to get up and running quickly and easily with new tools and projects; making it easy and pleasant for new developers to contribute saves time and money, improves morale, and is just the right thing to do. These same benefits apply when novices are able to pick up programming as a skill to help them solve their problems.
Keeping programming and the concerns of programmers in the mainstream can also guard against incompetent accusations (like nitwit Missouri Governor Mike Parson) and fend off possible dystopian scenarios by giving more people the knowledge to immediately understand how wrong they are. Gatekeeping doesn’t benefit the profession and serves only to stoke the egos of insecure people. Programming tools that are accessible to novices are therefore beneficial and important.
But, and this is the part that I don’t see discussed often enough, critiquing every programming tool or language based on its accessibility to novices is not valid. Programming is a skill everyone should have access to take up, but software engineering is also a profession that people dedicate their careers to. It is entirely acceptable for tools and languages to have a particular audience in mind, and that audience might be “professional software engineers with training and experience.” Consider some examples from other areas:
Anyone can dig in their garden, but nobody hates on CAT for not being able to use a 500-ton mining shovel with no training. Anyone can look at a watch and tell time, but nobody complains that it’s more difficult to set up a network time server synchronized to GPS satellites with a roof-mounted antenna. The size and/or cost of these tools makes it obvious they’re intended for professionals. But with programming tools, we’ve almost become victims of the success of the open-source model: literally anyone can download and install Visual Studio Code and .NET or Node.js or Python and start using the same tools that experienced professionals at billion-dollar enterprises use for major projects.
Unfortunately, people think that because programming tools are so easy to obtain, that demands that they must also be easy to use, and that’s just a false assumption. When tools are designed and built for use by experienced professionals, accessibility to novices is simply not even relevant, and trying to make that happen can compromise utility or productivity for the very professionals they were supposedly made for in the first place.
Ultimately, people need to think about programming in similar terms to other DIY fields: anyone should be able to put together some furniture from Ikea, but building furniture from raw lumber takes some more effort and planning and if you try to just plow through it having never used power tools before, you’re in for a learning curve. This isn’t being elitist, it’s not gatekeeping, it’s just the reality that things are complicated.
I will not name the person or link to the thread because I’m critiquing the general argument, not the particular individual. ↩