I read Yann Esposito’s blog post, How I protect my forgejo instance from AI Web Crawlers, and think that’s a great idea. My main concern with the crawlers is that they’re horribly written and behave poorly. My own Forgejo server was getting slammed with about 600,000 crawler requests per day. This little server is where I share tiny personal projects like my Advent of Code solutions. I wouldn’t expect any project there to get more than a handful of queries per day, but suddenly I was serving 10 requests per second. That’s not a lot compared to any popular website, but that’s a lot for this service, on this tiny VPS, on my shoestring budget.

Worse, the traffic patterns were flat-out abusive. All the content on this site comprises nearly static Git repositories. The scrapers try things like:

  • For every Git commit, fetch the version of every file in the repository at that commit.
  • See git blame for every file at every commit.
  • Attempt to download the archive of each repo at every commit.
  • Run every possible pull request search filter combination.
  • Run every possible issue search filter combination.
  • Fetch each of those URLs at random from some residential IP in Brazil that had not ever accessed my server before.

My first huge success at cutting through the flurry of bad traffic was with deploying Anubis. You know those anime girl pictures you see before accessing lots of web pages now? Well, those are part of a highly effective bot blocker. There’s a reason you’re seeing more and more of them.

And this morning, I also adapted Yann’s idea for my server which runs behind Caddy instead of Nginx. I made a file named /etc/caddy/shibboleth like this (but with the cookie name suitably altered to a random local value):

