ANetBBS Changelog
Versions are internal build numbers. Public releases are tagged
separately. Current release: v1.0b2.58 (July 2026). Full release: August 1 2026.
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 hotkeyK) 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=1for 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 frominstall.sh's required packages — fresh installs had no working terminal downloads out of the box. Now installed automatically. Existing installs upgrading viaupdate.shneedsudo apt-get install -y lrzszonce by hand (update.sh doesn't manage system packages).
v1.0b2.27 — Telnet door game servers (July 2026)
- FEATURE: new
door_telnetgame type for external telnet-only game servers (e.g. TWGS — Trade Wars Game Server, likegame.a-net-online.lol:2002, A-Net Online's own TWGS). No pre-authentication handshake likedoor_rlogin— justhost: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:
/mentionsalways showed 0 — mention detection was wired to event types the bridge never sends; moved to the realmrc_messagepath. Status-bar!Nindicator 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:
/mentionsoutput misaligned on wrap — restructured into a header line + indented body line per mention. - CHANGE:
/helpand/helpserverswapped —/helpnow asks the hub for its own help,/helpservershows the client's local command list. - FIX: outgoing text color never persisted across reconnects — now restores on join and saves via
set_styleon 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
ListPaginationhelper (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
AutoBanConfigsingleton model, settings card added to the existing IP Bans admin page. 8 new tests intests/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 Synchronetlogon1.ans/logon2.ans/logon3.ansmulti-screen convention.welcome132.ansis shown first; addwelcome132_2.ans,welcome132_3.ans, etc. and they display right after, same login — works for thedata/text/<slot>.ansoverride 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_rannaming (welcome_ran.ans,welcome_2_ran.ans, ...) — shows just one, chosen at random; wins over plain numbered naming if both exist. Seedocs/04-ansi-screens.md. - FIX (pre-existing, unrelated to the feature above — found while testing on the Pi3): an active
BbsAnsiScreenDB 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.ansandwelcome.answere 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 matchgoodbye132.ans/newuser132.ans's style. Added@PAUSE@to all three welcome variants and togoodbye.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/CHANGELOGwas 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.6for 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'sinstall_requiresis a separate, hand-maintained list fromrequirements.txt—update.sh/install.shinstall viapip install -e ., which readssetup.py. v1.0b2.20 addedpyspellcheckertorequirements.txtonly, 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. Addedpyspellchecker>=0.8.0tosetup.pytoo.
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'smsgbody/msgbody_richfilters 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.pywraps 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()inanetbbs/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_v2andBBSMenuUI.read_thread_v2 = _read_thread_v2monkey-patch the class, replacing the class-body methods with newer "v2" versions that add inlineN=new-thread andR=reply support. The board menu actually callslist_threads→_list_threads_v2→read_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_v2to 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_TICKwas 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.
v1.0b2.17 — ANetCRAFT: fix severe lighting-related slowdown (July 2026)
- FIX: v1.0b2.16's
World.light_at()was ported straight from the C# version's per-cell design — an O(R²) 17×17-tile neighborhood scan for every query, called once per visible cell (~1560 cells/frame in_draw_world). Cheap in compiled/JIT'd C#; measured 155ms/frame in pure Python against an 80ms tick budget — reported by Jerry as "very slow" on the main ANetBBS server and "pretty much unplayable" on a Pi3. NewWorld.compute_light_grid(cam_x, cam_y, vp_w, vp_h, is_day)computes the entire viewport's lighting in one pass: one O(depth) scan per column for skylight instead of per cell, and one pass over the viewport+margin that finds each light source once and splats its falloff onto nearby grid cells, instead of every cell independently re-scanning its neighborhood for sources._draw_worldnow calls this once per frame. Measured 5-7ms/frame typical, ~29ms even in a deliberately dense-torch stress test — both comfortably under budget. Verified byte-identical output against the old per-cell method across a full viewport (0 mismatches) before trusting the optimization. - FIX: Dropped lava (block 15) from
LIGHT_SRCin the Python version — a visible lava sea in the Nether can be hundreds of tiles, each of which would trigger an O(R²) splat; torches/glowstone/portals are naturally sparse and don't have this problem. (Lava is still a light source in the standalone C# door — not an issue there, not changed.)
v1.0b2.16 — ANetCRAFT: full Minecraft-style progression + win condition (July 2026)
- FEATURE: Ported the same expansion built for the standalone C# ANetCRAFT-Door project into the bundled Python
anetbbs/features/anetcraft.py(1582 → 2468 lines), by hand — the two are separate codebases with no shared code. Added:World.light_at()sky+torch/glowstone lighting (hostile spawns now gated on darkness viais_dark(), not day/night, so unlit caves are dangerous at noon); furnace block (id 20) + smelting (SMELT/FUELtables, iron/gold ore now drop raw ore ids 67/68 instead of ingots directly, sand→glass moved from instant craft to furnace); 4-tier armor system (ids 77-92) withPlayer.total_defense()/reduced_damage(); lava burn damage +fire_ticksstatus; a_respawn_player()death cycle that didn't exist before; a Nether dimension (World._gen_nether()) reached by building + lighting an Obsidian portal with Flint and Steel (flood-fill frame validator in_try_ignite_portal), with Blaze and Ghast mobs; Enderman mob → Ender Pearl → Eye of Ender crafting chain; an End dimension (World._gen_end()) reached through a ring of 8 End Portal Frames, hosting a full Ender Dragon boss fight (_dragon_ai) with healing-crystal pillars, ending in a victory screen (_render_victory_overlay) and Dragon Egg trophy. World keeps going after victory. - FIX: Pressing Enter in the ANetCRAFT inventory screen never actually swapped items —
_handle_key's generic "close overlay" branch intercepted Enter before it ever reached_inv_key's swap logic. The "Enter=grab/drop" hint had been non-functional since this feature was written. - FIX: Starving to 1 HP in ANetCRAFT could leave a player stuck there forever — there was no death/respawn handling at all for the zero-HP case.
- NOTE: Save format changed (
dimslist of per-dimension world+mobs instead of a single top-levelworld/mobs).load()auto-migrates old single-dimension saves, so existing player saves on the live server load fine.
v1.0b2.15 — Terminal message wrapping fix + Admin menu reorg (July 2026)
- FIX: Terminal message reader (board threads, private messages, Inter-BBS instant messages) truncated any line past column 78 (
line[:78]) instead of word-wrapping it — long web-composed paragraphs were cut off mid-sentence when read over telnet/SSH.read_thread, the PM viewer, and the InterBBS IM viewer now build their output through_wrap_text()and a new shared_page_lines()pager (extracted from_page_text()), so long messages word-wrap to the session's terminal width and paginate with a--MORE--prompt instead of truncating. - FEATURE: Admin navbar dropdown reorganized from one 58-item flat list into 8 entries (Dashboard + Users/Messages/Files/Content/Network/System/Settings). Each category opens a new
/admin/hub/<section>page rendering its tools as cards (ADMIN_HUB_SECTIONSinweb/admin.py,templates/admin/hub.html). Purely a navigation change — no existing admin route or URL was moved.
v1.0b2.14 — CP437 encoding fixes: terminal input + web renderer (July 2026)
- FIX: ANEdit dropped CP437 high-byte characters (ä ö ü Ä Ö Ü ß etc.) entered from SyncTERM/CP437 sessions. Root cause:
_Keysdecoded raw bytes aslatin-1, turning CP437 byte 0x84 (ä) into U+0084 (a C1 control character), whichisprintable()rejects._Keysnow usessession.encoding(defaultcp437) so high bytes decode to their correct Unicode glyphs and are inserted normally. UTF-8 multi-byte buffering also added for SSH sessions. - FIX: Web interface rendered correctly stored Unicode characters (ä ö ü ß etc. in local board posts) as CP437 box/line/math symbols. Root cause:
render_msg_body_richalways ran a latin-1→CP437 round-trip (e.g. ä U+00E4 → byte 0xE4 → CP437 Σ). Local posts are stored as proper Unicode and need no charset conversion — the round-trip is now skipped when nochrsheader is present.