Author: dwolven

  • Lions 31, Jets 24: More Survival Than Victory

    A win is a win, but that felt a lot more like survival than victory.

    The Lions were up 24-10 and looked like they had the game under control. Then the defense did what it has been doing and let the Jets right back into it. A 14-point lead disappeared in the fourth quarter and suddenly it was 24-24.

    Fortunately, the offense answered. Goff played another really clean game, and Gibbs was ridiculous: 99 yards rushing, 65 receiving and three touchdowns. When Detroit needed the lead back, they got it.

    The defense is still the concern. Geno Smith went 31-of-37 for 321 yards and three touchdowns, and the Jets were driving for a potential tying score before Chuck Clark finally ended it with the strip sack.

    There were some positives. Detroit held the Jets to 58 yards rushing, got five sacks and finally produced the big defensive play when they absolutely had to have one.

    So I’ll take 2-1.

    But I’d really like to watch a Lions game where the fourth quarter doesn’t turn into a survival exercise.

  • Falcons 35, Packers 14: A Thursday Night for the Rest of the Division

    Everyone loves an underdog. It’s even easier to love one when the team on the other sideline is Green Bay, and for roughly three quarters of the NFC North, Thursday night was pure entertainment. If you live by FTP, this one was for you.

    On paper it looked like a get-right game for the Packers. Atlanta came into Lambeau 0-2 under a brand new head coach in Kevin Stefanski, with an offense that had scored 16 points total across two weeks and hadn’t run a single play in the red zone. Green Bay was a five-point favorite in its home opener. Michael Penix Jr. was making his first start of the season, coming back from the partially torn ACL that ended his last one.

    For about one drive, the script held. Penix threw an interception on Atlanta’s opening possession, and Christian Watson caught his fourth touchdown of the year to put Green Bay up 7-0. I figured I’d be changing the channel by halftime.

    Then Atlanta scored 27 straight points.

    Penix settled in and went 13-of-15 with a touchdown across the second and third quarters. He finished 18-of-25 for 256 yards, one touchdown and the one early pick. That’s a quarterback who shook off a bad start instead of letting it snowball, which is more than I can say for some quarterbacks I watch every Sunday.

    The real damage came from Drake London and Bijan Robinson. London had nine catches for 194 yards after managing six for 80 in the first two games combined. Robinson ran it 29 times for 194 yards and two touchdowns, plus two catches for 19 more. For context, he’d had 80 rushing yards total through two weeks. According to the stat nerds, they’re the first pair of teammates to each clear 190 yards from scrimmage in a road game since 1976. Doing that at Lambeau is a nice touch.

    Green Bay’s run game, meanwhile, produced 17 yards. Seventeen. Jordan Love threw it 53 times for 312 yards and two touchdowns, and Matthew Golden got his first career touchdown with a 100-yard night, but most of that came while the Packers were chasing a game that was already gone. Atlanta outgained them 498 to 328 and went 6-of-10 on third down. It would have been over 500 yards if not for a couple of kneel-downs at the end.

    Final: Falcons 35, Packers 14. It’s Green Bay’s first home-opener loss since 2012, and it leaves them at 1-2, with the Week 1 loss to Minnesota already on the books.

    I’m not going to pretend three weeks settles anything in this division. Green Bay will be fine eventually; they usually are, which is exactly why a night like this is so satisfying. As a Lions fan, I’ll take a Packers team that couldn’t run the ball or stop the run at home against an 0-2 team, and I’ll enjoy it for the rest of the weekend. The Lions have their own problems to sort out against the Jets on Sunday, but that’s a post for another day.

  • The logo saga: when a matching file size isn’t proof of anything

    The logo saga: when a matching file size isn’t proof of anything

    The theming post covered the palette and the read-only wall around global styles. What it didn’t cover was the logo, because that turned into its own small disaster, and this is the honest account of it.

    A file that looked fine and wasn’t

    The original logo was black ink on a transparent PNG, invisible against the new dark background. Recoloring it to off-white was simple enough with Pillow. Getting the result into WordPress was the actual problem.

    There’s no upload tool here that reads a local file directly. The only path is base64: encode the file, paste the encoded text as a parameter, WordPress decodes it on the other end. For a 25KB image that’s roughly 33,000 characters of base64. Typing that out is not really typing, it’s an AI regenerating a long span of near-random text token by token, and that turned out to be exactly as reliable as it sounds.

    The upload succeeded. The file size matched the original byte for byte. Everything about it looked correct until it rendered on the actual page: only the top edge of the letters showed, the rest cut off clean. Sampling the live file’s pixels directly confirmed it, the bottom half of the image had gone fully transparent, silently, somewhere in that 33,000-character reproduction. A single wrong character in the compressed image data was enough to corrupt everything after it, while leaving the total file length untouched. Matching byte counts turned out to be worthless as an integrity check.

    Fixing it properly instead of retrying blind

    Retyping the same 33,000 characters again would have had the same odds of failing the same way. Two changes instead:

    • Shrink the file. The logo only ever displays at 120 pixels wide, so the original 783px source was overkill. A resize to 180px, plus posterizing the alpha channel to fewer steps, cut the base64 payload by more than half.
    • Verify by actually reading the pixels back, not by trusting a file size match. After the second upload, the live file’s alpha channel got sampled row by row in the browser and compared against the local source. Small rounding differences from PNG re-encoding, sure. No blank regions.

    That one held up. Byte-for-byte size match, row sums lining up, letters intact top to bottom.

    Where a human stepped in

    Even with a verified file, there’s still no write tool for the block that actually sets the logo in the header or footer template. That’s a Site Editor action: open the block, pick the image, save. Every version of the logo, correct or corrupted, needed a manual click to actually go live. This happened twice, once for the corrupted file and once for the fixed one, which is also how the corruption got caught in the first place. It rendered wrong on a live page, not in a status message claiming success.

    The manual step didn’t stop there. After the second upload, the fix still didn’t hold, because the file needed pointing at the block a second time and the interim result kept the old broken file wired in. In the end the working logo on the site right now isn’t the AI-generated one at all, it’s a version made directly in GIMP and uploaded through the ordinary media library flow, sidestepping the whole base64 problem entirely.

    Where that leaves things

    Passing large binary data through a text-based tool call is a real failure mode, not a hypothetical one, and a matching file size is not proof that the content survived the trip. The fix that actually held wasn’t a smarter retry, it was shrinking the problem and verifying the result against ground truth instead of trusting a success message. And some things, like pointing a template block at a specific media file, just need a hand on the mouse.

  • News Roundup: September 14 – 21, 2026

    A security-heavy week for anyone running a TICK-stack dashboard or automated backups — two patches below are worth prioritizing over the rest.

    Grafana 13.2.2: patch if you run it alongside InfluxDB/Telegraf

    Released September 15, this fixes a high-severity plugin-install RCE (CVE-2026-15815, CVSS 8.8) — a crafted plugin archive can use chained symlinks to drop an executable outside the plugin directory that then runs with the Grafana server’s privileges — plus a stored XSS in the Geomap panel (CVE-2026-76154, CVSS 7.3) that lets an Editor-role user escalate to Org Admin. If Grafana is sitting in front of your InfluxDB or Telegraf metrics, this one’s worth doing sooner rather than later, especially if you allow plugin installs from anyone other than admins.
    Grafana 13.2.2 release notes

    Ubuntu: rclone remote-control API can be tricked into running arbitrary commands

    USN-8782-1 (September 18, CVE-2026-49980) fixes a bug where rclone mishandles unauthenticated requests to its remote-control API, letting an attacker who can reach that API execute arbitrary commands as whatever user is running rclone. Only relevant if you run rclone with --rc enabled and exposed beyond localhost — a fairly common setup for people who trigger backups from a separate management box — but worth checking your bind address either way.
    USN-8782-1 advisory

    WordPress 7.1.1 maintenance and security release

    Out September 17 with 11 security fixes on top of the usual round of core and block-editor bugs. The headline is an unauthenticated stored XSS in wpautop(), the function that turns line breaks into paragraphs on almost everything WordPress renders — about as broad a blast radius as a WordPress bug gets. The rest are mostly privilege and disclosure issues that need at least contributor-level access, including an arbitrary post overwrite and a path-traversal bug in the REST API templates controller. Update now if your host hasn’t already done it for you.
    WordPress 7.1.1 release notes

    Nextcloud Hub 26 “Summer” released

    Landed September 16 with a new Nextcloud Teams feature for dedicated team spaces, a reworked Photos app, and the usual pile of automation and Office-integration updates. Hub 25 “Autumn” is now end-of-life, so if you’re self-hosting Nextcloud on that branch, it’s time to plan the upgrade.
    Nextcloud Hub 26 Summer announcement

    Home Assistant 2026.9.3 patch release

    This week’s bug-fix patch for the 2026.9 series (September 18) is mostly routine — fixes for Spotify, Wiz, and IMAP integrations, plus a fix for encrypting and decrypting the supervisor backup archive correctly. Safe to apply if you’re already on 2026.9.
    2026.9.3 release notes

    MikroTik RouterOS 7.24.4: small follow-up fix, not the security patch

    Don’t confuse this with the MikroTrick fix from a couple of weeks back — 7.24.4 (September 16) just corrects a regression in 7.24.3 that could delete modem firmware on a handful of specific LTE hardware variants (RBSXTLTE3-7 and certain EC25/EG25 modem combos). Only matters if you’re running one of those cards, but good to know before updating blind.
    RouterOS 7.24.4 announcement

  • Bills 41, Lions 31: The Defense Is the Story Two Weeks Running

    Same result as last week in one sense: another close-ish final score. Everything else about how it felt was worse.

    Detroit went into Buffalo already missing pieces up front, with two starting offensive linemen out and both starting safeties unavailable. Josh Allen and the Bills offense made that count immediately. Buffalo scored on their first three drives and it was 21-0 before the Lions ran a single successful play that mattered. By halftime it was 27-10.

    Watching it live, the offense looked like it couldn’t decide what it wanted to be early on. Goff started spreading throws to whoever was open, then within a series or two it was back to feeding Gibbs almost by reflex, like the play-calling defaulted to its most comfortable answer instead of what the defense was actually giving up. Gibbs finished with just 52 yards on 16 carries on the ground, which tells you Buffalo had that answer scouted and Detroit kept asking the question anyway for a stretch.

    The second half was a different team. Once Goff started living through Jameson Williams, Amon-Ra St. Brown, and Sam LaPorta instead of forcing the ground game, Detroit actually outscored Buffalo 21-14 the rest of the way. St. Brown finished with 142 yards and two touchdowns, Goff ended the night 327 yards and four touchdown passes, and for about a quarter and a half it looked like a real comeback instead of garbage-time stat padding.

    Then the defense gave up 41 points, for the second straight week after allowing 30 to the Saints. Two straight games of that is a pattern, not a blip. And it’s happening with the two starting safeties already out and the offensive line already missing two starters, which is exactly the kind of situation where thin depth turns a bad week into a genuinely ugly one. Allen finished with 317 total yards and five total touchdowns and the Bills converted 9 of 12 third downs. Detroit’s defense was beaten and gassed, on the field almost the entire night.

    Final: Bills 41, Lions 31. A game the offense nearly stole back in the fourth quarter, and a defense that made that theft necessary in the first place.

    Jets come to Ford Field next. Plenty of time to figure out who’s walking through that door to help the back end of this defense, though at this point I’m not holding my breath.

  • Lions 31, Saints 30: A Win That Felt Like a Loss

    Full disclosure up front: I’m not a football theory guy. I couldn’t diagram a cover-2 if you paid me. I watch as a casual fan who likes the Lions, yells at the TV a little, and moves on with my day. So take these as impressions, not analysis.

    Sunday’s game against the Saints was a weird one to sit through, because Detroit won 31-30 and it still didn’t feel like a win.

    The first half and change went about how you’d hope. Detroit built a 21-0 lead through the third quarter on two Jahmyr Gibbs rushing touchdowns and a Jared Goff touchdown pass to Amon-Ra St. Brown, and the Saints hadn’t put a point on the board yet. This is the part of the game I actually enjoyed watching.

    Then it fell apart. New Orleans scored on four straight possessions, a touchdown, another touchdown, a field goal, and a third touchdown, to erase the entire lead and tie the game at 24 with just over a minute left in the fourth quarter. A 21-0 lead turned into a tie game that fast. Their quarterback finished with 410 passing yards on the day, which is a number that should not be possible against a defense that also racked up five sacks and two interceptions. Both things happened in the same game.

    Detroit did answer. Goff found St. Brown again for a short touchdown to make it 31-24 with barely a minute on the clock, which should have sealed it. Instead the Saints marched right back down and scored again to make it 31-30, then went for the two-point conversion and the win instead of the extra point and overtime. The pass fell incomplete, and that’s the game.

    So Detroit wins by one point after giving up 30 unanswered and needing the Saints to miss a two-point try to close it out. The Lions also lost the total yardage battle, 369 to 469, and Detroit held the ball for five more minutes than New Orleans did while still surrendering nearly 500 yards. None of that shows up in the final score, but it’s exactly why the game felt like it did.

    I know a win is a win, and I’m sure there are Lions fans out there right now telling me to stop complaining about a victory. Fair. But if you were watching live, you know exactly the feeling I mean: that slow drain where a comfortable laugher turns into a nail-biter you didn’t ask for, and by the end you’re relieved more than happy.

    On to Buffalo tonight.

  • News Roundup: September 7–16, 2026

    A heavier week than the last one. The MikroTik router chain I flagged in the last roundup turned out to be a live zero-day, a popular WordPress events plugin picked up a 9.8 RCE, and Cisco dropped another advisory batch.

    • MikroTik’s “no details yet” RouterOS release from the last roundup turned out to be actively exploited. CERT Polska disclosed the chain, dubbed MikroTrick: CVE-2026-67276 (SSH auth bypass, CVSS 9.2) combined with CVE-2026-86060 (privilege escalation) gives an unauthenticated attacker full admin on any RouterOS device with SSH reachable from the internet. Exploitation reportedly started September 2, a day before the patch shipped, so this was a zero-day in practice. CISA added both CVEs to its KEV catalog on September 10, and Shadowserver counted roughly 122,500 internet-exposed RouterOS SSH endpoints as of September 5. If you haven’t patched to 7.24.2, 7.23.4, or 6.49.21 yet, do that now, then check for a “Flagged” device status and an unexpected user named something like “ops” before assuming you’re clean.
    • The Events Calendar plugin patched two critical code-injection bugs. CVE-2026-78159 (CVSS 9.8) is an unauthenticated code injection from insufficient validation; a second flaw, CVE-2026-78006, is also critical. Both need comments enabled on the plugin to be exploitable. Fixed in 6.17.3.1, but the plugin has over 600,000 active installs and download data suggests roughly half may still be sitting on a vulnerable version.
    • Cisco’s September 16 advisory batch covers hardening releases for BroadWorks CommPilot, Identity Services Engine, Nexus Dashboard, ThousandEyes Virtual Appliance, and a combined release for all three Secure Firewall products (ASA, FMC, and FTD). Worth calling out separately: the same batch includes a Secure Email Gateway hardening release with a SQL injection flaw that Cisco says is already being exploited, which bumps it above the usual “patch when convenient” priority.
    • WordPress 7.1.1 is still on track as a bug-fix-only release for tomorrow, September 17, covering 16 core tickets and 22 Gutenberg pull requests. No security content in this one, but if anything you run leans on responsive styles, the Icon Registration API, or the iframed editor, it’s worth a spin on staging before it lands.
  • Theming the site: a dark palette, a read-only wall, and two follow-up fixes

    Theming the site: a dark palette, a read-only wall, and two follow-up fixes

    Two posts in, this site was still using WordPress defaults with zero customization. Given the subject matter, that felt like an oversight. So the next request was simple: restyle the theme, pick a direction based on the site’s own content, and make it happen. It mostly worked. The interesting part was where “mostly” showed up.

    The direction

    Twenty Twenty-Five, the active theme, is a block theme, which means colors, typography, and spacing all live in a theme.json file rather than scattered across a Customizer panel. Given the content here is server racks, networking gear, and ISP work, the natural direction was something dark and technical rather than another white background with black text: deep slate, off-white body copy, a signal-green accent, and monospace for anything code-related. Status-light green felt more honest to the subject matter than a generic blue.

    Read access, no write access

    Here is where it got interesting. The MCP plugin exposes a tool to read the site’s current global styles, but nothing to write them. The wp_global_styles object isn’t a normal post either. Its REST endpoint expects a JSON body with top-level styles and settings keys, not the title and content fields every other post type uses, so even the generic custom-post-type update tool couldn’t touch it. Forcing a write through a mismatched schema on a live site was a bad trade for saving a few minutes, so instead of guessing, the actual theme.json got handed over as text to paste in directly.

    That meant a child theme rather than an edit to Twenty Twenty-Five itself, so a future core update to the parent theme wouldn’t wipe out the customization. Two small files, a folder created over SSH, activate from the dashboard. From there it was a normal back and forth: apply the palette, load the site, screenshot it, spot what needed adjusting.

    What needed a second pass

    The first version looked right at a glance but missed two things once actually rendered:

    • Inline code, the kind that shows up mid-sentence, wasn’t styled at all. Only the block-level code element had a rule, so a word like xfs_growfs in a sentence just inherited body text. Needed its own entry in the theme.json elements block.
    • Nav links stayed off-white instead of picking up the accent color. Block themes sometimes save color settings directly on a template part through the Site Editor, which can override theme.json for that one instance. Adding an explicit core/navigation block style to the file fixed it without needing to touch the Site Editor at all.

    Both went back into the same theme.json, pasted in once, confirmed live with a screenshot afterward rather than assumed. Inline code now has a dark background with green monospace text, matching the block-level code it sits next to, and the nav bar is green across the board.

    Where that leaves things

    The gap between what a plugin can read and what it can write is worth remembering. It’s tempting to assume access implies control, but here the honest answer was to say so and hand off a copy-paste step rather than force a fix that might have corrupted live styling. The site now actually looks like it belongs to an ISP nerd instead of a stock WordPress install, and the process for getting there, read what exists, design deliberately, verify by looking rather than assuming, is the part worth keeping around for next time.

  • News Roundup: September 7 – 14, 2026

    A quieter week than last time out — no active-exploitation fire drills, but a solid batch of releases and patches worth working through.

    Linux kernel 7.2.5 and 6.18.51 released

    Greg Kroah-Hartman pushed 7.2.5 and 6.18.51 (plus the usual parallel LTS bumps) on September 11, each carrying 500+ fixes. The notable cluster this round is on the graphics side — a run of nouveau patches targeting NVIDIA’s new Blackwell display pipeline — alongside several ksmbd hardening fixes, which matters if you’re exposing SMB shares from a Linux box.
    Kernel 7.2.5 / 6.18.51 coverage

    Ubuntu security notices: MiniUPnPd and a batch of media-library fixes

    The one worth prioritizing is USN-8731-1: an integer underflow in MiniUPnPd’s SOAPAction header parsing (CVE-2026-41196) that a malformed UPnP request can turn into a crash or out-of-bounds memory read — relevant if you run MiniUPnPd for port-forwarding on a router or NAS. Canonical also shipped fixes for KissFFT, libEBML, and Beets this week (USN-8745-1/8746-1/8747-1), all triggered by crafted input files rather than network traffic, but still worth an apt upgrade pass if any of your media tooling pulls them in.
    MiniUPnPd advisory · All Ubuntu security notices

    Jellyfin 12.0 lands with a version-scheme reset

    Jellyfin dropped the old 10.x numbering (what would’ve been 10.12.0 is just 12.0) and shipped native book support, FFmpeg 8.1, faster library scans, and a handful of security patches. The database changes mean there’s no rolling back once you upgrade, so take a full backup of your data directory first if you’re running it in a container or VM.
    Jellyfin 12.0 release notes

    Home Assistant 2026.9.2 patch release

    The second bug-fix patch for the 2026.9 series landed September 11, cleaning up issues across Hive auth, Roomba sensors, Google Calendar sync, Portainer, and several other integrations. Nothing dramatic, but a safe update if you were holding off on 2026.9 for stability reasons.
    2026.9.2 release notes

    Heads up: InfluxDB’s Docker “latest” tag is switching to InfluxDB 3

    Starting September 15, the latest tag on the official InfluxDB Docker image stops pointing at InfluxDB 2 and starts pointing at InfluxDB 3 Core. If your TICK-stack compose files or Ansible playbooks reference influxdb:latest instead of a pinned version, your next pull will hand you a different major version with a different query language and storage engine. Pin your tag now if you’re not ready for that jump.
    InfluxData community notice

    LWN: CERN is moving its accelerator controls from CentOS to Debian

    A talk at MiniDebConf Winterthur covered CERN’s plan to migrate roughly 2,200 embedded industrial-control computers running its particle accelerators from CentOS Linux to Debian trixie. It’s a useful data point if you’re weighing distro choices for infrastructure that needs to be boring and stable for a decade at a time.
    CERN’s migration path from CentOS to Debian

    WordPress: a fuzzer for plugin bugs, and one plugin to check right now

    Researchers at CISPA built THEMIS, a fuzzing tool purpose-built for WordPress plugins, and ran it against 68 real-world plugins — it reproduced 27 known vulnerabilities and turned up 10 previously unknown ones, three of which got CVEs assigned. Separately, if you or anyone you administer runs the small “Advanced Customized Prompts” plugin, versions through 1.0.1 have a critical authentication bypass (CVE-2026-14563, CVSS 9.8) that lets an unauthenticated attacker log in as any user, including admins — update or remove it.
    THEMIS fuzzing tool · CVE-2026-14563 details

  • Getting Codex connected to WordPress through MCP

    I finally got Codex talking to this WordPress site through MCP. The short version: the local config was close, the WordPress endpoint itself was fine, but the thing that actually made the tools show up here was setting it up through the web interface as a plugin and then reloading the client.

    What did not work at first

    I started with a local MCP server entry in the Codex config pointing at this site’s Easy MCP AI endpoint:

    [mcp_servers.wordpress]
    enabled = true
    url = "https://example.com/wp-json/easy-mcp-ai/v1/mcp"
    bearer_token_env_var = "WORDPRESS_MCP_TOKEN"

    That looked right on disk, but it did not make the WordPress tools appear in the running chat. The config was present, the server was enabled, and the endpoint URL was correct, but Codex’s live tool list still only showed the built-in app/plugin resources. In other words: configured is not the same thing as loaded.

    The token/header trap

    The other easy mistake was treating the token name as if it were the header name. Putting a value under a header called WORDPRESS_MCP_TOKEN is not the same thing as setting an environment variable named WORDPRESS_MCP_TOKEN, and it is not the same thing as sending Authorization: Bearer ....

    When I tested the WordPress endpoint directly with a bearer token, the MCP initialize request came back clean. The server identified itself as easy-mcp-ai and advertised tools/resources for managing posts, pages, media, comments, users, and site settings. So the WordPress side was alive. The problem was getting Codex to expose it as an actual callable tool in the session.

    What finally worked

    The working path was to add it through the web interface as a plugin, then reload the Codex client and resume the chat. After that, the WordPress MCP namespace appeared immediately, with tools like:

    • wp_list_posts
    • wp_get_post
    • wp_create_post
    • wp_update_post
    • wp_list_media
    • wp_get_site_settings

    The first real verification was intentionally boring: list one recent post. That returned a scheduled post and proved the connection was not just installed-looking, but actually usable.

    The practical checklist

    • Install and configure the Easy MCP AI plugin on WordPress.
    • Generate a token from the WordPress side.
    • Add the MCP endpoint through the web/plugin interface, not just local config, if you want it available inside this Codex session model.
    • Reload the Codex client after adding it.
    • Verify with a read-only call first, like listing posts or reading site settings.

    That last bit matters. A connector can be present in a settings screen, reachable over HTTPS, and still not be available to the model as a tool. The only test that counts is whether the WordPress tools actually show up and a harmless read works.

    Net result: this post was created from Codex through the WordPress MCP plugin, which is probably the cleanest possible proof that the setup is working.