Releases: AIops-tools/TrueNAS-AIops
Release list
v0.9.0
Fixed
undo applyreplays against the target the original write ran on. It dispatched the inverse against whatever target the caller named — in practice the config's first entry — while the write's own target sat unused in the undo record. On a multi-target config the inverse therefore ran against the wrong host; it only looks harmless because the resource usually is not there, but two hosts holding the same name and the inverse succeeds on the wrong one, silently. An explicitly named target still wins. Line-wide: all 24 copies had the identical defect. Caught live in container-host-aiops, where a stop recorded against a Podman target replayed against a Portainer one.
v0.8.1 — correct the published verification status
The skill description, installer notes, and all three reference guides still said "mock-validated only, not yet verified against a live appliance" — the text the skill registries display — while reads, governed writes with audit and undo, the WebSocket transport, degraded-pool RCA, replication and cloud-sync had all been exercised against live TrueNAS SCALE 25.04 and 26 appliances.
They now point at docs/VERIFICATION.md, which records what was checked and what is still open (real S.M.A.R.T. on failing media). No code changes.
v0.8.0 — replication state, from real replication
Release notes — truenas-aiops 0.8.0
Previous release: 0.7.0.
Replication state came from the wrong record
The replication surface had only ever been read on appliances with zero
tasks — an empty list, which proves nothing. This release follows a round
against real ones: a TrueNAS SCALE 25.04.2.1 → 26.0.0-BETA.2 replication over
SSH that actually transferred a snapshot, and a cloud-sync to a real MinIO S3
target that actually uploaded a file.
list_replication reported job.state. On a real appliance that is wrong twice
over:
| task | what the appliance reports | what the tool reported |
|---|---|---|
| ran successfully | FINISHED |
SUCCESS |
| created, never run | PENDING |
null |
| failed | ERROR + an error sentence |
FAILED, sentence dropped |
replication.query injects a top-level state from the moment a task is
created. job is the generic job record: it does not exist until a run has been
triggered in the middleware's current lifetime, and when it does it speaks a
different vocabulary. So the common case — any task on a freshly booted
appliance — reported null, "unknown" for a state the appliance states plainly,
and no value the tool ever produced matched what the appliance and its own UI
show.
The error sentence is the diagnostic: "Dataset 'tank/empty' does not have any
matching snapshots to replicate." It was discarded entirely. Replication rows
now also carry error, lastSnapshot and lastRun.
Cloud-sync is deliberately unchanged — it was already right
The obvious "fix both the same way" would have broken it. A cloudsync record
carries no top-level state at all — checked on the same appliance before and
after a run that reached SUCCESS — so its job record genuinely is the only
outcome signal. A regression test now pins the asymmetry, so a future "unify
these two" refactor fails loudly instead of silently reporting null for every
cloud-sync task.
Both replication.query and cloudsync.query keep their names on 25.04 and
26, checked against each appliance's own core.get_methods — unlike
zfs.snapshot.* → pool.snapshot.*, there is no rename to route around here.
Upgrading
replication_list's state values change from the job vocabulary
(SUCCESS/FAILED) to the replication one
(PENDING/RUNNING/FINISHED/ERROR/HOLD), and stop being null for tasks
that have not run. Anything matching on the old strings needs updating; anything
displaying them to a person now agrees with the appliance UI.
v0.7.0
Changed (BREAKING)
- Requires MCP SDK 2.0 (
mcp[cli]>=2.0,<3.0).mcp.server.fastmcpno longer exists in 2.0; the server is now built withMCPServerand reports its package version in the stdio handshake.
Fixed
- Authentication could never use
auth.login_ex. It defaulted the username to empty, which TrueNAS answers withAUTH_ERR, and the fallback to the deprecatedauth.login_with_api_keysilently rescued every login — so the tool depended entirely on a deprecated method while appearing future-proof.login_exis now used wheneverusernameis configured (both 25.04 and 26 serve it) and its failures are reported, not masked: a wrong username used to be papered over. With nousername, the deprecated call is used and warned about. - The method table was pinned to one release's names. Snapshots are
zfs.snapshot.*on 25.04 andpool.snapshot.*on TrueNAS 26 — each namespace absent from the other — andservice.restartbecameservice.control. Routes now carry candidates resolved against the appliance's owncore.get_methods, so one build works on both.smart_test_resultshas no equivalent on 26 and refuses with a teaching error rather than returning an empty list. - The CLI printed success for a failed write. A governed twin returning
{"error": ...}still produced a green line and exit 0 — live-caught on TrueNAS 26 where the middleware rejected the call outright. Every CLI write now goes throughchecked(): error → exit 1, undetermined → exit 2. - The pool RCA sanitised the pool name but not its status. Unlike the sibling tools, whose RCAs are handed already-sanitized ops output, this one receives RAW
/poolrecords — so appliance-controlled text reached a finding an agent reads without passing the control-character filter..upper()is not a sanitiser. A line-wide sweep confirmed this repo was the only one affected; the other RCAs re-stringify values their ops layer already cleaned. undo applyworks from the CLI. Every write tool is imported lazily inside its own CLI command, so a CLI-driven undo ran in a process where the inverse tool was never registered and failed with "inverse tool is not registered" — for every write tool. Only the MCP entry point, which imports the whole server, worked.
Added
- JSON-RPC 2.0 over WebSocket transport (
/api/current) — the API that survives TrueNAS 26, which removed REST v2.0. Newtransport:setting per target:auto(default; probes the appliance and prefers WebSocket),websocket, orrest. The transport presents the sameget/post/deletesurface over REST-shaped paths, so no ops module changed. Every middleware method was taken from a live appliance'score.get_methodsand cross-checked against its REST result; the full write→audit→undo→verified-restore loop passes over WebSocket.websocketsis now a declared dependency (it was only transitive viamcp), and the frame ceiling is raised well above the library's 1 MiB default, which closes the connection with1009 message too bigon any large listing. doctorreports the transport in use, and no longer warns that "this tool needs a WebSocket transport" while using one.- Pool reads accept a pool name, not just the numeric id.
/pool/id/{id}takes TrueNAS's numeric id, soget_pool/pool_status/scrub_statusreturned404 … the id may be stalefor a pool name — the only identifier a caller ever has. This tool's own pool-health finding r
v0.6.0
Release notes — truenas-aiops 0.6.0
Previous release: 0.5.0.
BREAKING — the authorization layer is removed
This tool no longer decides whether a write is permitted. Read-only mode
(<PREFIX>_READ_ONLY), the graduated-approval / approver gate, and the
rules.yaml deny engine are all gone. Whether an operation runs is the
agent's judgement, or the permission of the account you connect it with — point
it at a read-only credential and the write fails at the server, the place that
actually owns the permission.
What the tool guarantees instead is that nothing is silent: every operation,
over MCP and the CLI alike, lands a row in the audit log — there is no
unaudited entry point. Destructive writes still capture their before-state and
record an undo token where a clean inverse exists.
- If you set
<PREFIX>_READ_ONLY=1, it now has no effect and the MCP server
logs a warning at startup. Restrict writes via the connecting account instead. <PREFIX>_AUDIT_APPROVED_BY/<PREFIX>_AUDIT_RATIONALEstill work, but are now
optional audit annotations — recorded on the row when set, never required.- The declared
risk_levelis carried into the audit row as a descriptive tier
(a label, not a gate).
The governance harness is now: audit (MCP+CLI, unbypassable) · runaway/budget
safety guard · undo recording · output sanitize. policy.py is a small
risk-tier classifier; governance/readonly.py is deleted.
Also in this tool
- New
scheme:(default https).
v0.5.0
Release notes — truenas-aiops 0.5.0
Previous release: 0.4.0.
In this tool
- TrueNAS 26 removes the REST API this tool speaks. REST was deprecated in 25.04 and is gone in 26 (in beta now), replaced by JSON-RPC over WebSocket.
doctornow reads the server version and warns on 25.10.1+ (which raises a deprecation alert on every call) and errors on 26+; the connection layer explains the removal instead of returning a pile of 404s. The transport migration is not in this release — it is a second backend, not a path change, and it is tracked separately. restart_servicevalidates the service name. It previously forwarded any string, and the code that looks like a guard was only capturing prior state.ssh— the operator's out-of-band recovery path — now needs an explicitconfirm. A/serviceread that fails proceeds rather than blocking.- New
scheme:(defaulthttps) — the base URL was hardcoded.
Every tool in the line: previews and undetermined outcomes
This release fixes three harness defects that were silently degrading the audit
trail and the undo store.
A write that loses its response is no longer recorded as a failure. The
harness assumed a sanitized error meant nothing had happened. That assumption is
false in exactly the case that matters most: when a write severs its own
connection, the request has already landed, the response cannot come back, and
the operation was recorded as status=error with no undo token created at
all. Transport-level failures are now audited as status=unknown, the result
says plainly that the operation may have taken effect and should be verified
before retrying, and a write that stashed its before-state has its inverse
recorded anyway — flagged effectVerified: false, which undo_list and
undo_apply both surface. Existing undo.db files are migrated in place; their
rows read as verified, which is accurate, since the old code only ever recorded
on the confirmed path.
A dry-run no longer writes an undo token. Previews were recording inverses
built from a before-state they never had: the undo callback's permissive default
filled the gap with a guess, producing a real, applicable token for an operation
that never happened.
A dry-run no longer demands a named approver. Requiring an approval in order
to ask whether something needs approval inverts what a preview is for. The tier
is still computed and still audited, so the preview can tell you an approver
will be needed; it just no longer refuses to answer. The write itself is gated
exactly as before.
The invariant, now stated: a dry_run may read; it must never write. Guards
run on the preview path, which means a preview can and does report that an
operation would be refused.
Also line-wide
- Truncated text now ends in an ellipsis instead of being cut silently. This
line already treats a silent cut as a defect for lists; it was doing exactly
that to strings. - Error messages are capped at 800 characters, not 300. These messages end
with what to do instead, so the cap was removing the most useful sentence of
every long refusal.
Verification status
The version detection is built from TrueNAS's published deprecation notes
(deprecated 25.04, removed in 26) and is not verified against a 26 appliance
— 26 is in beta and not available here. The thresholds and the messages are
doc-modelled; the REST paths themselves remain as previously documented in
docs/VERIFICATION.md.
v0.4.0
Release notes — truenas-aiops 0.4.0
Previous release: 0.3.0.
Headline: read-only mode
export TRUENAS_READ_ONLY=1With this set the 6 write tools are never registered — an MCP
client lists 19 tools instead of 25. The writes are not hidden
behind a flag and not merely refused on call: they are absent from the session,
so a model cannot invoke one and cannot be argued into one. For a reviewer this
is checkable rather than promised — connect, list the tools, and the writes are
not there.
Enforcement is two layers deep: the @governed_tool harness refuses every
non-read operation (covering the CLI and in-process callers too), and the MCP
server removes write tools from list_tools(). Changing entry point does not
get around it.
BREAKING — return shapes changed
This release changes payloads that callers may be parsing. Both changes exist
to stop a result from misrepresenting itself:
- Absent fields are now
null, not"". A missing value and an empty value
were previously indistinguishable, which invited consumers to invent the
difference. Keys are still always present — only the value may be null. - Anything with a
limitnow returns an envelope —
{"<items>": [...], "returned": N, "limit": L, "truncated": bool}. Truncation is
measured (one extra row is fetched), never inferred from the page happening to
be full. Where a genuine pre-cap total is knowable it is reported astotal;
where it isn't,totalis deliberately omitted rather than echoingreturned.
New: read-only diagnostics / RCA
Two new read-only analyses — pool_health_rca and alert_and_capacity_rca — plus a
diagnose CLI group. Every finding cites the measured number that tripped it
along with a cause and a concrete action, ranked worst-first with an explicit
rank field, so priority is stated in the payload rather than implied by list
order. Transparent heuristics, not a black-box verdict.
Also in this release
docs/VERIFICATION.md— what the mock suite actually guarantees, a live
verification checklist, and the criteria for claiming this tool verified.skills/truenas-aiops/references/agent-guardrails.md— for driving this tool with a
smaller / local model: which guardrails are now enforced for you, and a
ready-made system prompt for the rest.- Expanded operator playbooks in the skill documentation.
- The advertised tool count now matches what an MCP client actually lists
(it includesundo_list/undo_apply), and a release gate keeps it honest. - The
(preview)label has been dropped. It never meant unreleased; verification
status now lives indocs/VERIFICATION.mdwhere it can be specific.
v0.3.0
v0.2.1
v0.2.0
Security-hardening release from a line-wide code review. Highlights: secure-by-default approver gate for high/critical operations (init seeds a starter rules.yaml), percent-encoded URL path segments, single-sourced version, governance persistence + CLI write-path tests. See CHANGELOG.md for the full list. BREAKING: fresh installs with no rules.yaml now deny high/critical writes until an approver is recorded.