ANetBBS Changelog

Current release: v1.0.68 (September 2026). This file covers v1.0.0
onward, which follows standard semantic versioning — patch releases are
v1.0.1, v1.0.2, and so on. The full internal beta build-number
history (v1.0a1.1 through v1.0b2.239) that got the project to this
release is preserved in
CHANGELOG-beta.md.

v1.0.68 — Door Games menu fixes (September 2026)

Fixed a cosmetic bug in the terminal Door Games menu where category section lines (e.g. "Puzzle / Casino") showed a literal ? in place of a small arrow separator before the door count — a non-CP437-safe character that got mangled on the way to the terminal. Replaced with a plain ASCII arrow that renders correctly everywhere. Also added a screen clear before each Door Games menu/submenu redraw, so a menu shorter than a full screen no longer leaves the previous menu's leftover content stacked above it.

v1.0.67 — Follow-up from the fourth audit pass, plus a CI-only test fix (September 2026)

Follows up on three items from the v1.0.66 audit pass that were deliberately left open pending a product decision rather than being fixed on the spot:

  • The web IRC client's SASL EXTERNAL (TLS client-certificate) support was already fully implemented at the connection layer but had no way to actually reach it — no UI, no way to supply a certificate. It's now wired up end-to-end: a sysop-facing toggle in the web IRC client lets a user paste in certificate/key PEM text, which the server writes to a fresh, server-named, permission-locked temporary location for the duration of that one connection and removes on every disconnect path. The client never supplies a server-side file path — only the certificate content itself — so this can't be used to reference or probe any file already on the server.
  • Closed a low-severity anonymous resource-exhaustion gap in inbound FidoNet file-request (FREQ) handling: a fully unauthenticated connection could queue an unbounded number of file-request entries with no realistic way for them to ever be delivered, since outbound delivery only ever happens to a real, already-configured node. Now rate-limited per source address and globally, matching the same bounding approach already used for other unauthenticated network responders in this codebase.
  • A small number of historical changelog and documentation entries referenced the sysop by name; these have been reworded to be name-neutral, matching the rest of the project's documentation.

Also fixed a CI-only test failure (caught in an actual GitHub Actions run, not reproducible locally): a regression test added in v1.0.66 relied on a module-level constant in the ANetCraft door game that was computed once at import time, so redirecting it for the test didn't fully redirect where multiplayer save data actually got written — it silently succeeded locally (against a directory that happened to already exist) and failed outright against a fresh CI checkout. Fixed by removing the constant in favor of a small function that always re-derives the save path, closing the whole bug class rather than just working around it in the test.

v1.0.66 — Fourth security/performance audit pass, plus correctness fixes and documentation cleanup (September 2026)

A fourth broad audit pass was carried out across the entire codebase — every service, every protocol handler, and the documentation/wiki — using the same severity-tiered approach as the three prior rounds (v1.0.38, v1.0.39, v1.0.58). As with those rounds, this entry intentionally omits vulnerability specifics in the interest of responsible disclosure; sysops running an older release should update at their earliest convenience. In summary, this pass:

  • Closed several real gaps where a user-facing feature had no meaningful boundary check at all, including an outbound dial-out feature that could be pointed at internal infrastructure, and a case where banning or locking a user account had no effect on that user's already-connected terminal session.
  • Fixed a case where a bulk network-fetch feature validated a destination once but then reconnected to it independently, reopening a timing gap the validation was meant to close.
  • Closed a handful of smaller gaps in inbound network-protocol handling (FidoNet file distribution, echomail/netmail composition) where untrusted peer-supplied data wasn't fully constrained before being trusted or relayed further.
  • Fixed several real correctness bugs found along the way: a broken menu link that could end a user's entire session instead of just failing that one navigation; a sysop-configurable setting that had no effect despite the admin UI showing it as active; a background task that could lose in-flight output under load; and a background delivery mechanism that was silently failing to record its own results.
  • Removed a small amount of confirmed-dead code (superseded implementations with zero remaining callers) and closed several minor consistency/hardening gaps that weren't proven exploitable today but were worth tightening anyway.
  • Corrected several inaccuracies in the documentation (an out-of-date architecture diagram, a stale claim about a security default, a couple of file-path references) and filled in a few real, previously-undocumented gaps (a user-migration tool, several configuration options, and the wiki feature itself never having its own entry in the doc index).

