Changelog

Every release, and what changed

Calendar versioned as YY.MM.PATCH. Breaking changes are stated at the top of the release that contains them, not left for you to find in production.

26.11.0

Latest

Security maintenance

The November security release. Advisories published in the two weeks after 26.10.4 covered Caddy, the Go standard library and toolchain, CPython, three Python packages, FFmpeg, Valkey, PgBouncer, and Kerberos and SQLite in the database images. Four were reachable in FreeSDN as shipped, three at the Caddy web edge and one in camera streaming, and all four are fixed.

Upgrading: Rebuild the images and recreate the containers. No configuration changes are needed. From a version older than 26.10.4, follow the 26.10.4 notes first.

  • SecurityCaddy 2.11.9 and Go 1.27.4 for the web edge. Caddy had forwarded hop-by-hop headers to the backend on /api (CVE-2026-55458), any client could crash it with a request body over the 50 MB limit (CVE-2026-97054), and a crafted compressed frame could crash it and the NetBird client (CVE-2026-116591).
  • SecurityFFmpeg 9.0.2 now carries FFmpeg's own fixes for ten of the eleven advisories against it, including the RTSP redirect loop a malicious camera could trigger, which 26.10.4 had accepted for 14 days. No FFmpeg release contains them yet. The eleventh is in the IMF demuxer, which FreeSDN never uses: it only gives FFmpeg rtsp:// and rtsps:// URLs.
  • SecurityPgBouncer 1.26.1 in the optional pooling profile: an unauthenticated client could crash it or make it stop serving. Valkey 9.1.3, CPython 3.14.10, tornado, pyasn1 and dnspython are updated too, and pip-audit reports no known vulnerabilities in the backend's dependencies.
  • Security14 Kerberos and 3 SQLite advisories in the database images have no fixed Debian package. Each is reviewed as unreachable, in writing and with an expiry date, and the reviews were checked inside the built images.
  • FixedThe HA standby database now runs with no-new-privileges, as the primary already did.
  • FixedThe license notices shipped with the nine compiled Go components, and the third-party license lists for the backend and web interface, are regenerated from what is actually built.

26.10.4

What 26.10.3's checks were still red on

26.10.3 shipped with two checks failing and one that never finished. This release clears them, and it was proved green on GitHub's own runners, on the exact commit, before it was published. The one application change fixes a TrueNAS backup download that could fail after 5 seconds.

Upgrading: On Lite set REDIS_MEM_LIMIT=384M, on Pro REDIS_MEM_LIMIT=1G, in your saved env file, then recreate the Valkey container. Max needs nothing. From an older version, follow the 26.10.3 notes first.

  • SecurityFFmpeg 9.0.2, compiled into the backend and go2rtc images, has five advisories with no fix released yet. They now go through the same written, expiring review as other unfixed image findings. One is reachable: a malicious camera can make FFmpeg follow RTSP redirects forever and spin one process. It is accepted for 14 days only, until FFmpeg 9.0.3.
  • FixedA TrueNAS backup download could fail after 5 seconds with "TrueNAS backup transfer failed". Its WebSocket client read on one thread while writing on another over the same TLS connection, which OpenSSL does not allow, and a request could be lost when the appliance's session tickets arrived. It happened to 4 of 2,400 transfers under load. The control channel now runs on a single thread.
  • FixedBuilding the backend and go2rtc images could fail when ffmpeg.org reset the connection during the FFmpeg source download, as 5 of 55 builds in CI did. The download now retries any network error, and the archive is still checked against its SHA-256 and FFmpeg's signature.
  • FixedValkey could be OOM-killed during a persistence fork under write pressure on Lite and Pro: the check that pressure-tests those limits failed in 8 of 28 runs on Lite and 5 of about 20 on Pro. The Valkey container limit is now 384 MiB on Lite, and 1 GiB on Pro (the same as Max, which never failed). Valkey's data budgets are unchanged, and a limit is a ceiling, not a reservation.
  • FixedThe full backend test suite in public CI was being cancelled by a late weekly scheduled run sharing its concurrency group. They no longer cancel each other.
  • FixedBackend tests could fail in public CI because the whole suite ran as one process for almost five hours, and its garbage collections grew to about 3 seconds. It now runs as 8 parts, and the Backend check passes only when they ran every collected test exactly once with none failing.
  • FixedAnother VoIP site-access test was failing while its counterpart passed without testing anything. Every test covering those lookups is now fixed and proven to catch a disabled access check.

