DevUtils.app, besides being incredibly useful, also injects a fun little whimsy in its random string generator. Ocean string!
Interesting seeing the rate of position players pitching over time. Look at that hickey stick growth! Almost a non-event now.
From Stop Wasting Everyone’s Time and Quit Already
The San Francisco Public Library app is pretty good for both physical books and ebooks. It’s able to check out an ebook and send it to Kindle without another app.
Wrote a blog post about using Uptime Kuma’s “push” monitors to track e.g. cron jobs.
Using Uptime Kuma push monitors
Uptime Kuma has a “push” monitor type which supports sending in status updates for something like a cron job to make sure it continues to execute. I use this for tasks like running backups or cleanup chores.
Aside: To avoid tying monitoring to my normal infrastructure, I’ve been successfully running Uptime Kuma on fly.io. I also recommend Cronitor for a similar commercial offering but its pricing doesn’t align well with my personal projects.
Normally to update Uptime Kuma you might append && curl …
to invoke the monitor URL at the end of a script. I think this is a little too limiting, so I’ve written a small script that wraps an underlying command with a monitor update:
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Usage: $0 <push_token> [command...]" >&2
exit 1
fi
push_token=$1; shift
start_time=$(date -u +%s%3N)
if [ $# -gt 0 ]; then
"$@" || exit
fi
end_time=$(date -u +%s%3N)
duration=$(($end_time - $start_time))
result=$(curl --fail --no-progress-meter --retry 3 "https://uptimekuma.example.com/api/push/$push_token?ping=$duration" 2>&1)
if [ $? -ne 0 ]; then
echo "Failed: $result" >&2
fi
This script is invoked with the token and an optional underlying command to execute and report to Uptime Kuma when the command is successful along with its execution time as the ping property. For example:
kuma 3EpwDA93fC docker system prune -a -f
I’ve automated this using the ansible-uptime-kuma Ansible collection to automatically create “push” monitors for recurring jobs and “http” monitors for web-facing services. This ends up looking something like the following for a push monitor:
- name: Store the monitor name
set_fact:
monitor_name: "{{ inventory_hostname }}-borgmatic"
- name: Create Uptime Kuma push monitor
delegate_to: 127.0.0.1
lucasheld.uptime_kuma.monitor:
api_url: "https://uptimekuma.example.com"
api_token: "{{ uptime_kuma_api_token }}"
type: push
name: "{{ monitor_name }}"
interval: 3600
- name: Get Uptime Kuma push monitor info
delegate_to: 127.0.0.1
lucasheld.uptime_kuma.monitor_info:
api_url: "https://uptimekuma.example.com"
api_token: "{{ uptime_kuma_api_token }}"
name: "{{ monitor_name }}"
register: monitor_info
- name: Set Uptime Kuma push token
set_fact:
push_token: "{{ monitor_info.monitors[0].pushToken }}"
- name: Create borg cronjob
cron:
name: "Borg backups"
job: "/usr/local/bin/kuma {{ push_token }} /usr/local/bin/borgmatic create"
minute: 33
This creates a monitor for my borgmatic cron and executes it hourly. When it fails to check in, Uptime Kuma sends me notifications, and when it succeeds it keeps track of how long it takes to execute. Perfect!
I track my reading in Calibre and export it to my website to disseminate. Just made my by-rating and by-author pages use sections. For example, all of the books by Jim Butcher that I’ve read. Significantly easier to link people to recommendations now.
Received my first Year of Sanderson box today, pleasantly surprised by the contents. I also enjoyed seeing my neighbors receive theirs in the mail room.
Interesting look at the history of the ferry system in the San Francisco Bay.
I’m surprised how many updates whois(1) receives on a regular basis.
If you have HomePods and worse-than-expected battery life on iPhone, try turning off Settings > General > AirPlay > “Transfer to HomePod.” It’s expensive in power as it’s constantly ranging.
In the screenshot, I turned off the setting during the middle charge. First time I have seen flat battery drain in months.
Another unexpected drain: the Finder device setting “Show this iPhone when on Wi-Fi” causes a large amount of energy usage by lockdownd, via saagar.
Discovered this using his excellent sysdiagnose energy log viewer.
Put together an app to compile user profiles from Slack into a Fediverse directory.
I tried out GitHub’s Copilot and I was pleasantly surprised at its helpful and timely suggestions. Navigated around a lot of Python-isms for me.
The Slack API was a bit more frustrating. To see custom fields you must issue 1 API call per user and the rate limit ends up at about 3 requests per second. Not so speedy in a nearly 4000 member one.
In this article, one of the SF Standard journalists sneaks into the Open AI office to try and find a subject to interview but ends up being asked to leave. It really paints them in a weird light, I wonder why they published it.
With unlimited options in the unlaunched gTLD .music
they chose belem.music (live, despite Mastodon not thinking so) as their domain name.
Random bug of the day:
The MatterAddDeviceRequest API new in MatterSupport requires adding the _matter._tcp
value to the Info.plist list of allowed Bonjour services, otherwise it errors with “Local browse failed; unable to start an add device operation” with error code -65555.
Fun new Seasonal post about artichokes and their wild history de-domesticating in California.
I needed to put Cloudflare in front of Caddy and realized that the X-Forwarded-For
header became less trustworthy since Cloudflare will pass along the value from the original request.
This is how I cleaned up the header without messing with Cloudflare rules.
X-Forwarded-For sanitization in Caddy
When reverse proxies like Cloudflare proxy a request, they communicate the original request’s source using the X-Forwarded-For
header. This can be dangerous: they will augment a value from the original request producing a value like Spoofed, Cloudflare-Given
.
When adding another reverse-proxy like Caddy into the mix you send upstream a value like Spoofed, Cloudflare-Given, Caddy-Given
. This is messy; I don’t want to teach upstream what the history of the request is and which proxies are valid.
The Caddy docs on reverse_proxy
indicate a way to fix this is by creating transformation rules within Cloudflare to strip the Spoofed
value in certain situations. This requires too much work and remembering to it when configuring a new site.
A tactical way to accomplish this is to use the semantics of the header value itself. Our downstream, trusted proxies are either setting the value to IP
or appending , IP
so we can disregard the earlier parts of the header value entirely. In a Caddyfile, this is done using the request_header
directive:
# Remove older X-Forwarded-For values since we only want to
# trust the last one iif we're going to trust at all.
# Regex information: https://regex101.com/r/KZknzS
request_header X-Forwarded-For "^([^,]*,)*\s*([^,]+)$" "$2"
# Later on in the server configuration somewhere
reverse_proxy upstream:1234 {
# Which proxies allow X-Forwarded-For to go upstream
trusted_proxies 198.51.100.0/24
}
When a direct request to Caddy comes in with a fake X-Forwarded-For
header, it’s not passed upstream because it’s not a trusted proxy. This is the normal behavior.
When a request comes in via a trusted proxy with an X-Forwarded-For
value like Spoofed, Cloudflare-Given
we pass to upstream Cloudflare-Given, Caddy-Given
without the Spoofed
part.
Interesting cascading effect of the Twitter sale: the .social gTLD saw a 435% spike in registrations.
The Defunctland video on Disney Channel’s Theme is enthralling. It’s an hour and a half of great storytelling surrounding the question: who wrote the theme, and when?
This opinion piece by Olivia Maki beautifully introspects the “post”-pandemic service industry:
Now, more than ever, perpetuating a service style that subjugates one person to another — server to served — only reinforces and exacerbates hierarchical thinking and encourages customers to dehumanize service workers. Modern service should be built on respect, which needs to go both ways to be sustained long term.