ANetBBS Changelog

Versions are internal build numbers. Public releases are tagged
separately. Current release: v1.0b2.32 (July 2026). Full release: August 1 2026.

v1.0b2.32 — Sysop-specific paths scrubbed from shared code (July 2026)

  • FIX: several hardcoded fallback paths/usernames from the maintainer's own install had leaked into shared code (drop-file path resolution, default gallery seed data, installer/upgrade-wizard fallbacks, an IRC CTCP reply URL, the sudoers template) — real bugs that would misconfigure a different sysop's install if the normal config lookup ever failed, not just cosmetic. Fixed all of them; also scrubbed the same path from the in-app tutorial, the seeded wiki content, and the GitHub issue template.
  • FIX: 3 systemd unit files (anetbbs-binkp.service, anetbbs-finger.service, anetbbs-mrc-irc-bridge@.service) were missed in the first pass and still had the maintainer's own User=/path hardcoded — found via a follow-up audit. Fixed to match every other unit's generic anetbbs//opt/anetbbs convention.
  • FIX: a stray third-party email address (copied from wherever a bundled tool was originally adapted from) in tools/bbs-files-tool.py's help text pointed sysops at an unrelated stranger instead of this project's own issue tracker.
  • FIX: tests/msp_loopback_test.py (a manual diagnostic script, not a real test) matched pytest's default test-file naming pattern and had no if __name__ == '__main__': guard — every pytest tests/ run was silently starting real MSP/SYSTAT network listeners and mutating process-wide environment variables as an import-time side effect. Renamed to msp_loopback_check.py (no longer matches pytest's collection pattern) and gated its body behind a proper main()/__main__ guard. Also fixed a second, unrelated hardcoded absolute path in the same file left over from a different machine's mount point.
  • FIX: two more test files (test_ebook_terminal_menu.py, test_qwk_hub_gating.py) had the same os.environ['FLASK_ENV'] leak pattern as last version's TestingConfig.SQLALCHEMY_DATABASE_URI fix — set on every test run, never restored. Fixed both.
  • FIX: anetbbs/web/healthz.py and anetbbs/web/preflight.py checked a FINGER_PORT config key that was never actually set anywhere (the real setting is FINGER_LISTEN_PORT), so the finger service's health check/preflight validation silently ignored any non-default port a sysop configured. Added FINGER_LISTEN_PORT/FINGER_LISTEN_HOST to Config and fixed both call sites.
  • FIX (docs): docs/00-overview.md's table of contents was missing links to two existing doc files (17-development.md, 20-federation.md); README's wiki page count was stale (41 → actual 43); README's Doors feature list didn't mention the bundled door_rlogin/A-Net Online game server; a few wiki pages used dead internal [[Page]]-style links for external tools (SyncTERM, NetRunner, DOSBox-staging) instead of the real external links used elsewhere in the same content.
  • FIX (docs, full accuracy pass): a broad cross-check of every doc file, the in-app sysop tutorial, and the seeded wiki against the actual current code turned up a large batch of stale/wrong content, most notably: docs/12-upgrading.md and docs/01-installing.md described an entirely stale install/upgrade flow (wrong backup path — /tmp/anetbbs-backup-*, not next to the install; wrong service names — the unified anetbbs.service replaced the legacy split anetbbs-telnet/anetbbs-ssh a while ago; a wizard prompt list missing half the real prompts; automatic rollback described as an interactive "offer"); the sudoers install instructions in docs/02-sysop-daily-ops.md, the in-app tutorial, and two wiki pages all broke when the sudoers template's placeholder was renamed to __SERVICE_USER__ earlier in this same version — a plain cp now installs a non-functional rule, fixed everywhere to show the required sed substitution; the wiki's Themes page listed five theme names that don't exist anywhere in the codebase (real theme count is 9, not the fabricated list); the wiki's Doors page and docs/17-development.md undercounted door types (7 vs the real 10, missing door_dosemu/door_telnet/door_dos_browser); a wiki page referenced a tools/db_backup.sh script that doesn't exist; docs/INSTALL.md was missing the anetbbs system-user creation step its own systemd section depends on and had a wrong apt package list; a real (non-doc) bug found along the way: anetbbs/web/healthz.py/preflight.py checked a FINGER_PORT config key that was never actually set anywhere, silently ignoring any custom Finger port a sysop configured — fixed by adding the real FINGER_LISTEN_PORT/FINGER_LISTEN_HOST to Config.
  • No behavior change for the maintainer's own existing install from any of the above.