26.10.3

Security maintenance

The first monthly security release. Two weeks after 26.10.2, 108 dependency advisories had been raised against the published source, 9 of them critical. They came down to 21 packages, one Go toolchain patch and one Go library; none was in FreeSDN's own code. It also makes the metrics profile buildable again and changes how image findings are gated, so a failing check means something can be fixed.

Upgrading: Pull and rebuild every image with docker compose build --pull, then restart. There are no database migrations and no configuration changes.

  • Securityuv 0.13.5 (critical; FreeSDN runs it at runtime to install plugin dependencies), certifi 2026.10.23 (critical; the CA bundle behind every adapter's TLS), aiohttp 3.14.5 (eight high advisories; the adapters' HTTP client), Pillow 13.0.0 and opentelemetry-api 1.45.1.
  • SecurityEvery Go component built from source moves to Go 1.27.3 for a standard-library advisory, including the gosu helper inside the database images. golang.org/x/net 0.60.0 (HTTP/2) in Tailscale, go2rtc, postgres-exporter, Prometheus and rclone, and go-chi 5.4.0 in rclone and the Caddy edge.
  • Securitynginx 1.30.6 for the optional nginx frontend and the HA load balancer: three high advisories in the core HTTP module and the map directive. It stays on the stable branch.
  • FixedThe metrics profile could not be built from the 26.10.2 source: the Prometheus image audits its web UI during the build, and new advisories made that audit fail. Its pinned lockfile is updated.
  • FixedThe backend test job in public CI had not run since 26.10.2. It stopped before the first test because of how pytest was invoked; the product was unaffected.
  • SecurityImage scans still list every finding, but only two things fail the build now: a HIGH or CRITICAL finding with a fix available, and one without a fix that has no current written review. Debian 13 carries 62 unfixed findings in the database images, so the old rule could never pass however current the images were.

26.10.2

A controller that imported nothing, quietly

Every fix in this release was found by running 26.10.1 against real hardware rather than by reading code. The most serious one had been breaking device discovery for every controller type without reporting it: the task that starts a discovery crashed before contacting anything, while a separate health check kept marking the controller connected with a fresh last-sync time and no error. The result was a controller that looked healthy and had never imported a single device.

Upgrading: Rebuild and restart. There are no database migrations and no configuration changes. If a controller has been showing devices you did not expect to be missing, run a discovery after upgrading.

  • NewDisabling a module now actually closes its endpoints. Previously the toggle hid the navigation and every route of a disabled module stayed callable with valid credentials. Only an explicit off refuses; an organization that never recorded a choice is still served, because treating absence as denial would lock it out of the whole product.
  • FixedPressing Disable did nothing for a module nobody had explicitly enabled, which is most of them. The toggle now records the decision.
  • FixedTrueNAS write support was under-reported in the product for two months: the badge said mock-tested while the storage write path had been validated against a real appliance. It now reads partial.
  • FixedDevice discovery crashed before contacting any device, for every controller type, and reported itself as healthy while doing so.
  • FixedA newly created site did not appear when adding a device, hypervisor or NVR for up to five minutes.
  • FixedAsking a camera for its RTSP URL returned a server error for every device.
  • FixedThe Omada Open API integration had never worked; its token, site-list and introspection calls all failed on their first line.
  • FixedPointing a vendor-specific endpoint at a controller of another vendor, or asking for a feature the firmware does not implement, returned a server error instead of saying which.
  • FixedThree VPN endpoints took over twelve seconds each whenever NetBird is not configured, which is the default, so the VPN page appeared frozen.
  • FixedRequesting an import session, recording or camera setting that does not exist returned a server error instead of not-found.
  • FixedDevices could be stored with a status the rest of the product does not understand, so a healthy appliance counted as offline.
  • FixedFirmware showed as unavailable for devices whose version had already been collected, and Proxmox node versions were never stored.
  • FixedTrueNAS synchronisation failed while its own controller page reported a successful connection.
  • FixedTailscale could not start inside the API container, whose filesystem is read-only by design.
  • NewAI provider model lists are now fetched from the provider with your own API key, so the picker shows the models that key can actually call.
  • FixedThe metrics profile could not be built from a published release, because a lockfile carrying a critical dependency pin was silently dropped on the way out. A release gate now fails instead of publishing a tree that cannot build.
  • FixedThe metrics profile was missing from the Max tier preset, so an enterprise install evaluated no alert rules at all.
  • DocsThe example environment file documented four TLS and port settings that nothing reads, and omitted the four that work.