New regression tests were added throughout — every fix in this pass has one, verified against the pre-fix code before being counted as done.

v1.0.65 — Fixed a CI-flaky output-truncation race (September 2026)

Fixed a timing race (caught via an intermittent CI failure on a busier build server, not a live report) that could truncate or drop the last bit of output from a custom menu item's "run an external program" action: the moment the child process exited, its output-forwarding task was cancelled immediately, with no guarantee the last already-buffered chunk had actually been read and sent to the caller yet. Now that task gets a short window to finish draining normally before being cancelled.

v1.0.64 — rlogin direct-door goodbye-screen fix (September 2026)

Fixed a live-reported cosmetic bug: a caller who reached a door directly through the rlogin game-server target feature (xtrn=<slug>, added in v1.0.60) saw the sysop's goodbye screen and the plain "Goodbye!" text right before the connection hung up on exit. A game-server-style connection like this is expected to just hang up silently the moment the door exits — the same behavior a real Synchronet rlogin game-server target has — so the goodbye screen is now skipped for this specific connection type only; every other logoff path (normal menu logoff, idle timeout, dropped carrier) is unaffected.

v1.0.63 — Who's Online / registration audit-trail fixes (September 2026)

Fixed Who's Online showing an actively-chatting MRC user on a confusing internal page ("/mrc/auth-check") with the wrong IP address (127.0.0.1) instead of their real page and IP — that endpoint is an internal check nginx makes on the server's own behalf, not something a real visit should ever be attributed to. (IP addresses on this page are sysop-only and have never been visible to other users.) A user chatting purely over the WebSocket, with no other page loads, still shows correctly as online the whole time — only the mistaken page/IP is corrected, not the activity heartbeat itself.

Fixed a gap where a newly self-registered account that lands straight into a session (no email/sysop-approval gate configured) never showed up in the caller log and its login count/last-login stayed stuck at "Never" — even while actively online — until its second-ever login. New accounts are now tracked from their very first session.

v1.0.62 — A-Net Game Server credential fix, door-list pagination, and two more live fixes (September 2026)

Fixed a real bug in the A-Net Game Server bulk-import tool: it could pick the wrong credentials (host/password/BBS tag) to apply to imported games when more than one matching game-server entry existed on an install, silently applying stale or incorrect values across every imported door instead of the sysop's real, active configuration. The tool now requires an unambiguous, agreed-upon credential source across all matching active entries, and fails with a clear message rather than guessing when it can't determine one. A related gap meant the bundled "A-Net Game Server" entry could resurface active (with freshly-regenerated random credentials) after being removed; it now stays correctly deactivated whenever a sysop's own separate configuration is already active.

Fixed a UX problem with large door-game categories: a category with more entries than fit on one screen previously wrote everything to the terminal in one unpaginated block. Category listings now page through results with [N]ext/[P]rev navigation, sized to the actual terminal height.

Fixed the Backspace key not working when playing a door game through the web interface — the browser terminal was sending a byte classic BBS door software doesn't recognize as backspace; it's now translated to the byte those programs expect.

Fixed an Internal Server Error when deleting a user with any real activity history (game sessions, private messages, posts, etc.) from the admin panel — the delete now fails gracefully with guidance to use Ban/Deactivate instead, rather than crashing.

v1.0.61 — Fixed a real rlogin/telnet door freeze, and A-Net Game Server bulk import (September 2026)

