$ cat post

·

·

xcwds: Everyday Tools That Never Phone Home

Originally, xcwds had no meaning. It was just a fun string of five characters to type on a keyboard, and since it’s five (seemingly) random keys, it had the benefit of being available as a dot-com domain.

I started working on xcwds because I wanted to use up some Claude Code cloud credits before they expired. The first addition was a parody recipe I wanted to share with some friends. Adding it was mostly about convenience: deployment and the domain were already set up, so I just needed to merge a PR and it would go live. Then, my friends were one click away from reading the epic adventure of Nana Birdie, the orchard, and Tyler’s propensity towards unseasoned cuts of meat.

Tyler’s pork chops did not speak. They screamed. Silently. Into the void.

What xcwds became

A few days later, I decided to keep building: a dedicated recipes page, a few utilities I thought I’d find useful, and basic navigation to make the site usable across devices. The URL Sanitizer was the driving factor behind turning the static site into a PWA. Now that the app is installed on my phone, I can share links to it with the native share sheet and they load directly into the URL Sanitizer, which shows the URL as a series of components instead of just a string of characters.

After having the PWA installed on my phone, I went looking for the ugliest URLs I could find. Luckily, my Facebook feed had plenty to offer. After clicking on a few ads and seeing the URL in my browser, I clicked Chrome’s share feature, selected xcwds, and was presented with a URL stripped of known tracking parameters. There’s one very important thing to understand from a privacy perspective about this.

Most tracking data in URLs lives in query string parameters. If you see a question mark in a URL, that’s the start of the query string. It’s an essential part of how the web works. The URL Sanitizer works by detecting known tracking parameters and removing them from the link. This also means the URL Sanitizer only works if the tracking parameters are included in the query string. The main utility of the URL Sanitizer is to remove tracking info from links you are sharing. A secondary utility is to replace all tracking parameters with the value BUTTSBUTTSBUTTS.

The heavy focus on privacy in the URL Sanitizer—the reason I turned xcwds into a PWA to begin with—led to another pivotal decision: the app should not make external requests. This decision serves two purposes:

  1. Ensures the app runs without a data connection and without a server
  2. Prevents the app from leaking sensitive data

The way links are shared to xcwds matters too. Instead of using a query string, it uses the URL fragment (everything after the #), and browsers never send the fragment to the server. This keeps the URL you share to xcwds from ever leaving your device. Thus, xcwds finally stood for something: eXecutes Client-side, Without Data Servers.

No data servers, on purpose

Because xcwds is hosted on GitHub Pages, it has to be a static site: no server-side routing, no central database storing everyone’s info, just files generated at build time. Any integration with a server would mean either a) calling a service I don’t own or b) developing, maintaining, and hosting a separate server. Neither sounded appealing. What did sound appealing was a set of tools that just work when I want them to. No in-app notifications, no “give us your email,” no “what’s your mother’s maiden name, the street you grew up on, and the last four of your social?” Just useful stuff that works.

A quick tour

The weightlifting calculator lets a user quickly calculate total weight for a lift. They can either select the weights they have already loaded onto the bar to calculate the total weight, or enter a target weight and it will calculate what plates should be loaded. The formula is the weight of the bar plus the sum of all the plates. The default is for a 45 lb barbell loaded symmetrically. Other tools include:

Settings are stored directly on your device, and import and export let you share them across devices.

How it’s built

GitHub Pages was the first architectural decision, and it directly shaped several that followed. I chose it mainly because it’s easy to set up. The custom domain was about being easy to remember and type.

The app is built with SvelteKit and Tailwind CSS. I could have built the UI with React, Angular, or Vue and generated a static site, but SvelteKit and Tailwind provide what I need with less bloat. If the app ever outgrows what SvelteKit can do as a static site, that’s probably a sign it’s no longer a good fit for GitHub Pages.

AI is involved in nearly every part of the SDLC. Instruction files guide agents through everything from feature implementation to adversarial PR reviews and code audits that surface issues worth addressing. That alone doesn’t guarantee every issue gets caught before deployment, but it goes a long way toward reducing AI slop.

What was hard

Without a backend, every feature has to run entirely client-side, and any reference data has to ship with the app. One major headache was turning the app into a PWA before building an update mechanism. This meant that any time I deployed a new version, I needed to uninstall the app, go back to the website, and save the app to my phone again. This made implementing a proper update mechanism a top priority and I did not reinstall the app until it was complete.

The app update mechanism works by installing a new service worker and setting a flag that signals an update is available, causing the app to display an update banner. Clicking the update button lets the new service worker take over, and the page reloads. The banner also warns you if the update would interrupt anything in progress, like a running timer.

What’s next

xcwds will remain available for anyone to use and its code will remain open source. I’ll keep adding features, fixing bugs, and continuing the epic of Tyler’s unseasoned pork chops.


cd ../posts