26.10.1

Documentation corrected after publication

26.10.0 published with documentation that still called it unreleased. The README opened with “Development target: 26.10.0 (unreleased)” on a repository that already carried the tag and the release, and the security policy said a version in source did not mean a release had been qualified. Both had been. No application code changes.

Upgrading: Nothing to do. This release changes documentation only; there are no application, database or image changes from 26.10.0.

  • DocsThe README described 26.10.0 as an unreleased development target, next to its own v26.10.0 tag and release.
  • DocsThe security policy stated that a version in source does not mean a public release or its images have been qualified, after both had been.
  • DocsThe support boundary, installation acceptance and upgrade documents described the release as a prepared candidate and the project as Early Access.
  • DocsUpgrade and quickstart guidance told readers not to invent a tag for the prepared candidate. The tag exists, so the guidance is now to install from a published release tag rather than the default branch.

26.10.0

Guards that did not guard

The recurring defect this time was not a broken feature but a check that could not fail. A type-drift guard with no schema and no caller. A staging-bypass ratchet that pinned a migration nobody was running. A compose file that passed an Alpine base into a Dockerfile requiring Debian, so building the database failed outright. Two qualification scripts demanding a runtime marker no shipped image writes, and a third asserting that the database refuses the very upgrade this release is built to perform. Each one sat behind a green build.

Upgrading: Pull, rebuild, restart. The database keeps its existing volume: no logical migration, no new volume, no locale change, and the collation, sort order, postgres uid and data checksums are exactly what 26.09.0 left behind. Verified against the image built from the published 26.09.0 tree. The one breaking change affects the HA profile only, where Sentinel discovery moves from DNS to reserved numeric addresses; read docker/redis/HA-UPGRADING before upgrading an HA deployment.

  • FixedThe shipped compose passed an Alpine base as a build argument into a Dockerfile that requires Debian, so `docker compose build postgres` failed for every from-source deployment. A build argument overrides the Dockerfile's own default, which is how the database runtime decision was undone without anyone noticing.
  • FixedTwo qualification scripts still demanded the Alpine runtime marker, which no shipped image writes. One failed the HA qualification outright; the other rejected the shipped image as unqualified, disabling the recovery procedure that the database's own refusal message points operators at. Both gate CI.
  • FixedThe database qualification asserted that the image refuses to reuse an existing volume, which is the opposite of the in-place upgrade this release performs. Because the check ran in the foreground, an image that correctly started never returned: it burned its whole deadline and reported a timeout instead of the contradiction.
  • FixedThe README's first words told operators not to update existing database containers in place, and the upgrade guide described an offline dump and restore into a new volume. Following either would have meant hours of work to achieve what pulling the image does.
  • FixedThe database entrypoint, which decides whether an operator's existing volume is adopted, started or refused, had no test at all. It now has 19 checks against the image built from the published 26.09.0 tree, including four negative controls that must refuse.
  • FixedThe API type-drift guard had no schema to check against and no caller, so the generated types could drift from the backend's response models silently. The schema now ships, the guard runs in CI, and it was verified to fail on a single added field rather than merely to pass.
  • SecurityTwo critical handlebars advisories that no image scanner could see, because Prometheus compiles its UI into the Go binary. Found by auditing the UI dependency lock rather than the image.
  • SecurityTen reachable Go advisories cleared by moving every build to Go 1.27.2, and the redis exporter is now built from source: upstream ships three unfixed Go standard-library advisories that no published tag resolves. Every release component now has zero actionable high or critical findings.
  • FixedThe backend image could not be built at all, and the demo deployment pointed at a path the public flattening removes.
  • DocsSix write-safety claims were untrue as written, the changelog had no section for three published releases, and the site changelog skipped two of them, one a feature release.

26.09.0

Things that had never worked

66 fixes, and the theme is not regressions — it is features that had never worked at all. Every MFA recovery code the system had ever issued was rejected before verification. Restoring a config-only backup NULLed every credential secret in the organization and reported success. Plugin install was impossible on any production deployment. The HA overlay could not start, and streaming replication had never once worked. Nearly all of it was found by RUNNING the product on a clean Linux box, from a fresh clone, rather than by reading it.