v1.0b2.31 — Docker support (July 2026)

  • FEATURE: Docker deployment alongside the traditional install.sh/systemd path — single-container quick start (supervisord-managed) and a docker-compose "correct way" (one container per service), both built multi-arch (amd64+arm64). See docs/22-containers.md.
  • FEATURE: Sysop Control Panel restart/upgrade works under docker-compose via a Docker-socket backend instead of systemctl; one-click self-upgrade pulls a new image tag and recreates containers.
  • FIX (hardening, also benefits bare metal): SQLite WAL mode + busy-timeout (anetbbs/models.py); MSP_PORT/SYSTAT_PORT now environment-overridable.
  • FIX: the admin DB Backup download button streamed the SQLite file with a plain copy and no checkpoint — under the new WAL mode, recent writes could sit only in the -wal file and be missed. Now forces PRAGMA wal_checkpoint(TRUNCATE) first. update.sh's own automated backups were already safe (sqlite3 .backup).
  • FIX (critical, live-caught): reading any echomail message via terminal (SSH/telnet/rlogin) failed with "Menu action failed (see server log)". Root cause: the read-only message viewer (_ViewerScreen.draw_text() in anedit.py) was never updated to accept the misspell parameter added when spell-check shipped (v1.0b2.20) — the shared redraw code the viewer inherits from the editor always passes it. Latent since v1.0b2.20; nobody had tried reading an echomail message via terminal since. Fixed + regression-tested (tests/test_aneview_draw_text.py, verified it reproduces the exact live traceback before the fix and passes after).
  • FIX (release hygiene): build-release.sh rewritten to build from git's own tracked/ignored file list instead of a manually maintained --exclude denylist, which had let bbs.log (containing a plaintext admin password from a local test boot) leak into 7 prior release tarballs, plus assorted stray test-scratch files. See .gitignore for the accompanying rule additions.
  • 30 new tests. Docker builds/runs themselves still need verification with real Docker access — not available in the environment this was built in.

v1.0b2.30 — Federation "Register with Hub" nav fix (July 2026)

  • FIX: the "Register this BBS with a federation hub" admin page existed but had no link anywhere in the UI. Added a Register with Hub card to Admin → Network.
  • FIX: the v1.0b2.29 hub-gating fix left the Hub Management card linking to a now-404 blueprint on every non-hub install. Hidden unless REGISTRY_MODE_ENABLED=true.
  • 2 new tests in tests/test_qwk_hub_gating.py.

v1.0b2.29 — Local chat, terminal colors, QWK/federation hub gating (July 2026)

  • FIX (critical): terminal Local Chat (Chat → 1) was a stub that only echoed your own message back to yourself, never broadcasting to other nodes. Now delegates to the already-working real-time broadcast/queue system in multinode.py (previously only reachable via a dead, unwired multinode menu action).
  • FIX (critical): broadcast()'s self-exclusion compared by username, not by node — two nodes logged in under the same account (e.g. testing with one account on two terminals) both matched the check, so the message was dropped for everyone. Now excludes by the specific sending node's slot.
  • FIX (critical): MagiTerm, NetRunner, and PuTTY showed no color on most screens (wall, file areas, boards, PMs, bulletins, MRC chat, pre-login welcome, games list) while the main menu rendered fine. The main menu's .ans art uses the classic ANSI.SYS bright-color convention (bold + base color); nearly everywhere else used the newer "aixterm" 90-97 SGR range with no bold, which those three clients don't recognize (SyncTerm supports both, masking the bug there). Converted every affected color table/constant across 10 files to the classic convention.
  • FIX (critical): the terminal QWK-node-application wizard wrote to whichever BBS install ran it instead of the real hub, and the hub admin review queue was exposed on every install with no gate ("all the sysops try to put in for a node and it goes to their system"). Added a real hub-side API (/qwkhub/apply, /qwkhub/status/<token>) and gated the hub-admin blueprint behind REGISTRY_MODE_ENABLED. The hub install needs REGISTRY_MODE_ENABLED=true set in its .env.
  • FIX: the federation self-registration wizard always showed "success" even when REGISTRY_URL was blank and nothing was sent. REGISTRY_URL now defaults to https://bbs.a-net.fyi, and the registration tick reports back real success/failure.
  • 9 new tests in tests/test_multinode_chat.py, plus manual verification against two real concurrent telnet sessions.
  • 4 new tests in tests/test_ansi_color_compat.py, plus manual verification against a live telnet session.
  • 10 new tests in tests/test_qwk_hub_gating.py, 4 in tests/test_registry_tick_status.py.

