Skip to content

Releases: luadch-ng/luadch

v3.1.14

Choose a tag to compare

@github-actions github-actions released this 13 Jul 10:06
f09ab8e

Luadch v3.1.14

Maintenance patch on the release/3.1.x line. One fix: a Windows-only hub-crash once the hub holds ~64 concurrent sockets. No breaking changes; no cfg / lang-file changes; drop-in upgrade from v3.1.13. Linux operators are unaffected (no functional change).

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories before any upgrade, on principle. This release has no required cfg or lang-file changes - the upgrade is a pure binary / script tree swap, but the backup discipline is worth keeping.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Why upgrade

Windows operators should upgrade, especially busier hubs. Before this fix, a Windows hub crashed with bad argument #1 to 'socket_select' (too many sockets) and dropped every user once it held about 64 concurrent sockets - a ceiling a moderately busy hub reaches organically (it can also be pushed there by a distributed connect flood; a single IP stays capped by ratelimit_perip_max_conns). Reported on Windows Server 2008 R2 running 3.1.13, but it affects any Windows version.

Linux is unaffected - its glibc select ceiling is already 1024, so this release makes no functional change on Linux beyond one informational boot-log line.

Bugfixes

#416 (Sopor) - Windows hub-crash at ~64 concurrent sockets

The symptom in the log, immediately followed by the hub dropping every user:

/core/server.lua:...: bad argument #1 to 'socket_select' (too many sockets)