Upgrading: Pull, rebuild, restart. The migration chain runs itself and was verified in place from 26.08.4 on existing volumes. Two things do not apply themselves: docker/postgres/init-replication.sh only runs on a FRESH Postgres volume, so enabling --ha over an existing single-node install needs the manual command in that script's header; and .env.max.example no longer enables the dr profile, which started an rclone container that cannot work without operator credentials.

  • FixedMFA recovery codes are minted at 10 characters and the login schema capped the field at 8, so every recovery code the system had ever issued was rejected before verification.
  • SecurityA camera stream token, designed to be safe in a URL query string, authenticated the entire REST API with its owner's full role. The scope check existed on the WebSocket and on no other route.
  • FixedRestoring a config-only backup with “Overwrite existing” NULLed every credential secret in the organization and reported success.
  • FixedPlugin install was impossible on any production deployment: the volume mounted over a path the image never creates, so it was root-owned while the container runs as uid 1001. Firmware upload, evidence export and the backup destination had the same fault.
  • Fixed./install.sh was not executable. Fifteen of nineteen tracked shell scripts were committed non-executable, so the documented first command failed with Permission denied on every fresh clone.
  • FixedEvery event a Celery task published was silently discarded: the bus only fanned out to Redis when a client was set, and nothing in the Celery bootstrap ever set one.
  • FixedThe HA overlay could not start. pgbouncer's tmpfs was owned by uid 999 while its image runs as uid 70, and the streaming-replication role had never been created by anything. --tier max --ha now brings up 19/19 healthy and passes a Sentinel failover drill with the replica promoted in about eight seconds.
  • FixedChanging your own password and enrolling MFA were unreachable from the shipped UI.
  • FixedThree API endpoints were unreachable because a catch-all route was registered ahead of them, including a certificate delete that dispatched to the VM-delete handler.
  • SecurityThe edge CSP no longer permits arbitrary WebSocket hosts; the hardening had been applied to API responses and not to the page. A factory reset could be confirmed with the string “false”.

26.08.4

Storage you can write to, and observability that acts

Storage was a very good pair of eyes with almost no hands, and the collector was a dead end. This release gives both something to do. The whole storage write path was validated live, end to end, against a real TrueNAS 26.0 appliance rather than only mock-tested.

Upgrading: Pull, rebuild, restart. No migration.

  • NewBack up to the TrueNAS you already monitor. A backup destination that names a managed controller instead of asking you to retype its address and API key. Each backup is stored as its own child dataset, so retention prunes it by destroying that dataset, which keeps per-backup ZFS visibility. Save, restore and prune were all proven against the real appliance.
  • NewStorage writes from the Storage page: take a snapshot, create a dataset, start a scrub, delete a snapshot. Each is staged through the same review-and-apply flow as every other managed write, and each is disabled with a clear reason on a legacy REST appliance that cannot perform it. A Snapshots section lists what is present.
  • NewObservability can trigger automation. The collector now publishes events that automation rules can subscribe to, so a syslog line or a trap can drive an action instead of only being recorded.

26.08.3

The plugin system, and a registry to install from

A feature release that built out the plugin system: the typed SDK, a scheduler for plugin-owned periodic work, the install and review endpoints, and the Plugins page in Settings. It shipped alongside the signed plugin registry going live, which is what gave the install path somewhere to install from. 95 files changed.

Upgrading: Pull, rebuild, restart. No migration.

  • NewThe typed plugin SDK, which is what a plugin is written against, grew to cover the capability surface plugins actually need rather than the subset that existed before.
  • NewA plugin scheduler, so a plugin can own periodic work instead of needing a core task to be added for it.
  • NewPlugin install and review endpoints, plus the Plugins page in Settings, so installing and inspecting a plugin is a supported operation rather than a manual file drop.
  • DocsThis release was published without release notes at the time. The description here was reconstructed in October 2026 from the published tag's own contents, so that the changelog does not skip it.

26.08.2

Writes that said they worked, and did not

A large patch release: 55 fixes and no schema changes. The theme is device writes that reported success without the device agreeing, which is the failure mode this project exists to avoid. It also fixes a session bug that logged every user out roughly an hour after signing in.

