Repository navigation
Releases
v3.1.1
A fix for the fleet view's mapping editor, a fully type-checked web layer, and a Chocolatey
package with a descriptive title and an icon.
Upgrading from 3.1.0: nothing has to change.
Fixed
- Opening or saving a peer's mapping in the fleet view no longer fails with HTTP 500 when the
peer answers with a mapping (or one of its sections) that is not a JSON object: the form says
Could not load the peer's mapping., and nothing is sent to the peer (#121).
Changed
- The web layer (
ser2netweb.web) is fully type-annotated and checked by mypy in CI, for Linux
and for Windows (#121). - The Chocolatey package is titled ser2netweb (serial-to-network bridge) and shows the
application's icon (#109).
Full list: CHANGELOG.md.
Downloads
| File | |
|---|---|
ser2netweb-linux-x64 |
Linux x86_64 executable (glibc ≥ 2.28) |
ser2netweb-linux-arm64 |
Linux arm64 executable (glibc ≥ 2.28; Raspberry Pi 3/4/5 with a 64-bit OS) |
ser2netweb-windows-x64.exe |
Windows x64 executable; --install-service runs it as a service |
ser2netweb_3.1.1_amd64.deb |
package for Debian, Ubuntu and other .deb systems, x86_64 |
ser2netweb_3.1.1_arm64.deb |
package for Debian, Ubuntu and other .deb systems, arm64 |
ser2netweb-3.1.1-1.x86_64.rpm |
package for Fedora, RHEL, AlmaLinux and other .rpm systems, x86_64 |
ser2netweb-3.1.1-1.aarch64.rpm |
package for Fedora, RHEL, AlmaLinux and other .rpm systems, arm64 |
ser2netweb-install.sh |
installs a Linux executable as a systemd service, checked against SHA256SUMS |
ser2netweb-standalone.service |
systemd unit for the Linux executables (data in /var/lib/ser2netweb) |
ser2netweb-*.cdx.json |
CycloneDX SBOM of each executable (3 files) |
ser2netweb-*.THIRD-PARTY-LICENSES.txt |
licence texts of the components inside each executable (3 files) |
SHA256SUMS |
SHA-256 checksums of all of the above |
The .rpm files attached here are not signed themselves; the copies in the package repository
are. SHA256SUMS and the build provenance cover both.
PyPI: pipx install ser2netweb, or pipx install "ser2netweb[all]" for MQTT, LDAP, OIDC and in-process certificates (pypi.org/project/ser2netweb, uploaded by trusted publishing with PyPI's attestations).
Docker image: ghcr.io/haliskilic/ser2netweb:3.1.1 (also 3.1 and latest), and its copy docker.io/haliskilic/ser2netweb, for linux/amd64, linux/arm64 and linux/arm/v7.
Also through: the signed APT/DNF/zypper repositories at https://haliskilic.github.io/ser2netweb/, Homebrew (brew install haliskilic/tap/ser2netweb), Scoop (scoop bucket add haliskilic https://github.com/haliskilic/scoop-bucket), the snap (sudo snap install ser2netweb), and winget and Chocolatey once their first submissions are through moderation.
Verify a download: sha256sum -c SHA256SUMS --ignore-missing, and the build provenance with gh attestation verify ser2netweb-linux-x64 -R haliskilic/ser2netweb (the image: gh attestation verify oci://ghcr.io/haliskilic/ser2netweb:3.1.1 -R haliskilic/ser2netweb). The Windows executable is not code-signed.
v3.1.0
Device-server features from classic ser2net, MQTT 5 with Modbus pulses and safe values, trace
files and TLS files handled from the browser and the REST API, xterm.js 6 in the web console,
and signed RPM packages.
Upgrading from 3.0.x: nothing has to change; every new feature is off until it is turned on.
dnf / zypper users of the package repository: with this release every .rpm there is
signed, which changes each file once. If dnf or zypper says a package doesn't match what it
downloaded, run sudo dnf clean metadata (or add --refresh) or sudo zypper refresh. To have
each package's signature checked too, fetch ser2netweb.repo again (zypper: remove the
repository and add it again); the old file keeps working with the signed metadata checked.
Going back to 3.0.x: delete cert-notices.json from the data folder first; 3.0.x drops the
new settings on its next save (backups/ keeps the file).
Device server (#120)
- Packing for raw TCP and UDP mappings: the device's output goes out as frames (a gap, a
delimiter or a size), one datagram per frame over UDP. - One writer, read-only listeners (
network.writer_client_ips): only those clients send to
the device; the others only listen, and a listener can never push a writer out. - Open the serial port only while a client is connected (
network.open_on_connect), so
another program can use it in between. - Port pools (classic ser2net's rotators): one TCP port hands each client to the first free
mapping of a pool. - Connect-out failover over
network.remote_targets, with an optional return to the first. - The classic ser2net import carries UDP remote addresses,
chardelayand rotators over.
MQTT and Modbus (#118)
- MQTT 5 per mapping (3.1.1 stays the default): clean start, session and message expiry,
the broker's reason codes in the log and the status. - Modbus pulses: a coil with
pulse_msswitches itself off again. - Modbus safe values: a point with
safe_value/safe_after_sis written to that value
once the broker (or the Sparkplug primary host) has been away that long, once per outage.
Operations (#119)
- Trace files in the REST API (list, download, delete; operator tokens) and timed
recordings of 15 min, 1 h or 24 h that end by themselves. - TLS certificate about to expire notification and metric.
- Upload TLS files in the browser (admins), checked before they are kept.
- Clock-step watch: a clock set while ser2netweb runs is logged, audited, marked in traces
and counted.
Web console (#102)
- xterm.js 6.0.0 with
@xterm/addon-fit0.11.0; the console's scrollbar now fades when not
in use.
Packaging (#117)
- Signed RPM packages in the package repository, checked by dnf and zypper.
- Packages built from the recipes in
packaging/know they are distribution packages
(--about, data directory, install hints). - EPEL 10 builds, a conda-forge recipe, and the reverse-proxy examples of
docs/REVERSE-PROXY.mdrun behind real nginx in CI.
Fixed
- Windows: a save no longer fails with "Could not save config.json" (a 400 in the web UI)
while config.json is open for a moment, e.g. during an antivirus scan. - A serial port at a baud rate without a termios constant (250000, 74880, …) got the input
speed 38400 on musl systems (Alpine) (#117). - A mapping's TLS key that is a copy of the admin UI's is refused too; a
!!note is written
as text in a hex trace.
REST API 1.2.0; the snap is now released to every channel. Everything, with the upgrade notes
in full: CHANGELOG.md.
Assets
| File | |
|---|---|
ser2netweb-linux-x64 |
Linux x86_64 executable (glibc ≥ 2.28) |
ser2netweb-linux-arm64 |
Linux arm64 executable (glibc ≥ 2.28; Raspberry Pi 3/4/5 with a 64-bit OS) |
ser2netweb-windows-x64.exe |
Windows x64 executable; --install-service runs it as a service |
ser2netweb_3.1.0_amd64.deb |
package for Debian, Ubuntu and other .deb systems, x86_64 |
ser2netweb_3.1.0_arm64.deb |
package for Debian, Ubuntu and other .deb systems, arm64 |
ser2netweb-3.1.0-1.x86_64.rpm |
package for Fedora, RHEL, AlmaLinux and other .rpm systems, x86_64 |
ser2netweb-3.1.0-1.aarch64.rpm |
package for Fedora, RHEL, AlmaLinux and other .rpm systems, arm64 |
ser2netweb-install.sh |
installs a Linux executable as a systemd service, checked against SHA256SUMS |
ser2netweb-standalone.service |
systemd unit for the Linux executables (data in /var/lib/ser2netweb) |
ser2netweb-*.cdx.json |
CycloneDX SBOM of each executable (3 files) |
ser2netweb-*.THIRD-PARTY-LICENSES.txt |
licence texts of the components inside each executable (3 files) |
SHA256SUMS |
SHA-256 checksums of all of the above |
The .rpm files attached here are not signed themselves; the copies in the package repository
are. SHA256SUMS and the build provenance cover both.
PyPI: pipx install ser2netweb, or pipx install "ser2netweb[all]" for MQTT, LDAP, OIDC and in-process certificates (pypi.org/project/ser2netweb, uploaded by trusted publishing with PyPI's attestations).
Docker image: ghcr.io/haliskilic/ser2netweb:3.1.0 (also 3.1 and latest), and its copy docker.io/haliskilic/ser2netweb, for linux/amd64, linux/arm64 and linux/arm/v7.
Also through: the signed APT/DNF/zypper repositories at https://haliskilic.github.io/ser2netweb/, Homebrew (brew install haliskilic/tap/ser2netweb), Scoop (scoop bucket add haliskilic https://github.com/haliskilic/scoop-bucket), winget, Chocolatey and the snap (sudo snap install ser2netweb).
Verify a download: sha256sum -c SHA256SUMS --ignore-missing, and the build provenance with gh attestation verify ser2netweb-linux-x64 -R haliskilic/ser2netweb (the image: gh attestation verify oci://ghcr.io/haliskilic/ser2netweb:3.1.0 -R haliskilic/ser2netweb). The Windows executable is not code-signed.
v3.0.1
More ways to install ser2netweb, dependency ranges that distributions can meet, and fixes for
3.0.0. Upgrading from 3.0.0: nothing has to change, except for Docker users of the old
image name (below).
Fixed
SER2NETWEB_PORTwithoutSER2NETWEB_BIND_IPwas ignored. The port (also under its old
nameSER2NET_PORT) now applies on its own and keeps the bind IPconfig.jsonhas;
--healthcheckalready read it that way and probed a port the server was not on. A port
outside 1-65535 now stops the start with one line in this case too (#122).- Homebrew: data of 3.0.0 run by hand is found again. 3.0.0 took the formula's install for
a pip install; while the user's data folder has aconfig.jsonand
$(brew --prefix)/var/ser2netwebhas none, it stays the default (#122). - A pyserial-asyncio-fast that is installed but fails to import is reported at start instead of
being replaced silently by the built-in copy of its transport (#122).
Added
- Signed APT, DNF and zypper repositories at https://haliskilic.github.io/ser2netweb/,
updated with every release (docs/PACKAGES.md; key fingerprint
B587 60E7 C0B0 515E 81D2 50F9 8164 D694 711B B799) (#109, #110). - Package managers: winget (
haliskilic.ser2netweb), Scoop (bucket
haliskilic/scoop-bucket), Chocolatey, Homebrew (brew install haliskilic/tap/ser2netweb),
an AUR packageser2netweb-bin, a snap (USB serial adapters through
sudo snap connect ser2netweb:raw-usb), and a Docker Hub mirror
docker.io/haliskilic/ser2netweb. ser2netweb recognises these installs, keeps its data
where an upgrade doesn't remove it, and--aboutnames the kind (#109, #111, #113). - Recipes for distributions, each built and started in CI: Debian / Ubuntu, Fedora,
openSUSE, Arch, Alpine, nixpkgs, Void, Gentoo, FreeBSD and MacPorts (packaging/,
docs/PACKAGING.md) (#109, #114, #116). - A built-in copy of pyserial-asyncio-fast's transport, used when that package is not
installed, and support for Starlette 0.46 to 1.5 (#112).
Changed
- Docker: the image name
ghcr.io/haliskilic/ser2netno longer exists. Since 2026-11-13
every pull of it fails and the 2.x images are no longer on GHCR; the image is
ghcr.io/haliskilic/ser2netweb(also on Docker Hub). Changeimage:in your compose file
ordocker runcommand; the data volume is used as it is (#115, #122). - Dependency ranges of the Python package are wider: the lowest versions that work
(Debian 13, RHEL 10, Ubuntu 26.04), without upper limits. The executables, the Docker image
andrequirements.txtkeep the tested pins; CI also runs the suite on the lowest and, weekly,
on the newest versions (#112). - Release pipeline: a release starts the channel workflows itself, and one that can't be
started no longer keeps the others from starting (#110, #122).
Everything, with the upgrade notes in full: CHANGELOG.md.
Assets
| File | |
|---|---|
ser2netweb-linux-x64 |
Linux x86_64 executable (glibc ≥ 2.28) |
ser2netweb-linux-arm64 |
Linux arm64 executable (glibc ≥ 2.28; Raspberry Pi 3/4/5 with a 64-bit OS) |
ser2netweb-windows-x64.exe |
Windows x64 executable; --install-service runs it as a service |
ser2netweb_3.0.1_amd64.deb |
package for Debian, Ubuntu and other .deb systems, x86_64 |
ser2netweb_3.0.1_arm64.deb |
package for Debian, Ubuntu and other .deb systems, arm64 |
ser2netweb-3.0.1-1.x86_64.rpm |
package for Fedora, RHEL, AlmaLinux and other .rpm systems, x86_64 |
ser2netweb-3.0.1-1.aarch64.rpm |
package for Fedora, RHEL, AlmaLinux and other .rpm systems, arm64 |
ser2netweb-install.sh |
installs a Linux executable as a systemd service, checked against SHA256SUMS; moves a 2.9.x install over |
ser2netweb-standalone.service |
systemd unit for the Linux executables (data in /var/lib/ser2netweb) |
ser2netweb-*.cdx.json |
CycloneDX SBOM of each executable (3 files) |
ser2netweb-*.THIRD-PARTY-LICENSES.txt |
licence texts of the components inside each executable (3 files) |
SHA256SUMS |
SHA-256 checksums of all of the above |
PyPI: pipx install ser2netweb, or pipx install "ser2netweb[all]" for MQTT, LDAP, OIDC and in-process certificates (pypi.org/project/ser2netweb, uploaded by trusted publishing with PyPI's attestations).
Docker image: ghcr.io/haliskilic/ser2netweb:3.0.1 (also 3.0 and latest), and its copy docker.io/haliskilic/ser2netweb, for linux/amd64, linux/arm64 and linux/arm/v7.
Also through: the signed APT/DNF/zypper repositories at https://haliskilic.github.io/ser2netweb/, Homebrew (brew install haliskilic/tap/ser2netweb), Scoop (scoop bucket add haliskilic https://github.com/haliskilic/scoop-bucket), winget and the snap (edge channel).
Verify a download: sha256sum -c SHA256SUMS --ignore-missing, and the build provenance with gh attestation verify ser2netweb-linux-x64 -R haliskilic/ser2netweb (the image: gh attestation verify oci://ghcr.io/haliskilic/ser2netweb:3.0.1 -R haliskilic/ser2netweb). The Windows executable is not code-signed.
v3.0.0
ser2net is now ser2netweb. 3.0.0 renames the project to tell it apart from Corey Minyard's ser2net, the C program that Linux distributions package under that name: ser2netweb is an independent project, not affiliated with or derived from it. Apart from the names, 3.0.0 works as 2.9.0 did, and 2.9.x and 3.0 nodes of a LAN cluster work together. Read Before you upgrade for what has a new name.
.deband.rpmpackages for x86_64 and arm64:sudo apt install ./ser2netweb_3.0.0_amd64.deborsudo dnf install ./ser2netweb-3.0.0-1.x86_64.rpm, thensudo systemctl enable --now ser2netweb(Linux packages).- A PyPI package:
pipx install ser2netwebon any OS and CPU with Python 3.10 or newer (pipx / pip). - New names for the release files, the systemd units, the Windows service and the Docker image, with a move-over from 2.9.x for each; the Docker image is published under both its old and its new path.
Before you upgrade
config.json and the data folder are used as they are. Check what applies to you:
- Release files have new names (
ser2netweb-linux-x64,ser2netweb-windows-x64.exe,ser2netweb-install.sh, …), and there are no files under the old names: a script that downloadsser2net-linux-x64must change. - Environment variables have the prefix
SER2NETWEB_; theSER2NET_*names still work. When both are set, theSER2NETWEB_*one wins, also when it is empty (Environment variables). --versionprintsser2netweb 3.0.0, and webhooks, OIDC and cluster peer requests sendUser-Agent: ser2netweb/3.0.0: scripts, receivers and filters that matchser2netmust accept the new name.- Python code that imports
app.*must importser2netweb.*. - Once moved over, the systemd unit is
ser2netweb.serviceand the Windows service and its Event Log source areser2netweb: monitoring that checks the old names must follow. - Unchanged: the cluster protocol, the Prometheus metric names
ser2net_*, the MQTT and Sparkplug B defaults, theconfig.jsonkeys, the REST API paths, the cookie names (nobody is signed out) and the default e-mail sender addressser2net@<host>.
How to upgrade
- Docker: nothing has to change:
ghcr.io/haliskilic/ser2netgets 3.0.0 as the very same image. To switch to the new path and the newdocker-compose.yml(service and containerser2netweb, the volumeser2net-datakept):docker compose pull && docker compose up -d --remove-orphans(Upgrading from 2.9.x). - Standalone binary installed with
ser2net-install.sh: putser2netweb-linux-x64(or-arm64),ser2netweb-standalone.service,ser2netweb-install.shandSHA256SUMSin one folder and runsudo sh ser2netweb-install.sh(add--remove-old-userto drop the userser2net). It moves/var/lib/ser2netto/var/lib/ser2netweb, keeps the address and your drop-ins, removes 2.9.x's unit and binary onceser2netweb.serviceanswers, and never touches anotherser2net.service, such as the classic daemon's. - Standalone binary run without the script: replace it with
ser2netweb-linux-x64(or-arm64) and restart it. - Source checkout under systemd:
sudo git -C /opt/ser2net pull && sudo systemctl restart ser2netkeeps it on the old names (ser2net.pystarts 3.0.0). The commands to move it to/opt/ser2netwebandser2netweb.service, and those forser2net@instances, are in the CHANGELOG. - Windows service: from an elevated prompt run
ser2netweb-windows-x64.exe --install-service(for named services first--install-service --service-name NAMEfor each). It takes over 2.x'sser2netservice, moves its data fromC:\ProgramData\ser2net\toC:\ProgramData\ser2netweb\when it was in that default place, and puts everything back if the new service doesn't start. Then re-add the firewall rule forC:\Program Files\ser2netweb\ser2netweb.exeand delete the oldser2netrule (commands in the CHANGELOG). - New installs: the
.deb/.rpmpackage,pipx install ser2netweb, the executables or Docker. The packages don't start the service by themselves and don't move a 2.9.x install.
Everything, with the upgrade commands in full: CHANGELOG.md.
Assets
| File | |
|---|---|
ser2netweb-linux-x64 |
Linux x86_64 executable (glibc ≥ 2.28) |
ser2netweb-linux-arm64 |
Linux arm64 executable (glibc ≥ 2.28; Raspberry Pi 3/4/5 with a 64-bit OS) |
ser2netweb-windows-x64.exe |
Windows x64 executable; --install-service runs it as a service |
ser2netweb_3.0.0_amd64.deb |
package for Debian, Ubuntu and other .deb systems, x86_64 |
ser2netweb_3.0.0_arm64.deb |
package for Debian, Ubuntu and other .deb systems, arm64 |
ser2netweb-3.0.0-1.x86_64.rpm |
package for Fedora, RHEL, AlmaLinux and other .rpm systems, x86_64 |
ser2netweb-3.0.0-1.aarch64.rpm |
package for Fedora, RHEL, AlmaLinux and other .rpm systems, arm64 |
ser2netweb-install.sh |
installs a Linux executable as a systemd service, checked against SHA256SUMS; moves a 2.9.x install over |
ser2netweb-standalone.service |
systemd unit for the Linux executables (data in /var/lib/ser2netweb) |
ser2netweb-*.cdx.json |
CycloneDX SBOM of each executable (3 files) |
ser2netweb-*.THIRD-PARTY-LICENSES.txt |
licence texts of the components inside each executable (3 files) |
SHA256SUMS |
SHA-256 checksums of all of the above |
Docker image: ghcr.io/haliskilic/ser2netweb:3.0.0 (also 3.0 and latest), and the same image as ghcr.io/haliskilic/ser2net:3.0.0 (3.0, latest); linux/amd64, linux/arm64 and linux/arm/v7. 3.0 and latest are rebuilt weekly when the Debian/Python base image changes; 3.0.0 stays the image this release published.
PyPI: pipx install ser2netweb, or pipx install "ser2netweb[all]" for MQTT, LDAP, OIDC and in-process certificates (pypi.org/project/ser2netweb, uploaded by trusted publishing with PyPI's attestations).
Verify a download: sha256sum -c SHA256SUMS --ignore-missing, and the build provenance with gh attestation verify ser2netweb-linux-x64 -R haliskilic/ser2netweb, the packages likewise (gh attestation verify ser2netweb_3.0.0_amd64.deb -R haliskilic/ser2netweb); add --signer-workflow haliskilic/ser2netweb/.github/workflows/release.yml to require the release workflow, and each executable also has an SBOM attestation. The image: gh attestation verify oci://ghcr.io/haliskilic/ser2netweb:3.0.0 -R haliskilic/ser2netweb, which also holds for ghcr.io/haliskilic/ser2net:3.0.0; a weekly rebuild of 3.0 / latest is attested by image-refresh.yml. Files of 2.x releases were attested under the old name: verify them with -R haliskilic/ser2net. The Windows executable is not code-signed yet, so SmartScreen may warn; check it the same way (docs/WINDOWS.md).
v2.9.0
ser2net 2.9.0 fixes the remaining problems found in 2.8.0 (issues #48–#90) and adds the features that came with them. Several fixes change behaviour on purpose: read Before you upgrade first.
- Two-factor sign-in (TOTP) and named REST API tokens with their own role and expiry;
PATCH, a bulk import and token-protected metrics in the REST API. config.jsonreloads without a restart (systemctl reload ser2net,ser2net --reload), and so do admin TLS certificates.- Reverse proxies (trusted proxies, a public URL, serving under a path) and several instances on one machine.
- Modbus RTU replies framed reliably on USB adapters and Windows; 64-bit, float64, string and bit points, word order, block reads, a unit map, unit health with Sparkplug B DDEATH; MQTT with a private CA and client certificates.
- The right adapter among identical USB adapters, listeners for IPv4 and IPv6 at once, telnet BREAK, UDP targets; retention and privacy controls; an event loop watchdog.
- A Docker image for 32-bit ARM,
ser2net-install.shto run a Linux executable as a systemd service, and new guides: Modbus and MQTT, moving from the classic ser2net, single sign-on, Linux serial ports.
Before you upgrade
config.json and data/ are used as they are, and the start-up log notes each stored value that 2.9.0 reads differently. Check what applies to you (coming from 2.8.0 or older, read the 2.8.1 notes too):
- Reverse proxy on the same machine: list it as
127.0.0.1under Settings → Reverse proxy → Trusted proxies, or every user shows as 127.0.0.1 and all share one sign-in limit. A plain-HTTP proxy that rewritesHostmust passX-Forwarded-Host, or forms get 403. See docs/REVERSE-PROXY.md. - Stricter checks when a mapping is saved (also on import and for the mappings a reload changes): two enabled mappings sharing MQTT command topics (
/set,/tx), a client ID or a Sparkplug B edge node, MQTT wildcards in a plain base topic, one serial port under two spellings, the admin UI's port, TLS files that don't load (a disabled mapping's too), Modbus point values of the wrong type and text for protective or only-true switches are refused, and a disabled mapping that would clash on MQTT can't be started. Stored mappings still load, with a note at start-up, but their next edit must fix them. - Text values in
config.jsonare read as their setting's type: numeric text is converted,"enabled": "false"leaves a mapping stopped,"writable": "false"makes a point read-only. Switches that turn something on only for JSONtrue(MQTTtx_enabled,poll_with_clients,proxy_hsts,dual_stack, …) read text as off; protective ones (read_only,tls,require_login,mask_personal_data, MQTTtls_check_hostname, …) read it as on. An allow-list with no usable entry admits nobody instead of everyone. The start-up log notes each case. - Modbus gateway: a reply must match its request, so slaves that miscount, don't echo FC5/FC6 exactly or answer reads of unit 0 get exception 0x0B; a broadcast write to unit 0 is answered with its echo, and a read of unit 0 gets 0x0B at once.
- Start no longer restarts a running mapping: scripts must use
restart. - A mapping listens before it opens its device, so a taken port fails without touching the device; a client can connect a moment earlier than before, and what it sends before the device is open is dropped.
- A telnet BREAK now reaches the device (0.25 s; not on read-only mappings).
- TLS mappings: connect-out TLS presents its configured certificate and key; an encrypted key needs its key password set, as it no longer asks at the console.
- SIGHUP reloads
config.jsoninstead of stopping ser2net (a reload is checked like an edit);kick_old_userno longer evicts priority clients. - Classic ser2net imports go through the preview only; merging a file again keeps what was set up here;
ipv6n4accepters listen on IPv4 and IPv6; port locking comes along. - Sign-in: 10 failures in 15 minutes give that user name HTTP 429 until they expire. Accounts get stronger password hashes at their next sign-in, which 2.8.1 and older can't read: after going back to 2.8.1, set a new password there (
ser2net --set-password NAME; on 2.8.0 and older, restore aconfig.jsonfrombackups/made before the upgrade). - New defaults: the event loop watchdog (90 s, then
data/hang.logand exit code 70;SER2NET_LOOP_WATCHDOG=0turns it off); traces pause below 256 MB free; new log and trace files are owner-only;::takes IPv6 only (All interfaces (IPv4 + IPv6) takes both); the first start deletes trace files of mappings that no longer exist. - systemd: copy the new
systemd/ser2net.serviceto/etc/systemd/system/and runsystemctl daemon-reload;systemctl reloadnow works with all three units (ser2net@.serviceandser2net-standalone.serviceare new). Instances ofser2net@.serviceshare onelib/: runsudo -u ser2net python3 /opt/ser2net/bootstrap.pyafter each upgrade. - Scripts: Settings forms answer 303 / 400 (follow redirects with the cookie jar to read a new token), and a save that switches admin TLS on or off answers 200 with the page; a dashboard bulk action that can't change a mapping answers 400 with the reasons, after doing the others; a page without a session redirects to
/login?next=<page>; REST errors are JSON (400 validation,500 save_failed); log times carry a UTC offset and IPv6 peers are bracketed; cookies on ports other than 8080 and over HTTPS have new names. - MQTT / Sparkplug B consumers:
.../set/resultcomes at QoS 1 with new error texts; Sparkplug B sends a DDEATH for a unit that stops answering andbdSeqas Int64. - LAN cluster: mixed versions work, and an older node's edit form offers only the settings it keeps, but upgrade every node (Upgrading a cluster).
- Windows: serial writes are paced to the line; if a virtual COM driver misbehaves,
SER2NET_WINDOWS_SERIAL=pyserialrestores the old write path. - Going back to 2.8.x: copy
data/first; older versions drop named API tokens, two-factor sign-in,dual_stackand the new TLS checks (README → Going back to an older version); put back that version's systemd unit, as 2.9'sExecReloadwould end it, and removenotify.conf.
How to upgrade
- Standalone executable (Linux): replace the file and restart ser2net, or install or upgrade it as a systemd service with
ser2net-install.sh(run it again to upgrade;--uninstallkeeps the data). - Windows service: run the new
ser2net-windows-x64.exe --install-servicefrom an elevated prompt. It upgrades the service in place, keeping its data directory, and now reports an error if the service doesn't start. - Docker:
docker pull ghcr.io/haliskilic/ser2net:2.9.0and recreate the container; addinit: trueandstop_grace_period: 30sto your compose file, as the shipped one has. - Source install: update the files and restart; the first start refreshes
lib/fromvendor/wheelsby itself, offline (all.log says what to run if it can't). Under systemd, copy the new unit first; forser2net@.serviceinstances, runbootstrap.pyas above.
Everything, with the upgrade notes in full: CHANGELOG.md.
Assets
| File | |
|---|---|
ser2net-linux-x64 |
Linux x86_64 executable (glibc ≥ 2.28) |
ser2net-linux-arm64 |
Linux arm64 executable (glibc ≥ 2.28; Raspberry Pi 3/4/5 with a 64-bit OS) |
ser2net-windows-x64.exe |
Windows x64 executable; --install-service runs it as a service |
ser2net-install.sh |
installs a Linux executable as a systemd service, checked against SHA256SUMS |
ser2net-standalone.service |
systemd unit for the Linux executables (data in /var/lib/ser2net) |
*.cdx.json |
CycloneDX SBOM of each executable |
*.THIRD-PARTY-LICENSES.txt |
license texts of the components inside each executable |
SHA256SUMS |
SHA-256 checksums of all of the above |
Docker image: ghcr.io/haliskilic/ser2net:2.9.0 (also 2.9 and latest), linux/amd64, linux/arm64 and linux/arm/v7. 2.9 and latest are rebuilt weekly when the Debian/Python base image changes; 2.9.0 stays the image this release published.
Verify a download: sha256sum -c SHA256SUMS --ignore-missing, and the build provenance with gh attestation verify ser2net-linux-x64 -R haliskilic/ser2net (add --signer-workflow haliskilic/ser2net/.github/workflows/release.yml to require the release workflow; each executable also has an SBOM attestation; the image: gh attestation verify oci://ghcr.io/haliskilic/ser2net:2.9.0 -R haliskilic/ser2net, while a weekly rebuild of 2.9 / latest is attested by image-refresh.yml). The Windows executable is not code-signed yet, so SmartScreen may warn; check it the same way (docs/WINDOWS.md).
v2.8.1
A security release. 2.8.1 fixes 16 security issues in 2.8.0 and earlier, together with the bugs found alongside them. Upgrade every installation, and every node of a LAN cluster. Several fixes change behaviour on purpose: read Before you upgrade first.
Security advisories
| Advisory | Severity | Affected versions | Summary |
|---|---|---|---|
| GHSA-47f5-rwq9-8c9p | High | ≤ 2.8.0 | Unvalidated mapping ids and serial-port URLs allow file writes outside the data directory |
| GHSA-gxrq-f87q-xx96 | High | 2.3.0–2.8.0 | LDAP/OIDC group-to-role mapping matches substrings and can grant unintended roles |
| GHSA-9qqv-mrqj-4fpx | High | ≤ 2.8.0 | Unauthenticated requests can exhaust memory and push all other records out of the audit log |
| GHSA-g2wg-gpfm-g9fp | High | 2.3.0–2.8.0 | The Read-only option is not enforced for Modbus gateway mappings |
| GHSA-jm3r-8jxr-4wh4 | Medium | 2.8.0 | MQTT commands can write beyond the addressed Modbus point and bypass the Read-only option |
| GHSA-p65v-hqq3-cjr9 | High | 2.5.0–2.8.0 | Fleet-view proxies can send the cluster key to a host that is not a cluster node |
| GHSA-5mrw-593x-q839 | High | 2.3.0–2.8.0 | LDAPS and StartTLS connections do not verify the directory server's certificate |
| GHSA-p2cr-p4jh-x4rc | Medium | ≤ 2.8.0 | First-run setup has no host check; sessions survive sign-out, account re-creation and restore |
| GHSA-7f6v-672v-7mjm | Medium | 2.3.0–2.8.0 | LDAP/OIDC sign-ins can take over a same-named local or other-method account; OIDC gaps |
| GHSA-xcxp-4hgp-m666 | Medium | 2.8.0 | Classic ser2net import enables connections without their access protections |
| GHSA-w2p8-66v2-rgr7 | High | 2.5.0–2.8.0 | Cluster key is sent in clear on peer requests and can be redirected by replayed beacons |
| GHSA-wv6x-867g-475q | High | 2.8.0 | Windows service runs as LocalSystem from a location standard users may be able to modify |
| GHSA-cjp3-9g2v-4w69 | Medium | ≤ 2.8.0 | Unauthenticated connections can use up file descriptors or stop the process on Windows |
| GHSA-3wh8-pjmf-qfh5 | Medium | 2.3.0–2.8.0 | Viewers can see data sent to devices, including typed passwords, and download trace files |
| GHSA-jj9j-m939-wmmm | Medium | ≤ 2.8.0 | RFC 2217 clients can make ser2net buffer subnegotiation data without limit |
| GHSA-4wgx-6v57-4r26 | Medium | 2.0–2.8.0 | UDP mappings buffer serial writes without limit and let any allowed source take over output |
2.8.0's note "Traces can no longer write outside the data directory" was incomplete: the mapping id, which is part of the trace and log file names, was not validated. 2.8.1 validates it everywhere it enters (GHSA-47f5-rwq9-8c9p).
Before you upgrade
config.json and data/ are used as they are. Check what applies to you:
- LAN cluster: upgrade every node. Legacy authentication (on by default) keeps older nodes working meanwhile; once all run 2.8.1, turn it off on each node and change the cluster key, which older versions sent in clear text.
- LDAPS / StartTLS now check the directory's certificate. If this system doesn't trust its CA (private, AD CS or self-signed, typical on Linux and in Docker), set Settings → LDAP → CA certificate file (
ldap.ca_file), or LDAP sign-in stops. - LDAP / OIDC group settings match whole group names only; a sign-in under a local account's name is refused, and an LDAP and an OIDC account can no longer share a name (the method used last keeps it). An OIDC sign-in in progress during the upgrade fails once. Locked out?
ser2net --set-password NAMEcreates a local admin. - Viewers no longer see what is sent to devices or download traces; give the operator role to those who need them.
- Read-only Modbus mappings refuse writes from masters and from MQTT / Sparkplug; retained MQTT
/setcommands are no longer replayed (there is an opt-in to keep that). - Backup restore keeps the current accounts, passwords, API token and cluster key unless you choose otherwise.
- First-run setup through a reverse proxy or another host name asks for the code in
data/setup-code.txt. - Request bodies over 2 MB get HTTP 413 (imports: 16 MB).
- One ser2net per data directory: a second instance on the same data directory refuses to start.
- Monitoring:
bytes_in/bytes_outcount at the device now (a one-time level change); some sign-in log lines are worded differently. - Classic ser2net imports done with 2.8.0: re-check those mappings' TLS, allowed client IPs and direction.
- Admin UI connection limits: a connection that sends no complete request within 10 s (or then sends its body slower than about 1 KiB/s, or takes nothing of the response) is closed, and at most 64 connections per address and 256 in all are taken (optional
admin_ui.request_timeout_s,max_connections_per_ip,max_connectionsinconfig.json, 0 = no limit). Behind a reverse proxy everyone shares one address: raise the per-address limit there. - TLS mappings: a handshake must finish within 15 s; hosts not in the allow-list are closed before it; past 32 at once from one address, further connections wait for a slot.
- Windows keeps the selector event loop, which now refuses new connections near its 512-socket limit instead of stopping;
SER2NET_WINDOWS_LOOP=proactoris an opt-in alternative without that limit (not yet soak-tested with serial devices).
How to upgrade
- Standalone executable (Linux): replace the file and restart ser2net.
- Windows service: run the new
ser2net-windows-x64.exe --install-servicefrom an elevated prompt. It copies itself toC:\Program Files\ser2net\and upgrades the service in place, keeping its data directory. - Docker:
docker pull ghcr.io/haliskilic/ser2net:2.8.1and recreate the container; afterwards checkdocker logsforWARNING: config.json:lines (settings the container now leaves off instead of restart-looping). - Source install: update the files, then, with ser2net stopped, refresh the bundled packages in the install folder:
python3 -m pip install --target lib --upgrade --no-index --find-links vendor/wheels -r requirements.txt(an in-place update keeps the old ones inlib/).
Also in this release
- A serial device that stops taking data (CTS low, XOFF, a virtual port nobody reads) no longer freezes ser2net or hangs a mapping's stop.
- The Modbus gateway reopens a lost serial port instead of answering 0x0B until restarted.
- The standalone executables show the Turkish UI again and keep their web UI when temp cleaners run.
- A rejected setting no longer restart-loops Docker or blocks every save; bind-mounted
/datafolders from older images and serial devices on hosts wheredialoutisn't GID 20 work. - Fleet view: remote actions work for peers behind NAT or Docker, by host name or IPv6; non-ASCII cluster keys work.
- Active Directory direct bind with
user@domainorDOMAIN\userreads groups; OIDC tolerates small clock differences. - RFC 2217 clients survive malformed values and a reopened serial port; serial ports and MQTT keep working with more than 1024 files open, and the open-file limit is raised at start.
- Account recovery from the command line (
--set-password,--sign-out-all), session controls, cluster key generation and planned key changes, a fixed UDP reply address. - Dependencies: click 8.5.1, paho-mqtt 2.2.0, authlib 1.9.5.
Everything, with the upgrade notes in full: CHANGELOG.md.
Assets
| File | |
|---|---|
ser2net-linux-x64 |
Linux x86_64 executable (glibc ≥ 2.28) |
ser2net-linux-arm64 |
Linux arm64 executable (glibc ≥ 2.28; Raspberry Pi 3/4/5 with a 64-bit OS) |
ser2net-windows-x64.exe |
Windows x64 executable; --install-service runs it as a service |
*.cdx.json |
CycloneDX SBOM of each executable |
*.THIRD-PARTY-LICENSES.txt |
license texts of the components inside each executable |
SHA256SUMS |
SHA-256 checksums of all of the above |
Docker image: ghcr.io/haliskilic/ser2net:2.8.1 (also 2.8 and latest), linux/amd64 and linux/arm64.
Verify a download: sha256sum -c SHA256SUMS --ignore-missing, and the build provenance with gh attestation verify ser2net-linux-x64 -R haliskilic/ser2net (each executable also has an SBOM attestation; the image: gh attestation verify oci://ghcr.io/haliskilic/ser2net:2.8.1 -R haliskilic/ser2net). The Windows executable is not code-signed yet, so SmartScreen may warn; check it the same way (docs/WINDOWS.md).
v2.8.0
ser2net v2.8.0 — Turkish web UI, Linux arm64, Windows service, Docker image, notifications, Modbus write + Sparkplug B, classic ser2net import (#28–#32)
Upgrading from 2.7.x
Nothing to migrate: replace the program (or pull the new image) and restart. config.json and data/ are used as they are.
- Backups: the first configuration change keeps the old
config.jsonindata/backups/. - Trace paths: a trace path in an old config becomes a file name under
data/traces/, and the start-up log names the move. This is a security fix (below). - Docker: the image runs as an unprivileged user (uid 10001). A
/datavolume written by the old root image is taken over on its first start. The image is now on GHCR:ghcr.io/haliskilic/ser2net:2.8.0(also2.8andlatest), for amd64 + arm64.docker-compose.ymluses it. - Web UI language: English or Turkish, by the browser's language, unless a visitor picks TR / EN or the admin sets Settings → Web UI access → Default language.
- Import: importing now shows a preview first. Scripts that post the 2.7-style one-step import still replace every mapping, as before.
Security
- Updating is recommended: v2.7.1 ships dependency versions with known vulnerabilities. They are fixed here:
- starlette: GHSA-82w8-qh3p-5jfq (high), form size limits ignored for URL-encoded bodies, which affects the sign-in form; GHSA-jp82-jpqv-5vv3 (low).
- anyio: GHSA-82r6-8w77-94w6 and GHSA-5p39-cfhj-2xmp.
- pyasn1 0.6.5 (ldap3), bundled in the executables.
- Traces can no longer write outside the data directory. A mapping's trace was a free file path, so an operator (not only an admin) could have network bytes appended to any file the service may write:
config.json, a startup script, and as root in Docker. Traces now always go to<data>/traces/<mapping id>-<file name>. - The Docker image no longer runs as root.
- Audit records can't be forged with control characters in a user-supplied name.
Added
-
Turkish web UI next to English. Logs, the audit log and the REST API stay English; notifications use the admin's default language.
-
USB identity: a mapping can find its device by VID:PID + serial number, so re-plugging an adapter as another
ttyUSB*/COM*keeps it working.- The port list shows
/dev/serial/by-idnames, and they can be selected. - The kernel's placeholder
ttySports without hardware are hidden.
- The port list shows
-
Notifications by webhook (JSON, Slack/Mattermost, Microsoft Teams, Discord, Telegram) and/or e-mail (SMTP) when:
- a serial device is lost or comes back;
- a mapping fails to start;
- clients connect or disconnect;
- a sign-in fails.
Repeats are suppressed and the rate is limited; Settings → Notifications can send a test message.
-
Dashboard: search, state filter, sort, and start / stop / restart of the selected mappings. The table is redrawn only when it changed, so it no longer flickers or loses the selection.
-
Traces in the UI: download a mapping's trace files (current + rotated), and a live hex view in the console with both directions and timestamps.
-
Modbus write from MQTT: points with
"writable": truetake values from<topic>/<point>/set.- Coils use FC5/FC15, holding registers FC6/FC16. The result goes to
…/set/result. - Coils and discrete inputs (FC1/FC2) can be polled too.
- Coils use FC5/FC15, holding registers FC6/FC16. The result goes to
-
Sparkplug B: a Modbus-polling mapping can be an edge node whose devices are the Modbus units it polls (
unit1,unit2, …).- NBIRTH/NDEATH carry
bdSeq; DBIRTH/DDATA carryseq. - Points are written through DCMD; NCMD triggers a rebirth.
- No protobuf dependency.
- NBIRTH/NDEATH carry
-
Import from the classic ser2net:
ser2net.yaml(4.x) andser2net.conf(3.x) go through the import preview. Options without an equivalent are listed as warnings.- ser2net opens a device only while a client is connected, but a mapping here keeps it open. So of several ports on one device, the first comes in enabled and the others disabled.
-
Configuration backups (newest 50, download / restore; a restore can be undone), an import preview (merge or replace) and an audit log viewer (search, filters, CSV) in Settings.
-
Linux arm64 executable (
ser2net-linux-arm64, glibc ≥ 2.28): Raspberry Pi 3/4/5 with a 64-bit OS, ARM gateways. -
Windows service with no extra software:
ser2net.exe --install-service/--uninstall-service.- It starts at boot and restarts after a failure;
--service-nameallows several instances.
- It starts at boot and restarts after a failure;
-
Release integrity:
SHA256SUMS, a CycloneDX SBOM per executable, and GitHub build-provenance and SBOM attestations. -
--about: component versions for bug reports. -
--licenses: the license texts of every bundled component. -
--healthcheck: used by the Docker HEALTHCHECK. -
SER2NET_LIB_OVERRIDE: loads a replacement ldap3 (LGPL).
Fixed
- The session lifetime setting now takes effect: sessions always lasted 8 hours before. It is under Settings → Web UI access.
- Writing a trace no longer blocks the event loop, and trace files rotate at a size limit (default 10 MB, 3 old files kept).
- The Docker HEALTHCHECK no longer reports a fresh container unhealthy while it waits for the first-run setup.
Changed
- Dependencies updated: starlette 1.10.0, uvicorn 0.54.0, websockets 17.4, python-multipart 0.0.33, anyio 4.16.0.
- Python 3.10 keeps the last releases that support it.
- The offline wheels in
vendor/wheels/match.
- CI adds mypy, pip-audit, coverage and Dependabot, and
mainis protected. - New repository files:
SECURITY.md,CONTRIBUTING.md,CHANGELOG.md.
Tested before publishing
- Every PR runs the test suite on Linux and Windows × Python 3.10–3.13.
- The executables are built and tested before they are attached. That includes the arm64 one, in 9 clean distributions each, and a real Windows service install / restart-after-crash / stop / uninstall.
- The dashboard, the mapping views and the settings page are checked in a browser at 1440 → 390 px, in English and Turkish.
- The Docker image is smoke-tested on amd64 and arm64, including the upgrade from a v2.7.1 volume.
Verify a download
sha256sum -c SHA256SUMS --ignore-missing
gh attestation verify ser2net-linux-x64 --repo haliskilic/ser2net
Türkçe özet
- Türkçe arayüz: üst menüde TR / EN; seçilmemişse tarayıcının dili ya da admin'in varsayılan dili. Log'lar, audit log ve REST API İngilizce kalıyor.
- Güncellemen önerilir: v2.7.1'deki starlette ve anyio sürümlerinde bilinen güvenlik açıkları var, bu sürümde kapandı.
- Trace güvenlik düzeltmesi: trace dosyaları artık yalnızca
data/traces/altına yazılabiliyor. Eskiden bir operator herhangi bir dosyaya yazdırabiliyordu. - Yeni özellikler:
- USB kimliğiyle cihaz bulma;
- webhook ve e-posta bildirimleri;
- panoda arama, filtre ve toplu işlem;
- trace indirme ve canlı hex görünümü;
- MQTT'den Modbus yazma;
- Sparkplug B (her Modbus unit'i bir cihaz);
- klasik
ser2net.yaml/ser2net.confiçe aktarma; - config yedekleri ve geri yükleme;
- içe aktarma önizlemesi;
- audit log ekranı.
- Dağıtım:
- Linux arm64 binary'si;
- ek program gerektirmeyen Windows servisi;
- GHCR'de amd64 + arm64 Docker imajı (root olmayan kullanıcıyla);
SHA256SUMS, SBOM ve build provenance.
- Yükseltme: v2.7.x'ten geçişte bir şey yapmana gerek yok.
config.jsonvedata/olduğu gibi kullanılıyor; ilk değişiklikte eski configdata/backups/içine yedekleniyor.
Downloads: ser2net-windows-x64.exe (Windows 10/11, Server 2016+) · ser2net-linux-x64 (x86_64, glibc ≥ 2.28) · ser2net-linux-arm64 (aarch64, glibc ≥ 2.28). They are standalone: nothing else to install. Docker: docker pull ghcr.io/haliskilic/ser2net:2.8.0.
v2.7.1
ser2net v2.7.1 — long names in the web UI, truly standalone executables (#26)
Fixed
- Long names no longer push the row buttons out of the web panel. Long mapping names,
/dev/serial/by-id/…paths, remote host names and error messages used to make the mappings and cluster tables wider than their panel: the Start/Stop/…/Delete buttons sat outside it and the page scrolled sideways.- Long text now wraps at separators (
/ _ . : -). - The row buttons wrap inside their cell in two groups, and stay on one line when there is room.
- Errors are clamped to 3 lines, with the full text in the tooltip.
- Below 940 px the lists become cards.
- The top bar, the log and console headers and the forms also handle long names and phone screens.
- Long text now wraps at separators (
- The Linux executable runs on glibc ≥ 2.28. The v2.7.0 binary needed glibc 2.38 and did not start on Ubuntu 22.04/20.04, Debian 12/11 or RHEL/Alma/Rocky 8–9.
- It is now built in an AlmaLinux 8 container with a python-build-standalone CPython.
- It runs on RHEL/AlmaLinux/Rocky 8+, Debian 11+, Ubuntu 20.04+, Fedora and openSUSE Leap 15 (x86_64; not musl/Alpine — use Docker there).
- It is also smaller: ~24 MB instead of ~34 MB.
- "Generate self-signed certificate" no longer needs the
opensslprogram; before, it never worked on Windows. The certificate is now made in-process with the bundledcryptography, with SAN localhost / 127.0.0.1 / ::1 / host name. - The Linux executable finds the distribution's CA certificates (Debian/Ubuntu, RHEL/Fedora, openSUSE and Alpine layouts) for certificate checks in MQTT over TLS and OIDC sign-in. Set
SSL_CERT_FILEto override; the startup log names the bundle in use. - Windows hardening:
icacls(data-folder permissions) is run by its full System32 path, so anicacls.exeplanted next to the exe can't run instead.
Tested before publishing
From this release on, the executables are only attached after these checks pass on every PR and release:
- the bundle needs nothing but OS-base libraries (glibc ≤ 2.28 / Windows system DLLs), and the VC++ runtime and Python are inside;
- they run with an empty
PATH(no Python, noopenssl): web UI, TCP mapping, MQTT/LDAP/OIDC libraries, certificate generation, OIDC over certificate-checked HTTPS; - every library the running process loads comes from the bundle or the OS;
- the Linux binary runs in clean AlmaLinux 8/9, Debian 11/12, Ubuntu 20.04/22.04/24.04, openSUSE Leap 15.6 and Fedora containers.
A browser test (Chromium, 1440 → 390 px) also guards the long-name layout.
Upgrading from 2.7.0
Nothing to migrate: replace the program and restart. config.json and data/ stay as they are. Coming from 2.6.x, see the v2.7.0 notes as well.
- Linux binary: it unpacks itself to
$TMPDIR(default/tmp) at start. If/tmpis mountednoexec, pointTMPDIRat a directory that isn't. - Web UI: the mappings and cluster tables now have 7 columns: serial parameters under the port, mode/type under the address. Browsers fetch the new CSS by themselves (versioned asset URLs).
- Log: the Linux binary logs
TLS: using the system CA bundle …at start, or a warning when it finds none.
Türkçe özet
- Web panelinde uzun eşleme adları,
/dev/serial/by-id/…yolları ve hata mesajları artık butonları panelin dışına taşırmıyor; dar ekranlarda liste kart görünümüne geçiyor. - Linux binary'si artık glibc 2.28 ve üzeri her x86_64 dağıtımda çalışıyor; önceden glibc 2.38 istiyordu.
- "Self-signed sertifika üret" harici
opensslgerektirmiyor. - Linux binary'si dağıtımın CA sertifikalarını kendisi buluyor.
- Binary'ler her PR'da ve her sürümde test ediliyor: boş PATH ile çalıştırma, yüklenen kütüphanelerin kaynağı ve 9 temiz dağıtım konteyneri. Yayın ancak bunlar geçince yapılıyor.
Downloads: ser2net-windows-x64.exe (Windows 10/11, Server 2016+) · ser2net-linux-x64 (x86_64, glibc ≥ 2.28) — standalone, nothing else to install.
v2.7.0
ser2net v2.7.0 — connection lifecycle & optional login (#24)
Fixed
- Dead TCP clients no longer stay
ESTABLISHEDforever. A client that vanished without closing (power cut, cable pull, Wi-Fi/VPN/NAT drop) kept its slot on a quiet raw bridge — and withmax_connections = 1every reconnect was refused withBusy. Every TCP data socket (server, connect-out, Modbus gateway) now uses TCP keepalive, on by default: a dead peer is dropped in ≈ 90 s (60 s idle + 3 × 10 s probes; configurable per mapping), also while the device keeps sending (LinuxTCP_USER_TIMEOUT, when shorter than the kernel's ~15 min); pre-1709 Windows (e.g. Server 2016) is supported viaSIO_KEEPALIVE_VALS. Verified with real half-open connections on Linux and Windows in CI. - Viewers can no longer open the mapping edit form or the mappings export, which expose stored MQTT credentials (now operator+); a viewer API token gets the MQTT password redacted.
- 2.6.x configs with an unvalidated
idle_timeout_s(-1,"300") keep booting (Docker/headless re-saves the config on start); such values are normalised.
New
- Connect timeout for connect-out mappings (default 10 s, 0 = OS default) — covers the TCP and TLS handshake.
- Max connection time per mapping (0 = unlimited, e.g. 86400 = 24 h).
- Idle timeout now also applies to the Modbus gateway.
- Every disconnect is logged with its reason (closed by peer, peer stopped responding, idle timeout, max connection time, kicked, …).
- Optional web login: Settings → Web UI access — switch "Require login" off on trusted networks and choose the role visitors get (viewer / operator / admin). Signing in still works, the REST API still needs its token, open access applies only when the UI is reached by IP /
localhost/ its own host name (DNS-rebinding guard; behind a reverse proxy passX-Forwarded-Host), other cluster nodes still need a real login, and open consoles close when access is revoked. Login stays required by default. - All of the above in the mapping form, REST API and JSON import.
Upgrading from 2.6.x
No config migration and no new dependencies: config.json (mappings, users, API token, cluster, TLS) is read as-is; new settings take their defaults. Back up the data/ folder, replace the program, restart:
- Standalone binary: stop it/the service, replace
ser2net(.exe), keepdata/next to it, start. - Source install / systemd:
git pull(or unpack the new source) over the old one, thensystemctl restart ser2net. - Docker:
git pull && docker compose up -d --build— the/datavolume is kept. - LAN cluster: upgrade every node; mixed versions keep working meanwhile.
Behaviour changes to be aware of:
- TCP keepalive is on for every TCP mapping (that is the fix): a vanished peer is dropped after ≈ 90 s. To restore the old behaviour for a mapping, untick TCP keepalive (or
"tcp_keepalive": false); for faster detection lower idle/interval/count. - Connect-out gives up a hanging attempt after 10 s and retries (was the OS default: ~21 s Windows / ~2 min Linux). For very slow links raise Connect timeout, or set 0.
- Modbus gateway now honours Idle timeout — it was ignored there before; if a Modbus mapping has one set, masters that stay silent longer get disconnected.
- Linux: a client that stops reading for longer than idle + interval × count while the device keeps sending is disconnected (
TCP_USER_TIMEOUT). - Viewer role: no longer opens the mapping form or the export (403); a viewer API token sees the MQTT password as
********. - Log format: disconnect lines now end with
— <reason>, and the startup line shows the keepalive setting — adjust any log parsers. - Login is still required by default; open access only if an admin turns it on.
- Rolling back to 2.6.x is safe: it ignores the new keys (and drops them on its next save).
Türkçe özet
FIN/RST göndermeden kaybolan istemcilerin bağlantısı artık sonsuza dek ESTABLISHED kalmıyor: tüm TCP soketlerinde TCP keepalive varsayılan olarak açık (~90 sn'de temizlenir). Bunun yanında connect timeout, maksimum bağlantı süresi, Modbus gateway için idle timeout, kopma nedeni loglama ve isteğe bağlı web girişi (Ayarlar → Web UI access) geldi. Ayrıca viewer rolü artık MQTT parolası içeren düzenleme formunu ve dışa aktarmayı göremiyor.
Downloads: ser2net-windows-x64.exe · ser2net-linux-x64 (standalone, no Python needed).
v2.6.3
v2.6.3: RS-485 hardware auto-RTS in the mapping UI