This weekend, I started off strong by going to the range with an old friend and then catching up over lunch. As far as the tech side of things, I broke down my serverless setup as it’s more expensive than a Wonderbox hosting containers. With that said I dusted off my nginx load balancer container, killed my ELB, and elastic ips, and retooled dns back to it’s humble roots. The following is Claude’s summary of the new features we implemented and the things we troubleshooted.
Dev work done this weekend
This weekend was a productive infrastructure and tooling sprint on the blog2/vacuumflask project. Here's a rundown of what got built and fixed.
Media Expiration System
Added a full lifecycle management system for media files. You can now set an expiration date on any file in the media library. After that date, a cleanup job removes it from S3 automatically. The expiration is stored in SQLite, visible in the media library UI with an edit button on each card, and also available at upload time. When a file is deleted manually, its expiration record is cleaned up too.
Headless API Authentication
Built a /api/login endpoint that issues a short-lived Bearer token using
VACUUMAPIKEYSALT + TOTP — no plaintext password required. This lets automated
scripts authenticate without storing credentials, using AWS Secrets Manager for the secret values
and pyotp for one-time passwords.
Lambda Cleanup Cron
Created cron/cleanup_expired.py — a Lambda-compatible handler that logs in via the
headless API and calls /admin/cleanup_expired. It logs structured output to
CloudWatch under the cron log group via watchtower, with each run
getting its own stream. Also ships as a local cron.sh that can be called from the
system crontab, scheduled for 12:01 AM daily.
MCP Server
Made the blog discoverable to AI assistants via the
Model Context Protocol. A FastMCP server exposes
blog posts, tags, and search as resources and tools. It runs as a Docker container with SSE
transport proxied through nginx at /mcp/, and is advertised to clients via
/.well-known/mcp.json and a tag in the page
header.
Blog Styling Modernization
Rewrote style.css with CSS variables, a sticky header, card-style post layout,
and a modernized tag cloud panel. Fixed a specificity bug where tags were rendering white-on-white,
and another where tag size weighting was overridden by the admin nav styles — so the tag cloud
now correctly reflects content volume.
Nginx + Certbot Infrastructure
Rebuilt the load balancer container to manage its own TLS certificates. On startup it generates self-signed certs so nginx can start, then immediately replaces them with Let's Encrypt certificates via the HTTP-01 webroot challenge. A cron job inside the container handles daily renewal at 3 AM and 3 PM. Certificates persist across restarts via Docker named volumes.
Redis Connection Fix
Tracked down a TimeoutError in the Redis client caused by a stale EC2 internal IP
in the server's .env file. The Flask container runs with --network=host
but Valkey runs in bridge mode, so Docker's loopback forwarding doesn't apply. The fix was to
use Valkey's Docker bridge IP (172.17.0.2) directly.

I gave it a try today with LM Studio and a 48GB Deepseek model. It looked promising at first, but it never finished any of the prompts.

My laptop should have the resources to run a bigger model, but it was quite slow with this model. In other news, after renting a Walther PDP compact with a nice Holosun optic, which was fine. I managed to get the tightest grouping at five yards that I’ve ever shot with my Glock.
.
So this weekend, I set about solving a problem that was bothering me. When purchasing a pewpew at auction, what should the maximum bid be, assuming we want a specified percentage discount off the retail new price for a given item?
Building on an existing formula I had worked out to calculate the savings percent over retail, I started working backwards.
Assumptions
- Auction items will require shipping
- Auction items will include tax plus an auction fee
- Auction items may or may not have shipping insurance
- Auction items will have a credit card payment fee
- Retail price will not include shipping
- Average Sale price info available online won’t include shipping, insurance, or credit card fees.
Variables
With the variables defined, now we can work backwards and then solve for the bid.
Last but not least, while solving for B, I tried a number of AI assistants. The winner ended up being ChatGPT, which was able to isolate B on the left side of the equation.
As I reflect on the absolute chaos that is 2025, I’m a bit taken aback by how much has changed this year compared to previous years. I lost a boss I liked, a gentleman who was the best engineer on my team, whom I thought would outlast me. The world has also been more chaotic than average. On the brighter side, I’m grateful for the new friends I’ve made this year.
For my midlife crisis, I’ve taken up shooting sports. When I was a kid, I was always shooting my bb/pellet guns, bows, and arrows. Even poked a few holes with arrows in my parents' aluminum siding. For me, shooting at the range has turned the volume down on a lot of my older vices, such as gaming.
2026 will be the year of Kubernetes for me at work. Here is hoping 2026 > 2025.
- Adding a virtual TPM
- Adding a virtual EFI Disk
- Switched the BIOS to OVMF
- Used a win 11 usb drive, cleaned up some bad entries in the MBR
- Repaired the MBR
- Converted the disk to GPT using MBR2GPT
- Ran the Windows 11 PC check and verified that everything is order
- Upgraded to Windows 11
from getpass import getpass
import requests
import json
import os
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
username = "colin@cmh.sh"
password = getpass()
session = requests.Session()
session.headers.update({
'Accept': "application/json",
'Content-Type': "application/json",
'Cache-Control': "no-cache",
})
url_authn = f"{os.environ.get("OKTA_ORG_URL")}/api/v1/authn"
logger.info(f"auth URL: {url_authn}")
payload_authn = json.dumps({
"username": username,
"password": password,
"options": {"warnBeforePasswordExpired": True,
"multiOptionalFactorEnroll": True},
})
response = session.post(url_authn, data=payload_authn)
logger.info(f"auth response: {response.text}")
resp_obj = json.loads(response.text)
if resp_obj["status"] != "SUCCESS" and resp_obj["status"] != "MFA_REQUIRED":
logger.error(f"auth response: {resp_obj["status"]}")
raise "Login failed"
if resp_obj["status"] == "MFA_REQUIRED":
factors = resp_obj["_embedded"]["factors"]
logger.info(f"factors: {factors}")
factorId = getpass(prompt="factor id: ")
mfa_otp_url = f"{os.environ.get("OKTA_ORG_URL")}/api/v1/authn/factors/{factorId}/verify"
#https://developer.okta.com/docs/reference/api/authn/#verify-totp-factor
otp = getpass(prompt="OTP:")
mfa_payload = json.dumps({
"stateToken": resp_obj["stateToken"],
"passCode": otp
})
logger.info(f"MFA URL: {mfa_otp_url}")
mfa_resp = session.post(url=mfa_otp_url, data=mfa_payload)
logger.info(f"mfa response: {mfa_resp.text}")
resp_obj = json.loads(mfa_resp.text)
if resp_obj["status"] != "SUCCESS":
logger.error(f"mfa response: {resp_obj["status"]}")
raise "MFA failed"
logger.info(f"Successfully logged into okta. sessionToken: {resp_obj['sessionToken']} userID: {resp_obj['_embedded']['user']['id']}")