Upgrading: Pull, rebuild, restart. No migration. Three breaking API changes, all removals of parameters that never did anything.

  • FixedSessions no longer end after one hour. The refresh endpoint required the token in the request body while documenting itself as accepting the httpOnly cookie, so the browser sent an empty object and received a validation error. The frontend treats a failed refresh as a dead session, so every user was returned to the login screen at token expiry.
  • FixedA device write that a Grandstream phone refused reported success. Seven call sites discarded the result. The reboot path could not report a refusal at all, because every failure branch returned success, including authentication failure.
  • FixedFirmware rollback ran a full permission check and then returned success from a stub that performed no rollback. It now answers 501 and says why: no adapter exposes a rollback primitive.
  • FixedNotification providers that were configured now actually send. Eight of fifteen, including Mailgun, SendGrid, Brevo, Postmark, Resend, Amazon SES and both Twilio channels, were constructed as a webhook with no URL and delivered nothing while reporting healthy.
  • FixedScheduled work runs. Firmware upgrade schedules had never executed because their next run time was never written, and SLA report schedules were broken at both ends.
  • FixedThe audit trail records the actor's email again. It was accepted by the logging call, documented, and discarded before the row was written, leaving only a user id that stops resolving once the account is deleted.
  • FixedTraffic analysis reports real numbers. Every NetFlow v5 record carried wrong values, flushes dropped batches under load, and custom deep packet inspection rules classified nothing.
  • SecurityAn imported OpenVPN profile was stored in plaintext, private key included. It is encrypted at rest now.
  • SecurityAn API key kept the permissions of its owner's previous role after a demotion. Scopes are intersected with the owner's current role on every request.
  • SecurityA single-camera stream token opened the organization-wide realtime socket.
  • SecurityZero-touch provisioned Grandstream phones were sent the wrong SIP password, and one endpoint returned a SIP secret in its response body.
  • BreakingTwelve endpoints no longer accept a site_id parameter. They ignored it before, so responses are unchanged; the records behind them have no site column to filter on.
  • BreakingPOST /firmware/devices/{id}/rollback returns 501 rather than 200. It never performed a rollback.
  • BreakingThe OpenVPN import endpoint no longer accepts a description. There was no column to store it in.

26.08.1

Every open dependency advisory closed

A patch release with no new features and no schema changes. Every change is a security fix, a correctness fix, or a documentation correction. The project went from 28 high and 20 moderate dependency advisories to zero.

Upgrading: Pull, rebuild, restart. No migration.

  • SecurityAll open dependency advisories resolved, including cryptography 49.0.0 (a PKCS#7 Bleichenbacher oracle) and aiohttp 3.14.1 (an out-of-bounds read in the C HTTP parser).
  • FixedWebhook retries were never delivered. A failed delivery was queued to a Celery queue that no worker declared or consumed, so it sat in a retrying state permanently and never reached the dead-letter table. Nothing surfaced an error, so the only visible symptom was a webhook that quietly stopped arriving.
  • FixedSingle sign-on could not be started. OIDC, SAML and LDAP providers could be configured and the callback route worked, but the login screen offered password authentication only, so a configured provider was unreachable.
  • FixedAn access-control relock task was registered with no worker to run it, so a door unlock window could elapse without the database row being restored to locked.
  • FixedThe agent registration command failed against any current controller, which is the first command an operator runs.
  • DocsThe write-safety default was documented backwards across roughly thirty pages. They claimed the adapter read-only flag defaults to true and that a fresh install cannot touch hardware; the shipped Compose stack has defaulted to read-write since June, because FreeSDN is expected to manage your gear out of the box.

26.06.1

First public release

The first public release of FreeSDN: a vendor-neutral, self-hosted infrastructure controller providing one interface, one API and one credential vault for the network switches, firewalls, cameras, VoIP, hypervisors and storage you already own.

Upgrading: Initial release.

  • NewTen independently loadable modules covering network, firewall, cameras, VoIP, hypervisor, storage, backup, observability, access control and an AI assistant.
  • NewVendor adapters for TP-Link Omada, Ubiquiti UniFi, MikroTik, OpenWrt, OPNsense, pfSense, Hikvision, ONVIF, FreePBX, Grandstream, Proxmox VE and TrueNAS, each carrying a published maturity label.
  • NewThe staged-write pipeline: every device mutation is recorded, shown, and applied only behind a platform-wide flag and an explicit per-request confirmation.
  • NewMulti-organization role-based access control with per-user site grants, credential encryption at rest, and an immutable audit log.
  • NewA typed plugin SDK with a permission-declared runtime, and an MIT-licensed edge agent for discovery inside remote networks.
  • DocsPublished honestly as young software with limited field exposure beyond its maintainer's own mixed-vendor deployment, with no third-party security audit or certification claimed.

Full release bodies, source archives and signed tags are on GitHub Releases. Upgrade instructions are in the upgrade guide.

Running an older version?

Upgrading is a pull, a rebuild and a restart. Take a backup first, as with any infrastructure component.