README: state and enforce the 24.10 minimum; document the compat preflight
CI / shell (shellcheck + syntax) (push) Successful in 13s
CI / python 3.11 (push) Successful in 16s
CI / python 3.12 (push) Successful in 16s
CI / python 3.13 (push) Successful in 15s

TrueCloud Backup does not exist before 24.10, so on anything older the patch would
attach to nothing and do nothing -- silently, while the user believed their backups
were set up. install.sh now reads system.version and refuses, naming the reason.

Also: the boot sequence now documents the preflight (and that an incompatible module
is skipped for one boot, NOT kill-switched); the troubleshooting table covers the
incompatibility warning; forge URLs point at Gitea.
This commit is contained in:
2026-07-13 18:09:23 +00:00
parent aa725fb198
commit cf2c6a8a02
3 changed files with 85 additions and 7 deletions
+8
View File
@@ -97,6 +97,14 @@ worse than no alert, because one day it carries a security fix.
### Changed
- **The minimum supported TrueNAS is stated, and enforced: 24.10.** TrueCloud Backup
does not exist before it — `plugins/cloud_backup/` is simply absent — so the patch
had nothing to attach to and would have done nothing at all, silently, while the
user believed their backups were configured. `install.sh` now reads
`system.version` and refuses, naming the reason. A version it cannot *parse* is a
warning, not a refusal: declining to install over a string we failed to read would
be a worse failure than the one being prevented.
- **A stable release may not leave work stranded under `## Unreleased`.** Either it
is finished and belongs in the release, or the release is premature. Candidates
are exempt: an rc may legitimately have work queued behind it.
+39 -7
View File
@@ -1,5 +1,14 @@
# truenas-truecloud-patch
> ### Requires **TrueNAS SCALE 24.10 or newer**
>
> TrueCloud Backup — the feature this patch extends — **does not exist before
> 24.10**. There is nothing here to install on an older release, and `install.sh`
> will refuse.
>
> Verified on **24.10**, **25.04** and **25.10**. Not yet compatible with the
> unreleased **26.0** (see [TrueNAS compatibility](#truenas-compatibility)).
Extends TrueNAS SCALE's **TrueCloud Backup** feature to:
- work with S3-compatible providers and native Backblaze B2, instead of Storj only;
@@ -436,9 +445,20 @@ Two different things must survive two different events:
so nothing registered there can run before it.
2. **Pools import** (`ix-zfs.service`), making `/mnt/<pool>` — and this
repository — available.
3. **`apply.sh` runs** (`ix-preinit.service`): mounts the writable overlay
(upper layer in `/run`), patches `b2.py` and `restic.py` on disk inside it,
patches the UI bundle, and writes `apply.log` and `hook_status.json`.
3. **`apply.sh` runs** (`ix-preinit.service`). Before it patches anything it runs
the **compatibility preflight** ([`tools/compat.py`](tools/compat.py)) against
the middlewared that is *actually installed*, and **any module whose assumptions
no longer hold is not applied** — see [TrueNAS
compatibility](#truenas-compatibility). What survives that check gets applied:
it mounts the writable overlay (upper layer in `/run`), patches `b2.py` and
`restic.py` on disk inside it, patches the UI bundle, and writes `apply.log` and
`hook_status.json`.
An incompatible module is skipped **for this boot only**. It is not the kill
switch: install a release that supports your TrueNAS and the patch re-applies
itself on the next boot, with no manual step. (The kill switch is permanent and
is set only when TrueNAS has made the patch *unnecessary* — a different
situation, and the opposite conclusion.)
4. **A deferred restart is scheduled.** The middlewared that is running
imported the stock modules in step 1 and never re-imports, so the on-disk
patch alone is not enough. `apply.sh` detects it was invoked by middlewared
@@ -476,7 +496,7 @@ then run `install.sh` from there:
```bash
# Replace /mnt/tank with your pool name
git clone https://github.com/sudolulo/truenas-truecloud-patch.git \
git clone https://git.onetick.ninja/flan/truenas-truecloud-patch.git \
/mnt/tank/truenas-truecloud-patch
cd /mnt/tank/truenas-truecloud-patch
bash install.sh
@@ -560,7 +580,7 @@ detonate on the next reboot. That is not hypothetical: **v0.0.4 shipped exactly
such a bug and took all 54 apps on a box down.**
The manual step *is* the safety gate. If you want convenience, watch the
[releases feed](https://github.com/sudolulo/truenas-truecloud-patch/releases);
[releases feed](https://git.onetick.ninja/flan/truenas-truecloud-patch/releases);
don't automate the pull.
## Update alerts
@@ -580,6 +600,17 @@ WARNING. The CHANGELOG's own section headings are the signal, and a security fix
anywhere in the range escalates the whole span — a docs-only release on top of a
security fix still reports as security.
**Release candidates never alert.** They are invisible to `update.sh` and to the
alert, which both take the newest plain `vX.Y.Z` tag. That is what lets debugging
happen in `-rc` tags instead of in your notification bell — see
[Releasing](#releasing).
The changelog is read from whichever forge `origin` points at, derived from the
remote rather than hard-coded. That is not cosmetic: when the changelog cannot be
read, the alert deliberately fires **anyway** rather than risk hiding a security
fix — so a wrong URL would not silence the alert, it would make it fire on *every*
release, including the documentation-only ones this section promises to suppress.
### How it works, and why it's built this way
TrueNAS **cannot raise an alert from the CLI** — `midclt` exposes only
@@ -720,7 +751,8 @@ python3 /mnt/tank/truenas-truecloud-patch/patch/create_task.py verify
| What you see | What it means |
|---|---|
| `[OK] providers`, `[OK]`/`[SKIP] nested_snapshots` | Fine. Nothing to do. |
| `WARNING: … pattern not found` (UI) | The Angular bundle changed. The UI dropdown reverts to Storj-only, but **backups keep working** — create tasks with `create_task.py` meanwhile, and [open an issue](https://github.com/sudolulo/truenas-truecloud-patch/issues) with your TrueNAS version. |
| `WARNING: … pattern not found` (UI) | The Angular bundle changed. The UI dropdown reverts to Storj-only, but **backups keep working** — create tasks with `create_task.py` meanwhile, and [open an issue](https://git.onetick.ninja/flan/truenas-truecloud-patch/issues) with your TrueNAS version. |
| `WARNING: truecloud-patch is NOT COMPATIBLE with this TrueNAS version` | This TrueNAS changed middleware underneath the patch, and the named module was **deliberately not applied** — see `incompatible.json` for exactly which assumption broke. TrueNAS is left stock, so nothing is half-patched. Check [TrueNAS compatibility](#truenas-compatibility), then `bash update.sh` once a release supports your version; it re-applies itself on the next boot. This is **not** the kill switch and needs no manual reset. |
| `[FAIL] providers` | **Your B2/S3 backups will not run.** middlewared is fine, but the credential/URL handling is gone. Open an issue with your version. |
| `[FAIL] nested_snapshots` | The stock guard is back, so tasks with `snapshot = true` on a nested dataset will fail validation. Turn the option off on those tasks until it's fixed. |
@@ -842,7 +874,7 @@ If a module shows `[FAIL]`:
risk.
4. **If the detail says the module doesn't exist**, a TrueNAS update renamed
or restructured the internal API.
[Open an issue](https://github.com/sudolulo/truenas-truecloud-patch/issues)
[Open an issue](https://git.onetick.ninja/flan/truenas-truecloud-patch/issues)
with your TrueNAS version number and the full verify output.
---
+38
View File
@@ -88,6 +88,44 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1
fi
# ── Minimum TrueNAS version ───────────────────────────────────────────────────
#
# TrueCloud Backup -- the feature this whole project extends -- was introduced in
# 24.10. On anything older, `plugins/cloud_backup/` does not exist at all: there is
# no restic, no cloud_backup task type, and nothing for the patch to attach to. It
# would not break the box, it would simply do nothing, silently, while the user
# believed their backups were configured. Say no clearly instead.
#
# A version we cannot PARSE is not a version we may refuse on: warn and continue.
# Refusing to install over a string we failed to read would be a worse failure than
# the one being prevented.
MIN_TRUENAS="24.10"
_tc_version_raw=""
if command -v midclt &>/dev/null; then
_tc_version_raw=$(midclt call system.version 2>/dev/null || true) # TrueNAS-25.10.4
fi
# Both spellings are real: modern releases report `TrueNAS-25.10.4`, older ones
# `TrueNAS-SCALE-24.04.2` -- and the SCALE- form is used by exactly the versions
# this gate exists to turn away, so failing to parse it would let them through.
_tc_version=$(printf '%s' "$_tc_version_raw" \
| sed -n 's/^TrueNAS-\(SCALE-\)\{0,1\}\([0-9]\{1,\}\.[0-9]\{1,\}\).*/\2/p')
if [ -z "$_tc_version" ]; then
echo "WARNING: could not determine the TrueNAS version" \
"${_tc_version_raw:+(got '${_tc_version_raw}')}."
echo "WARNING: this patch requires TrueNAS SCALE ${MIN_TRUENAS} or newer. Continuing anyway."
echo ""
elif [ "$(printf '%s\n%s\n' "$MIN_TRUENAS" "$_tc_version" | sort -V | head -1)" != "$MIN_TRUENAS" ]; then
echo "ERROR: TrueNAS ${_tc_version} is too old — this patch requires ${MIN_TRUENAS} or newer." >&2
echo "" >&2
echo " TrueCloud Backup does not exist before ${MIN_TRUENAS}, so there is nothing" >&2
echo " here for the patch to extend. Upgrade TrueNAS first." >&2
exit 1
else
echo "TrueNAS ${_tc_version} (minimum ${MIN_TRUENAS}) — ok"
fi
if ! command -v midclt &>/dev/null; then
echo "ERROR: midclt not found. Run this script on TrueNAS SCALE." >&2
exit 1