
-
This is a guide of the steps I followed to downgrade from TrueNAS 26 MASTER to TrueNAS 26 BETA1. (specifically from 26.0.0-MASTER+20260405-020459 to 26.0.0-BETA.1)
TrueNAS considers MASTER a higher train than BETA. Attempting a manual update from MASTER to BETA.1 gives:
Unable to downgrade from 26.0.0-MASTER+20260405-020459 to 26.0.0-BETA.1
Step 1: Bypass the Downgrade Check
- SSH into TrueNAS and remount /usr read-write:
- sudo mount -o remount,rw /usr
- Edit the middleware update file:
- sudo nano /usr/lib/python3/dist-packages/middlewared/plugins/update_/install_linux.py
- Find and remove these two lines (around line 64-65):
- if not can_update(old_version, new_version): raise CallError(f’Unable to downgrade from {old_version} to {new_version}’)
- Restart the middleware:
- sudo systemctl restart middlewared
Step 2: Fix the Alembic Migration Mismatch
You need to find BETA.1’s head migration revision. Mount the .update file on another Linux machine:
sudo mkdir -p /tmp/truenas-beta
sudo mount -t squashfs -o ro,loop TrueNAS-26.0.0-BETA.1.update /tmp/truenas-beta
sudo mkdir -p /tmp/truenas-rootfs
sudo mount -t squashfs -o ro,loop /tmp/truenas-beta/rootfs.squashfs /tmp/truenas-rootfsFind the last migration file and its revision ID:
ls /tmp/truenas-rootfs/usr/lib/python3/dist-packages/middlewared/alembic/versions/26.0/
grep “^revision” /tmp/truenas-rootfs/…/last_migration_file.pyThen on TrueNAS, stamp the database with that revision:
sudo sqlite3 /data/freenas-v1.db “UPDATE alembic_version SET version_num=”;”
Step 3: Apply the Update

DISCLAIMER: Keep a backup of your settings!!!!
- SSH into TrueNAS and remount /usr read-write:
-
Happy to contribute not only to TrueNAS but also to OpenZFS upstream !!
The official announcement from iXsystems: https://www.truenas.com/blog/blog-truenas-26-beta1-release/
https://www.phoronix.com/news/TrueNAS-26-Beta
Download the BETA iso from here: https://download.truenas.com/TrueNAS-26-BETA/26.0.0-BETA.1/TrueNAS-26.0.0-BETA.1.iso
The .update file version is available here: https://update-public.sys.truenas.net/TrueNAS-26-BETA/TrueNAS-26.0.0-BETA.1.update
-
TrueNAS 26 BETA1 come up next week. Also a quite interesting discussion about air-gapped backups.
-
Supports fractional intervals lengths for same day review prediction of probability !!

-
If you’re running FreeBSD on an AM5 board with a recent BIOS update and your system freezes during boot — right after the EFI framebuffer line — you’re probably hitting a firmware bug in AGESA 1.3.0.0a.
The Problem
After updating my Gigabyte B650 GAMING X AX V2 to BIOS F41a (AGESA 1.3.0.0a, released March 2026), FreeBSD stopped booting entirely. All versions — 14.2, 14.4, and 16.0-CURRENT — freeze hard after printing EFI framebuffer information. No panic, no output, complete CPU lockup (caps lock LED goes dead). The same issue has been reported on ASRock X870 boards with Ryzen 9600X on the FreeBSD forums.
Investigation
I built a diagnostic FreeBSD loader with printf calls at every stage of the EFI boot handoff. The results were clear: the hang occurs inside the UEFI firmware’s
ExitBootServices()call. The loader calls into firmware, and firmware never returns.An interesting secondary finding: adding printf (which calls the UEFI
OutputStringboot service) betweenGetMemoryMapandExitBootServicesinvalidates the memory map key. The firmware then hangs on the stale key instead of returning an error — which is itself a firmware bug. Well-behaved firmware should returnEFI_INVALID_PARAMETERand let the caller retry.Attempted Loader Fix
Knowing that stale memory map keys were part of the picture, I wrote a patch for the FreeBSD EFI loader (
stand/efi/loader/bootinfo.c) that:- Pre-allocates the memory map buffer with slack before the retry loop — no more
AllocatePagesinside the loop - Disables the UEFI watchdog timer before calling
ExitBootServices - Keeps the retry loop tight — only
GetMemoryMapfollowed immediately byExitBootServices, with zero intervening boot service calls
This matches the strategy used by the Linux EFI stub, which has had these hardening measures for years.
Unfortunately, the patched loader still hangs on BIOS F41a. The firmware bug in AGESA 1.3.0.0a is not just about stale keys —
ExitBootServicesis fundamentally broken, even with a fresh memory map key.The Fix: Downgrade Your BIOS
The only workaround is to downgrade your BIOS. I flashed back to F37 (AGESA 1.2.0.3g) using Q-Flash, and FreeBSD boots perfectly again. Here’s the full test matrix:
FreeBSD Version Loader BIOS F37 (AGESA 1.2.0.3g) BIOS F41a (AGESA 1.3.0.0a) 14.4-RELEASE Stock Boots Hangs 16.0-CURRENT Stock Boots Hangs 16.0-CURRENT Patched Boots Hangs I avoided BIOS versions F38–F40 (AGESA 1.2.8.0) as there are reports of boot failures on some AM5 boards with that AGESA version as well.
Affected Hardware
- My system: Gigabyte B650 GAMING X AX V2, Ryzen 9 9900X
- Also reported: ASRock X870 + Ryzen 9600X (FreeBSD Forums)
- Likely affected: Any AM5 board shipping AGESA 1.3.0.0a
Next Steps
The loader hardening patch didn’t fix the AGESA bug, but it’s still valid as defensive improvement — it eliminates a class of firmware interaction bugs by keeping the
ExitBootServicesretry loop free of boot service calls. I’ll be submitting it upstream to FreeBSD as a hardening measure.If you’re hitting this issue: downgrade your BIOS to any version before AGESA 1.3.0.0a. For Gigabyte B650 boards, F37 is a safe choice.
- Pre-allocates the memory map buffer with slack before the retry loop — no more
-
Officially an OpenZFS contributor 🙂