@needs_cookie {
    not {
        header User-Agent *git/*
    }
    not {
        header User-Agent *git-lfs/*
    }
    not {
        header X-Runner-Uuid *
    }
    not {
        header Cookie *Yogsototh_opens_the_door=1*
    }
}

handle @needs_cookie {
    header Content-Type text/html
    respond 418 {
        body `<script>document.cookie = "Yogsototh_opens_the_door=1; Path=/;"; window.location.reload();</script>`
     }
}

Note the extra X-Runner-Uuid line that Yann did’t have. This allows my Forgejo Action Runners to connect without going through the cookie handshake.

Then I added a line to the configurations for services I wanted to protect, like:

myserver.example.com {
    root * /path/to/files
    ...
    import shibboleth
}

This way I can easily reuse the snippet for any of those services.

Thanks for the great idea, Yann!

It’s about time for the annual attempt to migrate from OmniFocus to Reminders, which will probably fail spectacularly, as usual, reminding me (heh) why I’m glued to OF in the first place.

This is practically doomed and yet the season seems to demand the token effort.

I’ve heard friends talking about trying Zyn nicotine pouches and I want to shake them. They were created by a brand of Philip Morris, who spent decades claiming that cigarettes aren’t bad for you.

“But Zyn isn’t bad for you! There are studies!” Mmm-hmm. Tell me more about these objective third-party studies. I wouldn’t believe a nicotine industry study claiming water is wet, and if you would, then shame on you. Have you learned nothing?

A popular public transit app’s year end recap labeled me “Lord Tool, the Bus Punk”.

Uh, alright, I guess?

Screenshot of an iPhone app saying “Lord Tool 2025 in transit”.Screenshot of the same app: “The paparazzi even started calling you… the bus punk”. I am skeptical of this.

The dentist’s office has full-blown novels in the waiting room, which bodes ill for expected waiting times.

2 weeks in and GitHub still hasn’t replied to my urgent support ticket for a security-related audit log request, except to close it twice and make me re-open it.

And we pay good money for this privilege.

We’re this close to me asking my CTO if I can pilot a Forgejo POC.

I released v1.4.4 of Frozen BBS. It adds a ping command, a configurable delay between long reply pages, and an indicator of boards that have unread messages. It also removes message and DM and user bio limits, which are unnecessary since the pager can cope with longer texts.

Hey you.

Photo of a payphone. Someone has written on its box with a blue marker: “sitting naked by the phone, can you hear me?”

My coworker Secret Santa gets me.

A t-shirt that says “Stop clicking on sh*t! -Cyber Security”

I just bought my first “real” handheld ham radio, a Yaesu FT-70DR. I’ve been pretty good this year and figured Santa might want to hook me up with some nice gear. There were a lot of options in my budget range and here’s how I narrowed the choice down:

Yaesu FT-60R/FT-65R

I’ve heard nothing but good about these, with the 60R having a reputation for a slightly better receiver than the 65R. They only support analog, though, and I wanted to play with digital.

Yaesu FT5DR

This was extremely tempting, but I stumbled across report after report after report of the case cracking, even for people who never leave the house with them. If I paid that much for a radio, and the case broke itself, I’d have to find a steamroller to put it out of my misery.

Also, its APRS seems to keep getting stuck in transmit mode. Just… what?

AnyTone AT-878UVII Plus

The 878 also has a lot of fans! The only downsides were that I already have a DMR radio, and that its prices puts it up against some other extremely nice units.

Yaesu FT-70DR

So I ended up with the FT-70DR. It also has complaints about its battery life, but at least I can work around those by keeping it charged up or buying extra batteries. And now I’ll be able to play with Yaesu System Fusion digital mode, which sounds fun. The price was nicer than some of the alternatives, too, which makes Mrs. Claus a little more amenable to future purchases.

My coworkers weren’t sure what my “2600” baseball cap referred to. I sense an upcoming lunch and learn.

This morning’s programming-adjacent task: making a Shortcut that sets my phone to “reduce interruptions” mode for an hour, sets my AirPods to noise cancellation mode, and opens Maps showing transit times to my office.

Now I can say “hey Siri, commute” and do all these things at once.

My AirPods Pro case stopped charging on my MagSafe charger. The Internet told me, and I quote,

Just smack them shits on your table

It worked. Percussive maintenance carries the day, yet again.

I talked to my company today and told them how phishers use a sense of 1) danger, and 2) urgency, to push smart people to do rash things. I offered to be their personal, confidential fear consultant: “hey, I got this scary thing. Is it real?” Let me do the worrying for them.

This, more than goofy phishing tests that make people feel dumb, is how we help our friends avoid scams.

Currently reading: The Random Universe by Andrew H. Jaffe 📚

I have no idea what I’m getting into.

Finished reading: Class Clown by Dave Barry 📚

Govt. Issues SIM Binding Directions To WhatsApp and Telegram

India sees US and EU lawmaking idiocy and wants to get in on the fun. Basically, they demand that popular messaging apps link your online ID with a physical element the government believes they can control. They say so right in their claims: it will deter “fraud”; that is, any activity you wouldn’t want traced back to yourself.

In this specific case, I hope the corpos tell them to shove it.

Another hiker caught my wife and me cresting the top of a hill, then airdropped the photo to us. People on this trail tend to be either don’t-bother-me busy with their workout, or very social and ready to chat. Not that I’m either of those, exactly.

A tall man and short woman are silhouetted against the sky. There are some large trees overhead. Both people are using hiking poles. The man is wearing a brimmed hat.

The Black Panther Party had a 10 point platform. These demands are painted in various places around Oakland, CA. This is the painting for number 4:

We Want Decent Housing Fit For The Shelter of Human Beings.

It’s in the middle of a homeless encampment. Some things are better now. Some are not.

Photo of tents and bikes and trash under a yellow sky over a homeless camp. A sign behind it reads “4: We Want Decent Housing Fit For The Shelter of Human Beings.”

Mise + fnox + macOS Keychain is a great combo for running Ansible with stored, encrypted secrets.

In mise.toml:

[env]
DBSERVER_PW = { value = "{{ exec(command='fnox get DBSERVER') }}", tools = true, redact = true }

In Ansible’s host vars:

my_servers:
  hosts:
    dbserver:
      ansible_become_password: "{{ lookup('ansible.builtin.env', 'DBSERVER_PW') }}"

Now you can run ansible-playbook and friends without hardcoding your sudo passwords anywhere!