Skip to main content

Migrating My Blog to Docusaurus and Cloudflare Workers

· 7 min read

When I initially published my developer blog, I chose to use Jekyll and GitHub Pages. The decision was simple. I already used GitHub for repository hosting, so I had easy access to GitHub Pages and its built-in support for Jekyll. With Jekyll, I could generate a static website from Markdown posts, allowing me to focus on my blog content instead of wrangling with frontend code and infrastructure. The framework was (and still is) popular and well-supported, so there were plenty of tutorials available.

However, GitHub Pages has some major shortcomings. It has no support for pre-production environments, so you are forced to release directly to prod with no validation in between. Although my personal blog is not a mission-critical service, as someone with an SRE background, this situation always made me nervous.

Defense Against Stolen OAuth 2.0 Tokens

· 8 min read

In the News

HTTP authentication tokens are an attractive target for hackers. Stealing an access token allows an adversary to compromise a user account without having to break passwords or two-factor authentication. In a [recent attack] on content creator Linus Media Group, an employee was tricked into executing a malicious email attachment that copied browsing data off their computer. Using the access tokens, the attacker was able to create YouTube livestreams, delete videos, and edit profile information.

It is difficult to detect and stop token-based attacks before damage is done. Modern authentication protocols like OAuth 2.0 are designed to offload security tasks to third-party providers. When additional safeguards are built into the system, they often incur significant infrastructure complexity and performance costs.

Alpine Linux Containers

· 4 min read

What is Alpine Linux?

Many official Docker container images come in an alpine variant. They are based on [Alpine Linux][alpine], an incredibly lightweight distribution that results in smaller image sizes. For example, the official Alpine-based Golang image is almost 3x smaller than its Debian-based counterpart:

docker pull golang:1.19-bullseye
docker pull golang:1.19-alpine
docker images --filter 'reference=golang' \
--format '{{ .Repository }}:{{ .Tag }}\t{{ .Size }}'
golang:1.19-bullseye    993MB
golang:1.19-alpine 354MB

Alpine Linux achieves these gains by omitting many packages that are useful for a desktop OS, but are less important for a container image. The distribution uses musl libc instead of glibc, and busybox instead of dash or bash. Unfortunately, the barebones environment does result in a steeper learning curve and may cause incompatibilities with some projects.

Examining the Problem of Forward References in Python Type Annotations

· 4 min read

Type Hints

Python 3 introduced a new syntax for adding type hints to variables and functions:

class Container:
def __init__(self, name: str):
"""Initializes an empty container with the specified name"""
self.name: str = name
self.items: list[str] = []

Python is not a strongly typed language, so type hints are not checked at runtime and mismatches do not cause an error. Rather, the annotations are intended to be processed by third-party static analyzers like [Mypy][mypy], to detect bugs as part of the CI process. Type hints also serve as an indicator of a library developer's intent and can be used to generate richer API documentation.

Forward References

One limitation of the annotation syntax is the ability to handle forward references.

class Container:
def transfer(self, other: 'Container') -> None:
"""Moves the contents of this container to the other container"""
other.items.extend(self.items)
self.items = []

An Intro to Dev Containers in VS Code

· 5 min read

The Problem with Host Workflows

Running a development instance of your application often means installing its dependencies on your host computer. You'll need to consider operating system packages, compilers, and language-specific frameworks. For example, even a simple project like this blog site requires a particular version of Ruby, Jekyll, and other gems.

Problems arise when you have larger projects with complex installation requirements, or multiple projects with conflicting dependencies. Framework-level issues can be solved with tools like RVM, which allow the developer to switch between multiple concurrent installations of Ruby. OS-level issues are often trickier. For instance, on Debian Bullseye, apt-get install gcc provides GCC version 10. What if your project requires a newer or older compiler?

On the deployment side we have solved these problems using Docker containers. Each application can have its own set of container images with an isolated environment. We can apply the same techniques for local development.