https://github.com/openzfs/zfs/commit/a7157221db1d3a7c3517dee98f5124c13a515053
-
TL;DR: Many professional societies run Zoom-based webinar series where you need to attend a minimum percentage of sessions to earn a certificate. I used Claude to audit my attendance history directly from Gmail, then automated the whole process with a Google Apps Script that detects new Zoom registration emails and adds calendar events — so you never miss a session again.
The Problem: Tracking Attendance for Certificate Programmes
Many professional associations now run structured online education programmes through Zoom, where attendance is tracked and a certificate is awarded to participants who reach a certain threshold — typically 70% of live sessions. The problem is that life gets in the way: you register for a session, forget about it, and only realise you missed it when the “sorry you couldn’t make it” email arrives. Over a multi-month programme, these misses add up quietly.
The good news is that Zoom’s post-event emails are remarkably consistent and machine-readable. This makes the whole problem automatable.
Step 1: Reconstructing Your Attendance History from Gmail
Zoom always sends one of two emails after a webinar ends:
- “Thank you for attending…” — you were present
- “We’re sorry you were not able to attend…” — you registered but missed it
This means your full attendance history is sitting in your Gmail inbox, waiting to be counted. To find it, search Gmail using a query like:
from:no-reply@zoom.us ("thank you for attending" OR "sorry that you were not able") [your-series-keyword]Replace
[your-series-keyword]with something specific to your programme — the organiser’s domain, the series name, or any unique phrase that appears in your webinar emails. The results give you a complete attended/missed breakdown to calculate your current attendance rate.Step 2: Automating Calendar Events with Google Apps Script
Google Apps Script is a free JavaScript runtime that runs inside your Google account with direct access to Gmail and Google Calendar. No server, no third-party service, no cost.
The script does the following every hour:
- Searches Gmail for new Zoom registration confirmation emails matching your programme’s keywords
- Parses the webinar title and date/time from the email body
- Maps Zoom’s city-based timezone strings to proper IANA timezones
- Checks for duplicate calendar events before creating anything
- Creates a Google Calendar event with a 1-day email reminder and a 1-hour popup reminder
- Labels the processed email so it is never processed twice
Parsing the Zoom Email Format
The trickiest part was reliably extracting the date and time. Zoom confirmation emails always contain a line like:
Date & Time Oct 23, 2025 06:00 PM Amsterdam, Berlin, Rome, Stockholm, Vienna
The key insight is to anchor the regex on
Date & Timerather than trying to detect a date anywhere in the body — robust across different webinar topics and templates:const anchored = /Date\s*&\s*Time\s+(\w{3}\.?\s+\d{1,2},?\s+\d{4})\s+(\d{1,2}:\d{2}\s*[AP]M)(.*)/i; const m = text.match(anchored); // m[1] = "Oct 23 2025" m[2] = "06:00 PM" m[3] = "Amsterdam, Berlin, ..."Zoom expresses timezones as a city list rather than an IANA string. A mapping function handles the conversion:
function mapTimezone(raw) { const s = (raw || "").toLowerCase(); if (/amsterdam|berlin|rome|vienna|stockholm/.test(s)) return "Europe/Berlin"; if (/london|dublin|bst|gmt/.test(s)) return "Europe/London"; if (/eastern|new york|toronto/.test(s)) return "America/New_York"; // ... other timezones ... return "Europe/Berlin"; // default }Deduplication
The script checks the calendar for an existing event with a matching title near the same date, and applies a Gmail label (
Webinar-Added) to every processed thread — excluding it from future queries. Safe to re-run as many times as needed.Setup: 2 Minutes
- Go to script.google.com → New project
- Paste the full script, replacing all default content
- Update the
CONFIGblock — specifically thekeywordsarray - Select
createProcessedLabel→ click ▶ Run - Select
installTrigger→ click ▶ Run - Optionally run
backfillHistoricalWebinarsonce for past emails
Google will show a warning: “Google hasn’t verified this app.” This is normal for personal Apps Scripts — click Advanced → Go to [project name] (unsafe) and grant permissions. You are the developer and sole user.
Adapting for Your Own Programme
Everything lives in one
CONFIGobject at the top:const CONFIG = { processedLabel: "Webinar-Added", calendarId: "primary", defaultDurationMins: 90, emailReminderMins: 24 * 60, // 1 day before popupReminderMins: 60, // 1 hour before // Change these to match your programme keywords: [ "your-society-name", "your-series-name", "other-keyword", ], };Replace the
keywordsvalues with terms unique to your series — the rest works unchanged for any Zoom-hosted webinar programme.What This Doesn’t Do
- It doesn’t register you. The script only acts after you register — it creates the calendar reminder from the confirmation email.
- It doesn’t track actual attendance. Events are created from registration confirmations, not post-event emails. Your official record is with the organiser.
- It requires a consistent email address. Most programmes track attendance by email. Register with the same address every time.
Full Script
The complete script is available on my GitHub. MIT licensed — adapt freely.
Tags: Google Apps Script, Zoom, Webinar, Automation, Gmail, Google Calendar, CME, Medical Education, Professional Development