Fixed a live-reported freeze: playing a door through an external rlogin or telnet game server (including A-Net Online's own game server) could hang indefinitely if the remote server went quiet without actually closing the connection — a hung remote process, a stalled door. The bridge's own socket read had no timeout, so the session sat frozen with no way to recover short of the remote end eventually dropping the connection. Every other door-launch path in this codebase already enforced an idle timeout; this closes the one remaining gap, on both the rlogin and telnet terminal-bridge paths.

Added a bulk-import tool for A-Net Online's game server: Admin → Door Games → Add games from A-Net Game Server fetches the live game list, groups it by the remote site's own categories, and lets a sysop map each one onto a local category (or skip it, or create a new one) before importing. Every imported game reuses the host, password, and BBS tag already configured on the bundled A-Net Game Server entry, direct-launching via the existing xtrn= convention — nothing else to configure per game. Re-running the import later to pick up newly added games is safe; already-imported games are skipped, not duplicated.

v1.0.60 — ANetBBS as an rlogin game-server target (inbound) (September 2026)

ANetBBS's inbound rlogin listener can now act as a game-server target for other BBSes, the mirror image of the existing outbound door_rlogin game type. A connecting client sends the same handshake convention ANetBBS's own outbound rlogin client already speaks — password and username in the (inverted) standard rlogin fields, plus an optional xtrn=<slug> in the terminal field naming one of this BBS's own Game.slug values. A caller who includes xtrn= authenticates and lands directly in that door, no menu-diving, and the connection hangs up automatically once the door exits. A caller connecting without xtrn=, or requesting an unknown/inactive/access-gated slug, gets a normal interactive session instead of being dropped, so ANetBBS keeps working as a full BBS rather than becoming a pure game-server appliance.

v1.0.59 — Echomail area-tag case fix, and federation registry probe reliability (September 2026)

Fixed a live-caught echomail bug: inbound mail tagged with an area name in a different letter case than the locally configured area (e.g. ann.test vs. the stored ANN.TEST) was rejected as an unknown area and routed to the Bad Areas queue, even though the area genuinely existed. Inbound area tags are now normalized to uppercase before lookup, matching the case convention already used everywhere else area tags are stored and matched. A related lost-update race on each area's per-message counters and last-message timestamp — present in the same inbound-import code paths, plus the QWK hub's REP-packet importer — was also closed with an atomic update.

Fixed two issues with the federation registry (the directory that lists and cross-links other ANetBBS hubs): a mislabeled field in the anetbbs-cfg terminal tool meant a sysop entering their notification address under "Sysop Email" was actually writing to an unrelated setting, leaving the real join-request notification address unset without any obvious sign of the problem — the field is now correctly labeled and the right setting has its own entry. Separately, the periodic peer health check could delist a genuinely healthy peer after a small handful of single dropped UDP probes, with no way to adjust the timing short of hand-editing .env and restarting the service; the health check now retries a probe a few times before counting it as failed, the default thresholds are considerably more forgiving, and the interval/failure-threshold/staleness values are now editable live from Admin → Federation Registry, taking effect immediately with no restart required.

v1.0.58 — Third security and performance audit pass, including the MRC bridge (August 2026)

A third, broader audit pass was carried out across the whole codebase, covering areas the first two rounds (v1.0.38, v1.0.39) didn't reach in depth — the standalone MRC↔IRC bridge daemon, echomail's netmail-command bots and hub-side message fan-out, the terminal config tool's own screen handling, and the web application's scheduled-job and background-sync paths, alongside a fresh pass over the areas the earlier rounds already covered. Real issues turned up and were fixed, again addressed in strict severity order: closing a shell-command-construction gap in the door-launch menu system, hardening several more network-facing and inter-process protocol handlers, fixing a handful of lost-update races in per-user counters and a couple of user-submission paths, improving responsiveness in the MRC bridge under concurrent load, closing a few more resource-growth and unbounded-query points in long-running background jobs, adding several more missing rate limits and size/dimension caps, tightening a login-required gap and a couple of self-service admin-account safeguards, and removing a confirmed-dead legacy code path along with the stale documentation that referenced it. New regression tests were added throughout for every fix — the full suite now sits at over 3,100 tests, all green. As with the previous two rounds, this entry intentionally omits specifics in the interest of responsible disclosure; sysops running an older release should update at their earliest convenience.

v1.0.57 — Node monitor visibility fix, and BBSDEV.DRP dropfile support (August 2026)

Fixes a real bug in anetbbs-monitor, the live CLI node monitor: an
already-connected user could fail to show up at all, and a freshly
logged-in user could show correctly at first and then silently vanish
a few minutes later, even while still fully connected. Root cause: the
underlying presence data (NodeActivity.last_seen) was only ever
refreshed by active menu navigation, door play, chat, or an AFK-state
change — a session that just sat idle on one screen never touched any
of those, so it aged out of the 5-minute online window every presence
surface uses (the web NodeSpy panel and the in-BBS Node Monitor share
the same underlying data, and the same fix). The cross-process watchdog
that already polls every session every 5 seconds now also keeps its
presence timestamp fresh on each poll, closing the gap at the source.
anetbbs-monitor itself also got a small polish pass: an AFK session
now shows in a distinct color, a session that still hasn't checked in
after the fix above gets flagged rather than silently disappearing, and
a database hiccup on a refresh tick shows a warning instead of taking
down the whole screen.

Adds support for BBSDEV.DRP, a newer drop-file format for launching
door games (an alternative to DOOR32.SYS with a richer, UTF-8-aware
field set). A door game can now be configured with drop file type
bbsdev.drp in the Game Center admin; a standalone converter,
tools/door32_to_bbsdev_drp.py, is also included for turning an
existing DOOR32.SYS from another BBS package into a compatible
BBSDEV.DRP file.

v1.0.56 — Fixed a real FidoNet hub queue that never drained (August 2026)

Reported live: a real FidoNet hub (1:123/3003@fidonet) had 1359
files / 66MB stuck in its queue for 9 days, resending the entire
backlog on every single poll even though ANetBBS logged every session
as a clean success. Watching a live session showed why: ANetBBS's own
code was disconnecting partway through a large receive, after going
through "quite many of the 1000+ files."

Root cause: BinkPClient._receive_messages()'s receive loop was
hard-capped at 5000 frames total. A 66MB transfer needs roughly 16,000
individual 4096-byte data frames alone, before even counting the 1359
per-file headers — more than 3x the cap. Once a real transfer grew
past that count, the loop gave up and moved on, leaving everything
still in-flight unacknowledged — which is exactly why the hub's queue
never shrank despite the session otherwise completing normally. This
is the same class of bug already fixed once elsewhere in this file (an
earlier range(20) cap on a different loop, replaced with a time-based
wait) — fixed the same way here: the fixed count is gone entirely, and
the loop is now bounded only by its existing 5-second per-frame idle
timeout, so a real stall still ends the session cleanly but a large,
healthy transfer is no longer cut off arbitrarily.

v1.0.55 — Manual social posts, and a CI test-suite OOM fix (August 2026)

New: the auto-social-posting queue now has a manual compose option
(+ New Post on /admin/social/) for anything the automatic
triggers don't catch — a version bump, a new feature, an event. Text
plus an optional image, queued for review exactly like an automatic
draft; nothing posts until approved. Queuing any draft — automatic or
manual — now also notifies every admin (bell-badge + live toast for an
open tab), answering a real gap: there was previously no way to know a
post was waiting without checking the page yourself.

Fixed: the GitHub Actions Docker-build workflow's own test run
was failing from the same class of memory issue fixed in v1.0.54 —
running the full ~2,986-test suite as one continuous process
accumulates memory gradually (no single runaway test, roughly 5MB/file
compounding to ~2.85GB by the end) and was getting OOM-killed on the
CI runner. Now runs in 15 batches, each its own process, resetting
that accumulation between groups — confirmed locally to complete the
full suite at under 600MB peak instead.

v1.0.54 — Fixed a real OOM: unbounded log reads and a logging-handler leak (August 2026)

Fixes the root cause of a severe memory problem found live: repeated
freezes and kernel OOM-kills of an 11-12GB python process during
test runs, traced to two bugs working together.

anetbbs/web/admin.py's Settings page (and its sysop-console tail
command) read a log file's last N lines with f.readlines()[-N:]
loading the entire file into memory before slicing. Fine on a fresh
install, but a log with no rotation grows unbounded, and this one had
reached 6.1GB / 80 million lines. Both call sites now share a helper
that seeks near the end of the file and reads a small, fixed-size
window instead, regardless of file size.

Separately — and the real reason the log got that large in the first
place — _configure_logging() added a new log handler on every app
instance created without ever removing the previous one, and Flask's
per-app logger is never garbage-collected by name, so handlers piled
up within one long-running process and every log line was written
once per accumulated handler. Fixed by clearing prior handlers before
adding new ones, and by capping the log file itself with real
rotation (20MB × 5 backups) so it can't grow unbounded again either
way.

Verified with a full ~3,000-test suite run, memory-capped and
monitored throughout: peak memory across the entire run is now ~570MB,
down from the ~12GB that was crashing the machine. New regression
tests cover both fixes.