v1.0b2.28 — A-Net Game Server bundled by default + Ebook Reader (July 2026)

  • FEATURE: A-Net Online's rlogin game server (450+ door games) now ships pre-installed and active like LORD does. Each install gets its own randomly generated password and BBS tag on first boot — private to that BBS, never in the public source, and never regenerated afterward. Sysops can change either any time at Admin → Door Games → A-Net Game Server.
  • FEATURE: new door_rlogin BBS Tag field — the tag now has its own admin-form field instead of being hyphen-joined into the user-template text, purely for clarity (the wire format itself, username-TAG, is unchanged).
  • FEATURE: new built-in web game, Ebook Reader — search + read free Project Gutenberg classics in a book-styled reading view (serif type, page-turn navigation), with bookmarks, reading history, and download-to-.txt. Also has a terminal version (telnet/SSH/rlogin, main-menu hotkey K) with the same feature set.
  • FEATURE: per-front-end Web/Terminal toggle on every game (Admin → Games) — lets a sysop run the Ebook Reader as terminal-only, web-only, or both.
  • FIX: bundled-door seeding hardcoded max_nodes=1 for every stock door — wrong for a 20-slot remote multiplayer server. Now respects a per-door override.
  • FIX: lrzsz (ZMODEM, used by every terminal file transfer) was missing from install.sh's required packages — fresh installs had no working terminal downloads out of the box. Now installed automatically. Existing installs upgrading via update.sh need sudo apt-get install -y lrzsz once by hand (update.sh doesn't manage system packages).

