Hello!

Mainly notes to myself and writing to think out loud.

All views my own.

SSH Key Best Practices

Today I needed to create a new SSH public/private keypair for work. I wanted to make sure I was following modern best practices for this, so I did some reading online. I found these two articles to be very helpful: SSH Key Best Practices for 2025 – Using ed25519, key rotation, and other best practices SSH Agent Explained Things I learned I didn’t ever think to change the comment at the end of the keyfile. Now I plan to date them, as it suggests. Using the email sub-alias (+) is clever and I’m stealing that. ...

2025-09-10

Treating strings as numbers in Golang

Today I encountered an API which sometimes sends back numbers as strings. Example response: { "age": "30" } That’s annoying, because I wanted my struct in Go to be like this: type Person struct { Age int `json:"age"` } I learned that json struct tags have a string option meant for dealing with exactly this sort of problem: The “string” option signals that a field is stored as JSON inside a JSON-encoded string. It applies only to fields of string, floating point, integer, or boolean types. This extra level of encoding is sometimes used when communicating with JavaScript programs: ...

2025-08-28

Even Easier Git Rebasing

Rebasing your feature branch on main regularly is a good idea. I used to do it like this: # update local copy of main git fetch origin main:main # rebase on local main git rebase main This always felt a litle off because I was worried I’d typo main:main and mess it up. This works just as well and feels much better to me: # update local copy of `origin/main` git fetch # rebase on origin/main git rebase origin/main

2025-08-28

How to add values to an HTTP query string in Go

Here’s how to add key/value pairs to an http.Reqest’s query string in Go: package main import ( "fmt" "net/http" ) func main() { // create a request req, _ := http.NewRequest(http.MethodGet, "http://www.example.com/", nil) // get the current query string from the http.Request values := req.URL.Query() // add or set values values.Set("myKey1", "myVal1") // overwrite! values.Add("myKey1", "myVal2") // append! // add values back to the request req.URL.RawQuery = values.Encode() // query string: mykey1=myVal1&myKey1=myVal2 // print the url fmt.Println(req.URL.String()) } The above program will output: ...

2025-08-26

Binaries on Macos

Path Note /usr/local/bin/ System-wide binaries go here! ~/bin/ My user’s binaries go here!

2025-08-22

Initial thoughts on Ghostty

I’ve been using Alacritty exclusively for over a year. It’s fantastic! But I listened to an interview with Mitchell Hashimoto and he talked about trying to make terminal multiplexers obsolete. It struck a cord with me. I’ve been using Zellij ever since I switched from Iterm2 to Alacritty and it’s great, but also it can be cumbersome. I find it cumbersome to: Search through this history (I don’t do it frequently enough for the keybindings to stick in my head.) Splitting and closing panes. (Whereas iTerm2’s keybindings to do this are burned into my brain forever.) For all iTerm2’s faults, I miss it sometimes. (The biggest reason I ditched it is because you can’t commit its config to version control easily. Its config is huuuuuuge. And I won’t daily drive anything whose config I can’t version.) ...

2025-08-18

Using Github Issues

This interview with Simon Willison is great. It pushed1 me into using GitHub Issues for my private repositories. They’re wonderful. (In the past, I’ve been just tracking everything in Markdown documents. That worked, but was just OK.) As an example, I have a private GitHub repo with notes and documents for a private network I maintain (as a volunteer). It’s very ad-hoc and I sometimes go months between without working on it. ...

2025-07-10

HTTP/1, HTTP/2, and HTTP/3

There have been four1 major versions of HTTP in-use in the last 30-ish years: Version RFC Year HTTP/1.0 RFC 1945 1996 HTTP/1.1 RFC 2068 1997 HTTP/2 RFC 7540 2015 HTTP/3 RFC 9114 2022 It’s surprising to me how quickly HTTP/1.1 was released after HTTP/1.0. Less than one year! HTTP/1.0 A simple, text-based protocol. Runs over TCP. Only officially supports a single HTTP request and response over each TCP connection, then the connection was torn down. HTTP/1.1 Still a text-based protocol. Added connection re-use. On by default. Controlled via the Connection: keep-alive header and tuned via Keep-Alive. Added the Host header, which allows multiple websites to be hosted at a single IP address. Better cache control mechanisms. Added HTTP pipelining (allowing multiple requests to be sent in succession before responses were received). Apparently never got widespread support, though. Still suffers from Head of Line blocking. Even though TCP connections get re-used, a given request/response cycle must complete before the connection is eligible for re-use. Many browsers improved throughput by allowing up to 6 TCP connections per domain. Websites took this even further with domain sharding. Example: Sharding resources over www1.foo.bar, www2.foo.bar, www3.foo.bar gives you 18 connections to play with. No compression of headers. Lots of redundant data sent for each request/response in the headers, which is inefficient. HTTP/2 Based on pioneering work done by Google with their SPDY (pronounced: “speedy”) protocol in the 2010s. No longer a text-based protocol; now a binary-based protocol. Allows request multiplexing over a single TCP connection. Solved the application-layer Head of Line Blocking problem, but did not solve it for the transport layer (e.g., a dropped TCP packet would still delay all requests/responses behind it). Adds header compression. HTTP/3 Still a binary-based protocol. Moved from TCP to UDP+QUIC for the transport layer. Many improvements come from this. Faster connection setup Old: Setting up a TLS connection with older protocols required around three round trips (3-RTT): the TCP handshake (around 1-RTT) and TLS handshake (2-RTT). New: Setting up a TLS connection with QUIC requires a single round trip (1-RTT) because the transport connection setup and TLS handshakes are merged. Note that 0-RTT resumption is possible with QUIC as well. Avoids the transport-layer head of line blocking problem by using multiple streams. Head of line blocking now fixed at both HTTP and transport layer. Faster connection migration via Connection IDs. (Allows a client to switch networks seamlessly while browsing.) Closing It was interesting going down this rabbit hole on HTTP. ...

2025-07-09

System Design Patterns by Sean Goedecke

This blog post by Sean Goedecke was a great read on high-level system design patterns. Most of them were familiar to me, but I got several good ideas from it. Using a database as queue: Sometimes you want to roll your own queue system. For instance, if you want to enqueue a job to run in a month, you probably shouldn’t put an item on the Redis queue. Redis persistence is typically not guaranteed over that period of time (and even if it is, you likely want to be able to query for those far-future enqueued jobs in a way that would be tricky with the Redis job queue). In this case, I typically create a database table for the pending operation with columns for each param plus a scheduled_at column. I then use a daily job to check for these items with scheduled_at <= today, and either delete them or mark them as complete once the job has finished. ...

2025-07-02

Tools to Organize Your Life

Much productivity advice on the internet is too complicated and not actually that helpful. Complicated systems are less likely to be used due to the friction of using them and are more brittle in the face of changing needs. You are better off use basic, flexible systems; you are more likely to use simple systems and simple systems can adapt to change more easily. There are only a few tools you need to completely run your personal life. They are: ...

2025-06-25