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.10.2

Latest

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.