v1.0b2.27 — Telnet door game servers (July 2026)

  • FEATURE: new door_telnet game type for external telnet-only game servers (e.g. TWGS — Trade Wars Game Server, like game.a-net-online.lol:2002, A-Net Online's own TWGS). No pre-authentication handshake like door_rlogin — just host:port, user logs in interactively on the remote side. Works on both web and terminal doors, mirrors the rlogin door architecture. Includes a small RFC 854 option-negotiation filter so the remote server doesn't hang and negotiation bytes never reach the player's screen. Configure via Admin → Games → Game Type → "Telnet Door Server".

v1.0b2.26 — MRC: terminal fixes + stale-session cleanup (July 2026)

  • FIX: /mentions always showed 0 — mention detection was wired to event types the bridge never sends; moved to the real mrc_message path. Status-bar !N indicator now works live too.
  • FIX: messages up to 140 chars could get silently truncated to ~120 — terminal client now accounts for the bridge's handle-prefix overhead and splits long messages into (1/2)/(2/2) chunks instead of losing the tail.
  • FIX: mention indicator was reverse-video red, illegible on some terminals — switched to explicit fg/bg colors.
  • FIX: /mentions output misaligned on wrap — restructured into a header line + indented body line per mention.
  • CHANGE: /help and /helpserver swapped — /help now asks the hub for its own help, /helpserver shows the client's local command list.
  • FIX: outgoing text color never persisted across reconnects — now restores on join and saves via set_style on change.
  • FIX: Tab nick-completion gave no feedback on zero matches and could dump an unbounded candidate list — capped at 12 with a "+N more" hint. Also fixed a real bug where a visibly-present user couldn't be found: /who's roster is comma-separated on the wire, but the parser was splitting on whitespace. Bridge now also refreshes the roster on /who.
  • FIX (critical): a mid-round version of the Tab-completion fix briefly deadlocked terminal MRC solid on the next Tab press. Fixed, covered by a timeout-guarded test.
  • FIX (bridge): a dropped connection left MRC sessions looking permanently logged in, causing "you can only be logged on once" from the upstream hub. Added a WebSocket heartbeat so the existing (correct) cleanup logic actually runs.

v1.0b2.25 — Paginate large file areas (July 2026)

  • FEATURE: file area listings are now paginated (50 files/page) instead of rendering the entire area on one page — same fix pattern as the CHANGELOG page's own pagination (v1.0b2.22), reported after a 7,000+ file door-games area took a while to load. Sort (name/size/date) applies before slicing so page contents stay correct, and pagination links preserve the active sort. New shared ListPagination helper (anetbbs/web/list_pagination.py) with ellipsis-truncated page links, since a large area can span far more pages than the CHANGELOG ever will.

v1.0b2.24 — Configurable login auto-ban (July 2026)

  • FEATURE: the login auto-ban is now sysop-configurable from Admin → IP Bans instead of a hardcoded permanent-only trigger. Reported by Firehawke (feature request 2026-07-03): 10 failed logins in 5 minutes triggered a permanent IP ban with no way to configure the threshold, duration, or disable it — caught one of their test users off guard. Now configurable: attempt limit, time window, ban duration in hours (0 = permanent, still available), and an enable/disable switch. Default ban duration changed from permanent to 1 hour, matching the proposed resolution; the 10-attempts/5-minute trigger threshold is unchanged by default but now editable. New AutoBanConfig singleton model, settings card added to the existing IP Bans admin page. 8 new tests in tests/test_auto_ban.py.

v1.0b2.23 — Multi-screen welcome/goodbye/newuser sequences (July 2026)

  • FEATURE: sysops can drop in multiple variants of any lifecycle ANSI screen (welcome, goodbye, newuser, or a custom slot) and ANetBBS shows all of them, in order, every login — the classic Synchronet logon1.ans/logon2.ans/logon3.ans multi-screen convention. welcome132.ans is shown first; add welcome132_2.ans, welcome132_3.ans, etc. and they display right after, same login — works for the data/text/<slot>.ans override and the bundled stock screens, all three width modes. Each variant controls its own @PAUSE@; nothing is auto-inserted between screens. A single file (the common case) behaves exactly as before, no persisted state of any kind. Prefer one random pick instead of the whole sequence? Use _ran naming (welcome_ran.ans, welcome_2_ran.ans, ...) — shows just one, chosen at random; wins over plain numbered naming if both exist. See docs/04-ansi-screens.md.
  • FIX (pre-existing, unrelated to the feature above — found while testing on the Pi3): an active BbsAnsiScreen DB row with an empty body blocked the fallback to the bundled stock screen, so the screen silently showed nothing at all instead of falling back. Now an empty-but-active row is treated the same as no row.
  • FIX: welcome132.ans and welcome.ans were the last stock screens with a box border still on them, plus (for the 132-col one) a duplicated Sysop/Version line — rebuilt both borderless to match goodbye132.ans/newuser132.ans's style. Added @PAUSE@ to all three welcome variants and to goodbye.ans — none of them had it, so the banner rendered and immediately fell through to the next prompt, easy to miss at normal typing speed.

v1.0b2.22 — Paginate the web Changelog page (July 2026)

  • FEATURE: /docs/CHANGELOG was rendering this entire, ever-growing file as one markdown pass on every load, getting slower release after release. Now paginated at 15 version-entries per page (newest first), with Prev/Next + page-number navigation. Other /docs/* pages are unaffected. Verified against the real route: page 1 has the newest entry, page 2 differs, out-of-range page numbers clamp instead of erroring.
  • Also fixed the "Current release" line above, which was stuck at v1.0b1.6 for a long time — added a note to the version-bump checklist so it's updated alongside every new entry from now on.

v1.0b2.21 — Fix: pyspellchecker was never actually installed (July 2026)

  • FIX: setup.py's install_requires is a separate, hand-maintained list from requirements.txtupdate.sh/install.sh install via pip install -e ., which reads setup.py. v1.0b2.20 added pyspellchecker to requirements.txt only, so it never actually installed through the normal deploy path on any environment. The feature degraded silently (by design — missing spellchecker just means the feature is unavailable, not a crash) rather than erroring, so it looked like a UI bug ("misspelled words aren't underlined") rather than a missing-dependency bug until traced back. Added pyspellchecker>=0.8.0 to setup.py too.

v1.0b2.20 — Spell check + clickable URLs, web and terminal (July 2026)

  • FEATURE: anetbbs/features/anedit.py (terminal message editor) gains live spell check — misspelled words underline as you type, and /spell (/sp) jumps to the next one with a suggestion box (0-5=replace, N=skip, Esc=cancel). Quoted lines and ALL-CAPS jargon (SSH, QWK, ANSI, ...) are excluded from checking. New dependency: pyspellchecker (pure Python, bundled dictionary, no network, degrades silently if missing — same pattern as the optional markdown/bleach deps).
  • FEATURE: Web compose forms (PM, board post, echomail, netmail) now set spellcheck="true" explicitly on their textareas, enabling the browser's native spellcheck (red squiggly underline, right-click suggestions).
  • FEATURE: https:// URLs are now clickable everywhere a message body is shown. Web: render_msg.py's msgbody/msgbody_rich filters wrap plain URLs in <a href target="_blank"> (board posts still auto-embed image URLs as <img>, that's unchanged — just extended to non-image links too). Terminal: bbs_ui.py wraps URLs in OSC 8 hyperlink escapes when displaying board threads, PM bodies, and InterBBS instant messages — clickable on terminals that support it (Windows Terminal, iTerm2, kitty, gnome-terminal), silently ignored (URL text still shows) on ones that don't (SyncTERM and most classic BBS terminal clients).
  • Verified end-to-end against a real running instance (in-memory-DB Flask test client hitting the actual routes/templates) as well as direct unit tests of every new code path, including an explicit check that the spell-check suggestion picker can't recurse unboundedly on a document full of unrecognised words.

v1.0b2.19 — Fix GitHub #3 for real: the active v2 thread reader was never patched (July 2026)

  • FIX: GitHub issue #3 (terminal message reader truncating long paragraphs instead of word-wrapping them) was marked fixed in v1.0b2.15's changelog, but a sysop retested against v1.0b2.18 and found it still truncating — with an exact root-cause diagnosis and a tested patch attached to the issue. The v1.0b2.15 fix touched read_thread() in anetbbs/features/bbs_ui.py, but that method is shadowed at module import time: near the bottom of the same file, BBSMenuUI.list_threads = _list_threads_v2 and BBSMenuUI.read_thread_v2 = _read_thread_v2 monkey-patch the class, replacing the class-body methods with newer "v2" versions that add inline N=new-thread and R=reply support. The board menu actually calls list_threads_list_threads_v2read_thread_v2_read_thread_v2, which still had the original bug: for line in (p['content'] or '').splitlines(): ... line[:_line_w], silently discarding everything past the terminal width on any paragraph without manual line breaks. Rewrote _read_thread_v2 to build its output through _wrap_text() and the _page_lines() pager (same infrastructure the v1.0b2.15 fix added), so long paragraphs wrap and long threads now paginate with --MORE--, closing the gap the original bug report asked for ("Long messages should be paginated") that even the reporter's own patch didn't fully close. Also fixed _line_w = max(76, _w - 4)max(20, _w - 4): the old floor of 76 could make lines overflow on a narrower-than-80-column terminal.
  • Verified by reconstructing the full paragraph from its wrapped output and diffing word-for-word against the original — confirmed no content loss — before shipping. Also grepped every BBSMenuUI.<name> = <name> override in the file afterward: private messages and Inter-BBS instant messages have no such shadow, so their v1.0b2.15 fixes are confirmed to still be the active code path.

v1.0b2.18 — ANetCRAFT: day/night cycle was 40 seconds, now 10 minutes (July 2026)

  • FIX: DAY_TICK was 500 (40 seconds/day at the 80ms tick rate) — reported by Jerry as far too fast to feel like a real survival mechanic. Changed to 7500 (~10 minutes/day). All consuming code (DayT()/_day_t(), the "Day N" counters, the saved-game lobby preview) derives from this one constant via division/modulo, so the change propagates cleanly with no other edits needed. Applied identically to both this Python version and the standalone C# ANetCRAFT-Door project.