The hub event loop (core/server.lua's tick()) calls socket.select over every connected socket at once. On Windows, luasocket's select.c hard-caps that at FD_SETSIZE:

#ifdef _WIN32
    if (n >= FD_SETSIZE)
        luaL_argerror(L, tab, "too many sockets");

The bundled Windows build never defined FD_SETSIZE, so it inherited the Winsock default of 64 (luasocket/CMakeLists.txt set only WINVER). Once the hub held ~64 sockets (logged-in users + v4/v6 listeners + HBRI + HTTP), the unguarded select raised and the main loop died.

Fix: define FD_SETSIZE=1024 for the Windows luasocket build (socket module + luasocket_static) - parity with the Linux glibc fd_set. On Windows fd_set is a SOCKET array sized by this macro, so raising it genuinely enlarges the set. This is Windows-only: on Linux, glibc's fd_set is a fixed 1024-bit buffer that redefining FD_SETSIZE would overflow.

Watching more than ~1024 concurrent sockets needs replacing select() with poll() on both platforms, tracked in #310 on the 3.2.x line. max_users (default 3000) counts logged-in users, not sockets, and is not reached on either platform before this ~1024 socket ceiling.

The hub now logs its compile-time select capacity (socket._SETSIZE) once at boot (hub.loop, to event.log) so you can confirm the raised cap. The identical fix is on the 3.2.x line (master, PR #417) with a smoke regression that provably fails pre-fix on the Windows CI leg.

Build / runtime

No toolchain changes. Same Lua 5.4.8, same LuaSec 1.3.2, same LuaSocket 3.1.0, same build toolchain as v3.1.13. The Windows build simply compiles luasocket with -DFD_SETSIZE=1024.

The linux-aarch64 artifact continues with the Bullseye-container pipeline (glibc 2.31 baseline, works on Pi OS Bullseye / Bookworm / DietPi v9.x).

Upgrade

# Windows (the platform this fix targets)
# Download luadch-v3.1.14-windows-x86_64.zip, extract, copy cfg+data over, restart.

# Linux x86_64 / aarch64 (no functional change; upgrade at leisure)
wget https://github.com/luadch-ng/luadch/releases/download/v3.1.14/luadch-v3.1.14-linux-x86_64.tar.gz
tar xzf luadch-v3.1.14-linux-x86_64.tar.gz
# move your cfg/, scripts/data/, etc into the new tree, restart hub

3.2.x is the active development line on master; security backports continue to land on release/3.1.x per CLAUDE.md §8.

v3.1.13

Choose a tag to compare

@github-actions github-actions released this 11 Jul 19:44
c6389c1

Luadch v3.1.13

Security maintenance patch on the release/3.1.x line. One fix: a remote, unauthenticated hub-crash. No breaking changes; no cfg / lang-file changes; drop-in upgrade from v3.1.12.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories before any upgrade, on principle. This release has no required cfg or lang-file changes - the upgrade is a pure binary / script tree swap, but the backup discipline is worth keeping.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Why upgrade

Every operator should upgrade. This is a remote, unauthenticated denial of service that takes the whole hub down - all users dropped, no reconnections possible until you manually restart the process. It is trivially triggerable (a single TCP connect followed by an immediate reset) and reproducible against any hub regardless of configuration.

Bugfixes

#401 (Sopor) - remote hub-crash on a peer reset right after accept

The symptom in the error log, immediately followed by the hub dropping every user:

././core/ratelimit.lua:176: attempt to concatenate a nil value (local 'ip')
*** TLS error: Connection closed

core/server.lua's accept path reads the connecting client's IP via client:getpeername() and hands it to the per-IP rate-limit guard. When a peer resets the TCP connection in the split second between accept() and getpeername() (connect + immediate RST - anyone can do it), getpeername() returns nil. That nil reached ratelimit.accept_ip, which builds its token-bucket key as "ip:" .. ip - raising attempt to concatenate a nil value (local 'ip'). The error was uncaught inside the listener's accept loop, so the hub stopped accepting connections and dropped everyone online.

ratelimit.release_ip already guarded a nil IP; accept_ip did not - the asymmetry that made this reachable.

Fix (defence in depth):

-- core/server.lua, accept path
 local clientip, clientport = client:getpeername( )
+if not clientip then
+    -- peer reset between accept() and getpeername(); socket is dead,
+    -- and we cannot rate-limit an unknown IP. Drop it before the guard.
+    client:close( )
+    return false
+end
-- core/ratelimit.lua, accept_ip
 if not _activate then return true end
+if not ip then return true end   -- mirror release_ip's nil guard

core/server.lua now closes a just-accepted socket whose peer address cannot be read before the rate-limit guard runs, and accept_ip guards nil directly.

The 3.2.x line (master) additionally guards core/blocklist.lua, which does not exist on 3.1.x, and carries a regression unit test (tests/unit/ratelimit_test.lua) that reproduces the exact crash and provably fails pre-fix. The 3.1.x fix is the same server + ratelimit guard, reviewer-verified, and validated by running that same test against this line's ratelimit.lua.

Build / runtime

No changes. Same Lua 5.4, same LuaSec 1.3.2, same LuaSocket 3.1.0, same build toolchain as v3.1.12.

The linux-aarch64 artifact continues with the Bullseye-container pipeline (glibc 2.31 baseline, works on Pi OS Bullseye / Bookworm / DietPi v9.x).

Upgrade

# Linux x86_64 / aarch64
wget https://github.com/luadch-ng/luadch/releases/download/v3.1.13/luadch-v3.1.13-linux-x86_64.tar.gz
tar xzf luadch-v3.1.13-linux-x86_64.tar.gz
# move your cfg/, scripts/data/, etc into the new tree, restart hub

# Windows
# Download luadch-v3.1.13-windows-x86_64.zip, extract, copy cfg+data over, restart.

3.2.x is the active development line on master; security backports continue to land on release/3.1.x per CLAUDE.md §8.

v3.1.12

Choose a tag to compare

@github-actions github-actions released this 10 Jul 13:17
e335ae4

Luadch v3.1.12

Maintenance patch release on the release/3.1.x line. One bugfix for a backport slip introduced in v3.1.10. No breaking changes; no cfg / lang-file changes; drop-in upgrade from v3.1.11.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories before any upgrade, on principle. This release has no required cfg or lang-file changes - the upgrade is a pure binary / script tree swap, but the backup discipline is worth keeping.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Why upgrade

Only deployments that explicitly set kill_wrong_ips = false are affected (the 3.1.x default is true). Operators run that opt-out specifically to admit NAT / CGNAT users whose client advertises a different IP than the one they connect from - and this bug defeats exactly that opt-out: instead of stamping the real IP and letting the user in, the hub raised a Lua error in incoming and the affected user could not complete login. If you set kill_wrong_ips = false (or plan to), upgrade. If you run the default, you are not affected and can upgrade at leisure.

Bugfixes

#393 (Sopor) - kill_wrong_ips = false path errored with attempt to read undeclared var: 'userfam'

The symptom, once an operator set kill_wrong_ips = false and a mismatched-IP user connected:

hub.lua: function 'incoming': lua error: ././core/hub_dispatch.lua:356: attempt to read undeclared var: 'userfam'

The #214 Gap 2 fix (shipped to 3.1.x in v3.1.10) stamps the authenticated TCP-source IP over a mismatched INF claim so the wrong IP is never broadcast. The v3.1.10 cherry-pick wrote that stamp as adccmd:setnp( userfam, userip ) - but userfam is the master branch's variable name. The 3.1.x incoming function calls its address-family variable ipver (declared at the top of the function, used correctly by the first setnp on the no-IP path ~15 lines above). Under luadch's restricted core environment, reading the undeclared userfam raises an error - so for every user whose claimed INF IP differed from their real TCP-source IP while kill_wrong_ips = false was set, the stamp branch threw and the user (typically the very NAT/CGNAT client the opt-out exists to admit) could not log in.

Fix: use the in-scope ipver at that call site, matching the sibling setnp:

-               adccmd:setnp( userfam, userip )
+               adccmd:setnp( ipver, userip )

Default kill_wrong_ips = true deployments are unaffected - they take the kill branch and never reach the stamp branch. Master is unaffected; it uses userfam consistently throughout its renamed incoming. This was a partial-rename slip in the v3.1.10 backport only, so the fix is applied directly on release/3.1.x (nothing to cherry-pick from master).

The kill_wrong_ips = false stamp path had no smoke coverage, which is how the slip shipped in v3.1.10; a behavioural regression test needs a kill_wrong_ips = false hub config and is tracked as a follow-up.

Build / runtime

No changes. Same Lua 5.4, same LuaSec 1.3.2, same LuaSocket 3.1.0, same build toolchain as v3.1.11.

The linux-aarch64 artifact continues with the Bullseye-container pipeline (glibc 2.31 baseline, works on Pi OS Bullseye / Bookworm / DietPi v9.x).

Upgrade

# Linux x86_64 / aarch64
wget https://github.com/luadch-ng/luadch/releases/download/v3.1.12/luadch-v3.1.12-linux-x86_64.tar.gz
tar xzf luadch-v3.1.12-linux-x86_64.tar.gz
# move your cfg/, scripts/data/, etc into the new tree, restart hub

# Windows
# Download luadch-v3.1.12-windows-x86_64.zip, extract, copy cfg+data over, restart.

3.2.x is the active development line on master; security backports continue to land on release/3.1.x per CLAUDE.md §8.

v3.1.11

Choose a tag to compare

@github-actions github-actions released this 07 Jun 19:10
8514c14

Luadch v3.1.11

Maintenance patch release on the release/3.1.x line. One privilege-escalation bugfix cherry-picked from master. No breaking changes; no cfg / lang-file changes; drop-in upgrade from v3.1.10.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories before any upgrade, on principle. This release has no required cfg or lang-file changes - the upgrade is a pure binary / script tree swap, but the backup discipline is worth keeping.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Why upgrade

If your hub has more than one operator-level account and only one hubowner, the bug fixed here is exploitable: any account with +ban permission (typically op-level and above) could ban the hubowner while the hubowner was offline, locking them out on the next login attempt. With a single hubowner this becomes denial-of-administration on the hub - the hubowner cannot remove their own ban because they cannot log in. With multiple hubowner-level accounts the impact is reduced to a removable nuisance.

Default-config hubs are affected. The fix applies to every operator from level 60 upward (default cmd_ban_permission ladder).

Bugfixes

#320 (Kcchouette) - cmd_ban offline-by-nick path silently bypassed the hierarchy check

scripts/cmd_ban.lua had two divergent target-resolution paths converging on addban():

  • Online branch resolves target to a user OBJECT (has :level() method), falls through to the existing check at line 594: if permission[level] < target:level() then ... msg_god
  • Offline-by-nick branch resolves target to a profile TABLE from regnicks[] (has .level field, no :level() method), then returns at addban() two lines BEFORE the check could run

Effect: a level-60 operator could send +ban nick hubowner_offline 60 reason, the offline branch ran addban() directly without checking that the operator outranked the target, and the hubowner was banned. On their next login attempt the ban hit, kicking them with ISTA 232 ... TL<reconnect-seconds>.

Fix: explicit hierarchy guard inside the offline-by-nick branch using target.level, mirroring the online-path semantics with the table-shape accessor:

elseif by == "nick" then
    local _, regnicks, _ = hub.getregusers()
    target = regnicks[ id ]
    if not target then
        user:reply( msg_off, hub.getbot() )
        return PROCESSED
    end
    if permission[ level ] < ( target.level or 0 ) then    -- new
        user:reply( msg_god, hub.getbot() )
        return PROCESSED
    end
end

cid / ip offline branches keep no profile lookup and stay unchecked by design (no hierarchy info available - the hub doesn't know whose IP a given IP is).

Plugin v0.36 → v0.37. Cherry-picked from master (PR #322, commit 30c8809); the new HTTP-API-based regression smoke test stays master-only because the HTTP API ships on 3.2.x only - the fix logic is identical and reviewer-verified.

A pattern sweep over the related command-and-profile plugins (cmd_gag, cmd_setpass, cmd_upgrade, cmd_delreg, cmd_nickchange, cmd_redirect, cmd_disconnect, etc_trafficmanager, etc_msgmanager) for the same online-vs-offline-hierarchy-check divergence came back clean - either properly guarded on both paths or online-only by design. cmd_ban was the only affected plugin in the family.

Build / runtime

No changes. Same Lua 5.4.7, same LuaSec 1.3.2, same LuaSocket 3.1.0, same build toolchain as v3.1.10.

The linux-aarch64 artifact continues with the Bullseye-container pipeline (glibc 2.31 baseline, works on Pi OS Bullseye / Bookworm / DietPi v9.x).

Upgrade

# Linux x86_64 / aarch64
wget https://github.com/luadch-ng/luadch/releases/download/v3.1.11/luadch-v3.1.11-linux-x86_64.tar.gz
tar xzf luadch-v3.1.11-linux-x86_64.tar.gz
# move your cfg/, scripts/data/, etc into the new tree, restart hub

# Windows
# Download luadch-v3.1.11-windows-x86_64.zip, extract, copy cfg+data over, restart.

3.2.x is the active development line on master; security backports continue to land on release/3.1.x per CLAUDE.md §8.

v3.1.10

Choose a tag to compare

@github-actions github-actions released this 23 May 20:54

Luadch v3.1.10

Maintenance patch release on the release/3.1.x line. Two security / UX bugfixes cherry-picked from master. No breaking changes; no cfg / lang-file changes; drop-in upgrade from v3.1.9.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories before any upgrade, on principle. This release has no required cfg or lang-file changes - the upgrade is a pure binary / script tree swap, but the backup discipline is worth keeping.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Why upgrade

If operators have set kill_wrong_ips = false (NAT-weird deployments) - their hub was forwarding unverified primary-IP claims to other clients, opening the historical DC++ DDoS-amplification vector. This release closes that path.

If operators see FAILED-AUTH spam with reason "User sent offending flag in INF: I4" (HadesDCH reported this) - legitimate DC++ clients refreshing INF after NAT events were being killed in a loop. This release stops the kill and silent-strips the field instead.

Default-config hubs (kill_wrong_ips = true, no INF-refresh storms) are not actively affected by either bug, but the upgrade is harmless and recommended.

Bugfixes

#214 Gap 2 - DDoS-amplification on kill_wrong_ips = false opt-out

core/hub_dispatch.lua elseif infip_match ~= userip branch: when kill_wrong_ips = false AND the BINF claim doesn't match the TCP-source IP, the wrong claim STAYED in adccmd and was broadcast to other clients - they would then direct CTM / RCM frames at the spoofed address (Maksis-confirmed DC++ DDoS-amp vector).

Fix: the opt-out path now stamps the authenticated userip over the lie via adccmd:setnp(userfam, userip). Opt-out intent (don't kill the user) preserved. Default kill_wrong_ips = true deployments unaffected. Side benefit: legitimate NAT-deployments now broadcast a routable IP, so other clients' CTMs succeed (pre-fix they targeted the wrong IP and failed).

The companion Gap 1 (secondary-family unverified broadcast) is the upcoming HBRI implementation's responsibility - tracked on master.

#222 (HadesDCH) - post-login INF with I4 / I6 killed legitimate users

scripts/hub_inf_manager.lua forbidden.flags_on_inf contained I4 / I6 (originally added in #97 to prevent post-login IP-spoofing). Real DC++ clients refresh INF including I4 on routine triggers (NAT rebind, ISP-IP change, plain refresh); pre-fix those legitimate refreshes triggered ISTA 240 + TL300 reconnect-block, producing FAILED-AUTH log spam and bouncing users in a loop.

Fix: flags_on_inf split into _kill (PD / ID - identity spoofing, real attack signal = kill) and _strip (I4 / I6 - IP mutation attempt OR routine refresh = silent-strip). The strip path removes I4 / I6 from cmd via cmd:deletenp() before applying remaining fields. Anti-spoofing intent of #97 preserved: stored _inf IP is never mutated, broadcast doesn't carry the new claim. Other INF fields in the same update (DE, SS, etc.) still get applied normally. Plugin v0.06 → v0.07.

Build / runtime

No changes. Same Lua 5.4.7, same LuaSec 1.3.2, same LuaSocket 3.1.0, same build toolchain as v3.1.9.

The linux-aarch64 artifact is built with the Bullseye-container pipeline introduced as the v3.1.9 in-place asset swap (glibc 2.31 baseline, works on Pi OS Bullseye / Bookworm / DietPi v9.x).

Upgrade

# Linux x86_64 / aarch64
wget https://github.com/luadch-ng/luadch/releases/download/v3.1.10/luadch-v3.1.10-linux-x86_64.tar.gz
tar xzf luadch-v3.1.10-linux-x86_64.tar.gz
# move your cfg/, scripts/data/, etc into the new tree, restart hub

# Windows
# Download luadch-v3.1.10-windows-x86_64.zip, extract, copy cfg+data over, restart.

3.2.x is the active development line on master; security backports continue to land on release/3.1.x per CLAUDE.md §8.

v3.1.9

Choose a tag to compare

@github-actions github-actions released this 13 May 15:46

Luadch v3.1.9

Maintenance patch release on the release/3.1.x line. Three bug fixes (two restoring spec-compliant hublist visibility, one defense-in-depth) plus a new pre-compiled linux-aarch64 release artifact for Raspberry Pi. No breaking changes; no cfg / lang-file changes; drop-in upgrade from v3.1.8.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories before any upgrade, on principle. This release has no required cfg or lang-file changes - the upgrade is a pure binary / script tree swap, but the backup discipline is worth keeping.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Why upgrade

Public hubs on v3.1.8 are effectively invisible to ADC hublist pingers. The PING HSUP handler errored out silently (#162) and even when it didn't, the BINF validation rejected pinger clients that legitimately omitted I4 / I6 (#161). Both fixes restore hublist visibility on public hubs.

Reg-only (private) hubs are unaffected by #161 / #162 in operator-visible ways but still benefit from the #160 defense-in-depth and the core/server.lua latent-bug closure. Upgrade is recommended for all operators regardless of hub mode.

Bugfixes

#161 - BINF without I4 / I6 was rejected

Per ADC 4.3.x the I4 / I6 fields are conditionally required (only when the client advertises TCP4 / UDP4 / TCP6 / UDP6 in SU). Hublist pingers and any IP-agnostic probe legitimately omit them. The hub now treats a missing I4 / I6 like the spec-defined 0.0.0.0 placeholder - fills in the TCP-source IP under the connection's address family, no special-case "no-IP user" shape downstream. kill_wrong_ips spoof-detection is unchanged for actually-mismatched claims.

Mirrors upstream luadch/luadch#176.

#162 - PING HSUP handler crashed silently on public hubs

A T1.3 regression introduced in v3.1.8 by #147: the new SS / SF aggregator loop called pairs( _normalstatesids ) but pairs was not imported into the core/hub_dispatch.lua sandbox locals. Every ADC PING handshake against a reg_only = false hub hit the sandbox guard, the dispatcher errored out per-connection (caught by the hub's pcall, logged to error.log), and the pinger saw zero frames.

Reg-only hubs were unaffected because the _cfg_reg_only short-circuit prevented the aggregator from running.

#160 (Sopor) - etc_trafficmanager defense-in-depth

The onSearch listener already swallows searches in both directions for blocked users, so a blocked user normally has no search to reply to. The new onSearchResult listener catches the protocol-violating edge case where a blocked user sends an unsolicited DRES / FRES (or a DRES targets a blocked user). Plugin bumped to v2.2.

Latent crash in core/server.lua changesettings()

tonumber() called seven times without local tonumber = use "tonumber" import. Function is currently dead code (no caller in hub or plugins) so no production impact; surfaced by the #162 sandbox-locals audit. Fix is a one-line use declaration.

Features

#159 (Sopor) - pre-compiled linux-aarch64 Raspberry Pi binary

New release artifact luadch-v3.1.9-linux-aarch64.tar.gz alongside the existing linux-x86_64 and windows-x86_64 builds. Native arm64 build on GitHub's ubuntu-24.04-arm runner - no cross-compile.

Covers Raspberry Pi 3+ / 4 / 5 / Zero 2W with a 64-bit OS (>95% of the active Pi installed base in 2026). 32-bit ARM (Pi 1 / Zero v1 / Pi 2 32-bit) still requires the source build per docs/BUILDING.md.

Notes

  • No breaking changes, no cfg / lang-file edits required. Drop-in upgrade from v3.1.8.
  • Pre-merge review pattern. All three bugfixes were caught by a two-pass review (independent agent + self-spot-check) that was codified during this cycle. The review also surfaced the core/server.lua tonumber latent bug as a sibling-module audit finding.
  • 3.1.x line still on security-fixes-only. v3.1.9 is a maintenance release; new feature work continues on the 3.2.x line on master per CLAUDE.md §8.

Downloads

File Platform
luadch-v3.1.9-linux-x86_64.tar.gz Linux glibc x86_64
luadch-v3.1.9-linux-aarch64.tar.gz Linux glibc aarch64 (Raspberry Pi 3+ / 4 / 5 / Zero 2W, 64-bit OS)
luadch-v3.1.9-windows-x86_64.zip Windows x86_64 (MinGW UCRT64)
ghcr.io/luadch-ng/luadch:v3.1.9 Container, linux/amd64 + linux/arm64

Migration from v3.1.8

Drop the new install tree in place of the old one (or git pull && cmake --build build && cmake --install build from source). Container users get the bundled *.lua sync on the next docker compose up -d after pull.

No cfg/cfg.tbl migration is needed.

Build from source

git clone --branch v3.1.9 https://github.com/luadch-ng/luadch.git
cd luadch
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
cmake --install build

v3.1.8

Choose a tag to compare

@Aybook Aybook released this 12 May 18:28
aad70a9

Luadch v3.1.8

Modernisation-complete patch release. Closes the #80 ratelimit v2 and #147 ADC protocol coverage tracks. After this release the 3.1.x line is stable, security-fixes-only; new feature work moves to the 3.2.x line on master. See CLAUDE.md §8 for the full release-line policy.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories. This release adds new keys to several lang files (additive, see below) and introduces several new optional cfg keys for the ratelimit / RDEX / PING extensions.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Lang file changes

Operators with stock bundled lang files get the new keys automatically via the Docker autosync from #118 or cmake --install build for source builds. Operators with custom translations have a small one-time additive merge:

File New key
cmd_shutdown.lang.{en,de} msg_hub_disabled
cmd_restart.lang.{en,de} msg_hub_disabled

Missing keys fall back to the hardcoded English string ("Hub is shutting down." / "Hub is restarting.") so the hub stays functional.

Cfg additions

All new keys are additive with defaults that preserve v3.1.7 behaviour. No cfg/cfg.tbl migration is required.

Ratelimit v2 (#80)

-- Per-bucket rate / burst, each independently tunable
ratelimit_user_pm_rate  = 5,   ratelimit_user_pm_burst  = 10,
ratelimit_user_inf_rate = 2,   ratelimit_user_inf_burst = 20,
ratelimit_user_ctm_rate = 2,   ratelimit_user_ctm_burst = 30,

-- Per-userlevel tier overlay - any subset of fields per tier,
-- missing fields fall back to the global scalars above
ratelimit_tiers = { },
ratelimit_tier_for_level = { },

Worked example for the tier overlay is in docs/SCRIPTS.md. At defaults (empty tier tables) behaviour is identical to v3.1.7.

ADC protocol coverage (#147)

-- ADC-EXT RDEX rich redirect
hub_redirect_protocols = 3,         -- bitmask (ADC=1, ADCS=2, NEODC=4)
hub_redirect_alternatives = { },    -- list of alternative URLs as RX fields
hub_redirect_permanent = false,     -- PT1 flag on redirects

-- ADC-EXT PING min-hubs federation policy (also from #146)
min_user_hubs = 0,                  -- min OTHER hubs (federation requirement)
min_reg_hubs = 0,
min_op_hubs = 0,

Highlights

Ratelimit v2 (#80, 4 PRs + 2 review followups)

The hub's per-user rate-limit machinery split BMSG / DMSG / EMSG / BINF / DCTM / DRCM into five independent buckets. Each bucket has its own rate + burst cfg keys, and all five become optionally tier-mappable per user level via the new ratelimit_tiers + ratelimit_tier_for_level overlay. Op-level bypass preserved. Worked tier-overlay example in docs/SCRIPTS.md.

A strict-positive validator rejects rate = 0 / burst = -1 / NaN at cfg-load time, with a clear out_error log entry; the previous silent-mute failure mode under operator typo is gone.

ADC protocol coverage (#147, 8 PRs)

Eight protocol-completeness items shipped as small, additive PRs:

  • NATT relay (DNAT / DRNT, ADC-EXT 3.9) - hub-relay-only NAT-traversal for passive-passive transfers.
  • RDEX rich redirect - IINF.RP advertisement + IQUI.RX / PT NPs on every kick / redirect.
  • PING completeness - SS / SF aggregate share + file count, HE email, MU / MR / MO min-hubs federation policy now emitted in the ADPING reply. Hublist scrapers see the full spec-defined data.
  • STA emission codes - cmd_shutdown / cmd_restart emit ISTA 212 ("Hub disabled") before close so clients distinguish a graceful shutdown from a network glitch. cmd_ban switches from incorrect 230 / 231 to spec-correct ISTA 232 for finite-TL temporary bans.
  • FRES routing - feature-filtered search-result delivery (F-class) now dispatched.
  • HQUI from client honored - client-initiated quit triggers a clean close in any state instead of ISTA 125 unknown-command.
  • ECTM / ERCM dispatch - modern E-class CTM / RCM variants accepted (was: silently dropped).
  • Passthrough extensions documented - TYPE / ONID / DFAV / FEED and friends are explicitly transparent passthrough (no SUP advertisement needed, hub relays the commands without inspection).

Per the audit the hub is now at ~90% ADC + ADC-EXT coverage for hub-relevant features. Remaining 10% is BLOM (Tier 2, demand-driven) and HBRI / ZLIF (Tier 3, deferred to 3.2.x).

Operator documentation

  • New docs/SCRIPTS.md lists every bundled plugin (commands + cfg keys) with a full rate-limit configuration guide.
  • README cleaned up - dropped the "what's different in this fork" section now that the modernisation programme is done; added a release-line status table near the top.
  • CLAUDE.md §8 documents the post-3.1.8 maintenance-branch model.

Bugfixes

  • user.sendsta typo (#151) - pairs(nil) crash on callers that omitted the optional flags arg, present since the API was added.
  • user.redirect MS<quitmsg> was emitted raw; multi-word reasons produced malformed ADC. Now escaped.
  • Smoke battery's PM / CTM / RCM / NATT burst tests previously short-circuited at the target-lookup before reaching the rate-limit gate; they now self-target and exercise the actual code paths.

Notes

  • No breaking changes at defaults. Every new cfg key has a default that preserves v3.1.7 behaviour. Operators upgrading without touching cfg get the new features behind unchanged knobs.
  • ERES no longer accepted by the parser. ADC 5.3.6 defines only D and F classes for RES; the parser context tightened to [FD] as part of FRES routing. No known client emits ERES; exotic NMDC bridges that did would now see parse-time rejection instead of forward-as-E-class.
  • 3.1.x maintenance from this release on. New feature work goes to master (3.2.x line); security fixes for 3.1.x go to a release/3.1.x branch created from this tag. See CLAUDE.md §8.

Downloads

File Platform
luadch-v3.1.8-linux-x86_64.tar.gz Linux glibc x86_64
luadch-v3.1.8-windows-x86_64.zip Windows x86_64 (MinGW UCRT64)
ghcr.io/luadch-ng/luadch:v3.1.8 Container, linux/amd64 + linux/arm64

Migration from v3.1.7

Drop the new install tree in place of the old one (or git pull && cmake --build build && cmake --install build from source). Container users get both the bundled *.lua sync and the lang add-only sync on the next docker compose up -d after pull.

No cfg/cfg.tbl migration is needed - all new keys are additive with defaults. To opt into the tier-overlay rate-limit, see the worked example in docs/SCRIPTS.md Rate-limit configuration.

Build from source

git clone --branch v3.1.8 https://github.com/luadch-ng/luadch.git
cd luadch
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
cmake --install build

v3.1.7

Choose a tag to compare

@Aybook Aybook released this 11 May 13:57
68db73d

Luadch v3.1.7

Plugin data-integrity patch release. util.savearray / util.savetable are now atomic-by-default across the bundled scripts tree, defensive or {} was swept onto util.loadtable consumers, the cross-month accounting bug in usr_uptime is fixed, and cmd_gag gains a shadowmute mode plus duration syntax.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories. This release modifies scripts/lang/cmd_gag.lang.{en,de} (5 new keys + 4 rewritten strings for shadowmute / duration support) and adds encrypt_usertbl to examples/cfg/cfg.tbl. If you have customised cmd_gag translations, see the Lang file changes section below before letting the autosync run.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Lang file changes

Operators with stock bundled lang files get the new keys automatically via the Docker autosync from #118, or via cmake --install build for source builds. Operators with custom cmd_gag.lang.* files have a small one-time merge:

File What changed
cmd_gag.lang.{en,de} New keys: msg_invalid_duration, msg_add_user_with_duration, msg_expired, ucmd_duration, ucmd_menu_ct1b. Rewritten: msg_usage, help_usage, help_desc (now reference shadowmute + <DURATION>), msg_show_users (adds the "Shadowmuted users" block). Missing keys fall back to the hardcoded English defaults; behaviour stays correct, the chat output just sits in mixed languages until the merge happens.

Features

  • cmd_gag shadowmute mode + duration (#85 / #132) - the gag bot gets a 4th mode where the target sees their own messages echo back as if everything works, but nobody else on the hub receives them. Combined with this, all three restriction modes (mute, kennylize, shadowmute) now take an optional duration: 30s, 10m, 2h, 1d, 1w and combinations like 1h30m. Empty duration = permanent. Restrictions auto-expire as the gag table is walked every 60s. Offline ungag works through hub.getregusers() so admins can lift restrictions even after the user disconnects. Right-click menu gets a "Shadowmute User" entry alongside the existing mute / kennylize ones.
  • encrypt_usertbl opt-out toggle (#128 / #129) - the Phase-7f AES-256-GCM at-rest encryption of cfg/user.tbl is now optional. Default true preserves the v3.1.3+ behaviour. Setting encrypt_usertbl = false writes plaintext Lua for single-user / home-hub deployments where disk-level confidentiality isn't part of the threat model and operator tooling needs direct read access to the file. Auto-detected on read via the LDC1 magic prefix, so migration is transparent in both directions; master.key is loaded if present (legacy decrypt) but only auto-generated when encryption is on.

Bugfixes

  • usr_uptime cross-month accounting (#127 / #131) - sessions that span month boundaries no longer accumulate as "years" of uptime. The pre-fix code bracketed sessions on login and credited the whole span to the login month on logout, so a session starting on the 31st and ending on the 1st saw the end-month-minus-start-month delta arithmetic wrap into multi-year totals. The fix walks the user list on a 60-second timer and credits each tick to the calendar month it falls into, so cross-month sessions land in both months correctly.
  • Plugin save crash-safety - F-PLG-1 (#133 / #134) - util.savearray and util.savetable previously opened the target file in "w+" (truncate) and wrote serialised content directly, so a hub crash mid-write left the .tbl partial. Both helpers now route through a new public util.atomic_write(path, content) helper that does tmp + rename (Windows fallback: remove then rename). 21 plugin save sites across the bundled tree get crash-safe writes with zero call-site changes; core/cfg_users.lua keeps its chmod 600 for user.tbl separately via util.chmod_secret.
  • Plugin load nil-handling - F-PLG-2 (#133 / #135) - util.loadtable returns nil on missing / unreadable / parse-fail. Defensive or {} added at 22 consumer sites across 12 bundled plugins (bot_session_chat, cmd_accinfo, cmd_delreg, cmd_nickchange, cmd_reg, cmd_usercleaner, etc_msgmanager, etc_trafficmanager, usr_hide_share, plus field-read defence in cmd_hubinfo, cmd_uptime, hub_runtime). Sites with the existing init-pattern (check_hci style: type-check + savetable + opchat warning) intentionally left alone - they handle nil correctly and auto-create the file.
  • F-PLG-3 audit (#133) - silent no-op on incomplete +cmd audited across 24 bundled scripts and 53 utf.match-on-parameters sites. No actionable bugs found. Every command handler already has either an explicit pre-check guard or a final msg_usage fallback. Audit closeout on the tracker.

Notes

  • New public helpers in core/util.lua: util.atomic_write(path, content) and util.tabletostring(tbl, name). Plugins that roll their own save logic can route through them for crash-safe writes. The companion luadch-ng/scripts repo already uses them in ptx_poll_bot, ptx_freshstuff, etc_requests, and etc_mainecho (min hub version: v3.1.7).
  • Smoke harness: 31/31 PASS on Linux + Windows. No test count change in this release; the existing tests already cover the new behaviour (atomic-write path validated by test_usertbl_bak_atomic_refresh from v3.1.5).
  • Companion plugin updates landed in luadch-ng/scripts#24 (closed) with two PRs adding atomic save + nil-handling to the curated plugin tree - operators running those plugins should also pull the latest from that repo.

Downloads

File Platform
luadch-v3.1.7-linux-x86_64.tar.gz Linux glibc x86_64
luadch-v3.1.7-windows-x86_64.zip Windows x86_64 (MinGW UCRT64)
ghcr.io/luadch-ng/luadch:v3.1.7 Container, linux/amd64 + linux/arm64

Migration from v3.1.6

Drop the new install tree in place of the old one (or git pull && cmake --build build && cmake --install build from source). Container users get both the bundled *.lua sync and the lang add-only sync on the next docker compose up -d after pull.

No cfg.tbl migration is needed. The new encrypt_usertbl key in examples/cfg/cfg.tbl is purely additive - existing cfg/cfg.tbl files without the key default to encrypted, which matches v3.1.6 behaviour. To opt out, add encrypt_usertbl = false and +reload (or restart the container).

Build from source

git clone --branch v3.1.7 https://github.com/luadch-ng/luadch.git
cd luadch
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
cmake --install build

v3.1.6

Choose a tag to compare

@github-actions github-actions released this 09 May 21:04
e0fe8f8

Luadch v3.1.6

Security-themed patch release. Hub now defaults to TLS-only with an auto-generated self-signed cert on first boot, password leakage in admin reply paths is closed for +setpass / +accinfo / +usersearch, and Docker deployments pick up new bundled language files automatically.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories. This release touches lang files in particular - if you have customised translations for cmd_accinfo, cmd_setpass, or cmd_usersearch, see the Lang file changes section below before letting the autosync run.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Lang file changes

This release adds new lang keys and one renamed value. Operators with stock bundled lang files (scripts/lang/<plugin>.lang.{en,de}) get all of this automatically via the Docker autosync introduced in #118, or via cmake --install build for source builds. Operators with custom lang files have a small one-time merge:

Plugin What changed
cmd_accinfo.lang.{en,de} New key: msg_redacted = "<REDACTED>". Falls back to the hardcoded English default if missing.
cmd_usersearch.lang.{en,de} Same new key: msg_redacted = "<REDACTED>".
cmd_setpass.lang.{en,de} Existing msg_ok rewritten: "Password was changed to: " -> "Password was changed." (no longer concatenates the password value). Custom translations that read like a sentence-prefix will sit next to a missing value cosmetically; behaviour is correct regardless.
usr_nick_length.lang.{en,de} New file - previously the script had no lang infrastructure. Two keys: msg_failedauth_reason, msg_invalid_length.

Breaking

  • TLS-only default (#77 / #113) - the bundled cfg/cfg.tbl now ships TLS-only on both stacks: IPv4 (tcp_ports = { }, ssl_ports = { 5001 }) and IPv6 (tcp_ports_ipv6 = { }, ssl_ports_ipv6 = { 5003 }), with use_ssl = true. Existing cfg/cfg.tbl files are not migrated - operators upgrading keep their plain-port settings on both stacks until they choose to flip. Fresh installs and Docker first-boot are TLS-only by default.

Features

  • Auto-generated self-signed cert on first boot (#113) - if certs/servercert.pem / serverkey.pem are missing, the hub generates a P-256 ECDSA pair via adclib's OpenSSL bindings and writes them to disk before the TLS listener binds. Keyprint logged to stdout in the boot banner so docker compose logs / the launching terminal shows the adcs://host:port/?kp=SHA256/<base32> URL to share with users. make_cert.{sh,bat} stay around for manual rotation; the entrypoint no longer runs them.
  • Slaxml XML parser bundled (#112) - lib/slaxml/slaxml.lua is now part of the install tree. Plugins can use "slaxml" for RSS / XML feed parsing without a separate dep install.
  • Docker autosync extended to lang files (#118) - new bundled scripts/lang/*.lang.* files land on operator mounts automatically. Strictly add-only, existing translations are never overwritten. The same LUADCH_AUTOSYNC_SCRIPTS=0 opt-out covers both the *.lua overwrite-on-diff sync and the new lang add-only sync.

Bugfixes

  • Password redaction in admin reply paths (#95 partial / #119):
    • +setpass drops the password from the caller's reply. The target user still receives the new password via PM (admin-sets-target case) - they need it to log in.
    • +accinfo and +usersearch show <REDACTED> in the password column instead of the cleartext value.
    • +reg auto-generated password delivery is intentionally unchanged - target needs the value to log in. The cmd_reg redesign is Phase-8+ scope and depends on either an alternate delivery channel (#100 SMTP) or a token-based first-login flow.
  • usr_nick_length localisation (#48 i18n half / #117) - operator-facing onFailedAuth reason and user-facing ISTA 221 kill message now route through the new scripts/lang/usr_nick_length.lang.{en,de}.
  • Plugin-header grammar fix (#114 / #116) - 'an user' -> 'a user' across nine bundled plugin headers. Comment-only.

Notes

  • Bug-report and feature-request issue templates added under .github/ISSUE_TEMPLATE/.
  • Smoke harness: 14/14 PASS on Linux + Windows (no test count change in this release; existing tests cover the new behaviour).

Downloads

File Platform
luadch-v3.1.6-linux-x86_64.tar.gz Linux glibc x86_64
luadch-v3.1.6-windows-x86_64.zip Windows x86_64 (MinGW UCRT64)
ghcr.io/luadch-ng/luadch:v3.1.6 Container, linux/amd64 + linux/arm64

Migration from v3.1.5

Drop the new install tree in place of the old one (or git pull && cmake --build build && cmake --install build from source). Container users get both the bundled *.lua sync and the new lang add-only sync on the next docker compose up -d after pull.

If you want to flip to TLS-only on an existing deployment, edit cfg/cfg.tbl. luadch separates IPv4 and IPv6 listeners into distinct port arrays - flip both stacks to mirror the new bundled defaults:

tcp_ports      = { },
ssl_ports      = { 5001 },
tcp_ports_ipv6 = { },
ssl_ports_ipv6 = { 5003 },
use_ssl        = true,

If you only run on one stack, leave the other one as-is. Then +reload (or restart the container). The hub's auto-cert-gen path picks up the missing certs/serverkey.pem / servercert.pem and writes a fresh self-signed pair before binding the listener.

Build from source

git clone --branch v3.1.6 https://github.com/luadch-ng/luadch.git
cd luadch
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
cmake --install build

v3.1.5

Choose a tag to compare

@github-actions github-actions released this 09 May 14:19
077d03d

Luadch v3.1.5

Patch release. Closes upstream luadch/luadch#189 (registered users disappearing) and adds image-side auto-sync of bundled plugin code on docker compose pull.

⚠️ Before upgrading

Back up your cfg/, scripts/lang/, scripts/data/, scripts/cfg/, certs/, and secrets/ directories. Bundled scripts/*.lua are auto-synced from the image; everything else is operator-owned and never touched by an upgrade, but a clean snapshot is the safety net for any production hub.

tar -czf "luadch-backup-$(date +%F).tar.gz" cfg scripts certs secrets

Bugfixes

  • #108 / upstream luadch#189 - registered users no longer disappear from user.tbl. Stale file-scope cache in cmd_nickchange.lua + non-atomic saveusers were the two root causes; both fixed in #109.

Features

  • Docker entrypoint auto-syncs bundled scripts/*.lua from the image to the mounted scripts/ directory on every container start (#110). Operator-owned state untouched. Opt-out: LUADCH_AUTOSYNC_SCRIPTS=0. See docs/DOCKER.md.

Notes

  • user.tbl.bak now refreshed on every successful save (was: only at +reload). Operators relying on .bak as a stale-rollback should adjust workflows.
  • Smoke harness: 12 -> 13 tests.

Downloads

File Platform
luadch-v3.1.5-linux-x86_64.tar.gz Linux glibc x86_64
luadch-v3.1.5-windows-x86_64.zip Windows x86_64 (MinGW UCRT64)
ghcr.io/luadch-ng/luadch:v3.1.5 Container, linux/amd64 + linux/arm64

Migration from v3.1.4

None required. Drop the new install tree in place of the old one (or git pull && cmake --build build && cmake --install build from source). Container users get the auto-sync of bundled scripts/*.lua on the next docker compose up -d after pull.

Build from source

git clone --branch v3.1.5 https://github.com/luadch-ng/luadch.git
cd luadch
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
cmake --install build