ANetBBS Changelog — Beta History (pre-v1.0.0)
Archived history of every internal beta build number (v1.0a1.1 through
v1.0b2.239) from before ANetBBS's v1.0.0 full release in August 2026.
Preserved as-is — a real record of what changed at each step during
development — but split out of the main CHANGELOG.md
once it passed 6,000 lines, to keep that file scannable for the
v1.0.0-onward history that actually matters day to day. Newest-first,
same as the main changelog.
v1.0b2.73 — Close the remaining inbound packet-record parser desync (July 2026)
- FIX: v1.0b2.70's fix for the embedded-null desync only checked the 2 bytes immediately after a candidate null terminator for
MSG_TYPE_2(\x02\x00) before accepting it as a message boundary. ABINKP_DEBUG_DUMP_DIRcapture of real inbound traffic (added in v1.0b2.71/72 specifically to root-cause this) caught the gap directly: a message's real body ("Men In Black: The Secret Terror Among Us", from a live rescan) happened to contain the exact byte sequence0x00 0x02 0x00as ordinary embedded content, with no relation to any real message boundary. The 2-byte check couldn't tell the difference and falsely split the packet there, corrupting that message and the next. Traced byte-for-byte against the actual captured packet (anetbbs/echomail/binkp.py:_parse_ftn_packet()) rather than inferred from database fallout this time. Fix: additionally require a date-shaped string ("DD Mon YY") exactly where the packed message header's date field would sit — every real FTS-0001 message record has this by definition, and a coincidental byte sequence satisfying both the type marker and this pattern is astronomically unlikely. 1 new regression test reproducing the exact false-positive byte pattern found in production, confirmed to fail against the v1.0b2.70-72 parser and pass against this fix.
v1.0b2.72 — Widen diagnostic capture to cover arcmail-bundled packets (July 2026)
- DIAGNOSTIC: v1.0b2.71's
BINKP_DEBUG_DUMP_DIRcapture only instrumented the raw-.pkt-over-the-wire path in_import_completed(). A live poll transcript showed the actual bulk rescan traffic arriving as FTS-0006 arcmail bundles (0000ff91.fr7,.fr8— day-abbreviation + sequence naming, ZIP-compressed) rather than bare packets, which are unpacked via a separate code path (_is_zip()→ per-memberzipfileextraction →_parse_ftn_packet(inner)) that the diagnostic never touched. Refactored the dump into a shared_debug_dump_packet()helper and wired it into both call sites, so a capture now covers every inbound FTS-0001 packet regardless of whether it arrived bare or inside a ZIP bundle. Still zero-cost and no behavior change unless the env var is set.
v1.0b2.71 — Diagnostic capture for the still-open inbound packet desync (July 2026)
- DIAGNOSTIC: the v1.0b2.70 fix for the embedded-null packet desync reduced but did not eliminate the corruption — confirmed live, with multiple consecutive garbled
netmail_messagesrows still appearing after deploying v1.0b2.70 and running a fresh%RESCAN. Guessing further from already-corrupted database rows isn't productive: by the time a misparsed message is stored, the true wire-level byte layout that caused the misparse is gone. Added an opt-in, zero-cost-by-default diagnostic: settingBINKP_DEBUG_DUMP_DIRon the server saves the raw bytes of every inbound FTS-0001 packet to that directory before parsing (anetbbs/echomail/binkp.py:_import_completed()), so the actual wire bytes can be captured and inspected directly instead of reverse-engineering them from corrupted output. No behavior change when the env var is unset (the default). Root-cause fix to follow once real packet bytes are captured.
v1.0b2.70 — Fix inbound FTS-0001 packet-record parser desync on embedded null bytes (July 2026)
- FIX:
anetbbs/echomail/binkp.py:_parse_ftn_packet()located the end of each message's text by scanning for the first0x00byte after the header fields. FTS-0001 message records have no explicit length field, so this is normally correct — but a real inbound packet (a Mystic hub's AreaFix%RESCANreply, delivering thousands of backlogged messages after the v1.0b2.69 capValidate fix let a resubscribe actually land) contained an[ANSI]-tagged post whose raw ANSI-art body had an embedded0x00byte partway through. The old code truncated that message's body at the embedded null and left the read cursor pointed mid-body — not at a real message boundary — permanently desyncing every message record parsed after it in the same packet. Everything downstream came out as a fabricated "message" with From/To/Subject fields built from fragments of real body text, until the cursor happened to re-align on a genuine boundary a message or two later. Confirmed against a live database dump: raw\rbytes embedded infrom_name/subjectcolumns, and English sentence fragments split across fields that should never contain them. The fix only accepts a candidate null as the real terminator if what follows it looks like a valid boundary (another message's type marker, the packet's own end-of-data marker, or end of buffer); otherwise it keeps scanning past the embedded byte. Because this only fires on messages containing an embedded null (rare — mostly raw ANSI-art posts), the bulk of a rescan's messages imported correctly even before this fix; only a handful landed corrupted in the Netmail Inbox instead of their real echo areas. 3 new tests intests/test_binkp_embedded_null_desync.py, including one confirmed to fail against the pre-fix parser.
v1.0b2.69 — Fix malformed FTS-0001 packet header rejected by strict tossers (July 2026)
- FIX: every outbound FTN packet (netmail and echomail, both the outbound-poll client path and the hub-to-downstream-node server path —
anetbbs/echomail/binkp.py:_build_ftn_packet()is the single shared writer for both) had a malformed Type-2+ header extension field.capValidate(header offset 40-41) was written as a plain little-endian copy ofcapWord(offset 44-45); per FSC-0048 it must instead hold the same value byte-swapped (big-endian) — confirmed directly against thehpt/husky tosser's own source (pktread.c/pktwrite.c). A plain identical copy only happens to produce a matching pair for palindromic capWord values (high byte == low byte); ourCAP_WORD = 0x0001is not one, so every packet we ever sent silently failed this check on any strict receiving tosser. Root-caused from a real report by an external FTN sysop (SmallTime BBS, runninghpt), whose tosser logged the packet rejection outright:CapabilityWord error in following pkt! rtfm: IgnoreCapWord.Other tossers (e.g. Mystic, which several of this install's own upstream hubs run) may fail the same way without surfacing as clear an error — silently dropping the packet instead, which is indistinguishable from "the hub has nothing queued for us" from our side. 5 new tests intests/test_binkp_capword_byteswap.py, decoding the header exactly the wayhpt's own reader does rather than just asserting the bytes "look right."
v1.0b2.68 — Close the confirmed gaps from the Synchronet/Mystic comparison (July 2026)
Follow-up to the competitive gap analysis against Synchronet and Mystic BBS (see the research artifact referenced in project notes). Ships the 5 tactical gaps plus a unified access-control helper — the scripting-language gap and JAM/Squish message-base interop stay out of scope as accepted limitations for now.
- FEATURE: unified read-access check. New
anetbbs/features/access_control.py:evaluate_access()replaces scattered per-featuremin_access_level/is_sysop_only/admin-bypass comparisons (migrated: echomail area access, RSS feed listing, game lobby listing). Fixed two confirmed real gaps found along the way: the web file-area browser never checked an area'smin_access_levelat all (only the terminal client did), and/searchreturned sysop-only boards and access-gated echomail to any user, including logged-out visitors, since neither result branch checked access at all. - FEATURE: consolidated the 21 separately-duplicated admin-only route gates (
anetbbs/web/*.py) into one sharedanetbbs/web/access_control.py:require_admin/require_admin_or_403, standardized onabort(403)everywhere —admin.py's routes previously flashed a message and redirected instead, a deliberate, confirmed UX change. - FEATURE: file search —
/searchgained afilesresult category (scoped to the DB-backed file gallery; the FTN-style file-area browser has no per-file database rows to search cheaply). - FEATURE: duplicate-file detection on upload. New
anetbbs/features/file_dedup.py:hash_file(); the file gallery gets a realcontent_hashcolumn, the FTN-style file-area routes get a per-area.hashes.jsonsidecar cache (no per-file DB row exists there). A notice, not a hard block — the upload still succeeds. - FEATURE: archive integrity testing on upload, all four upload routes. New
anetbbs/features/archive_meta.py:test_archive_integrity()—zipfile.testzip()/rarfile.testrar()/py7zr's.test()where available, best-effort read-through for tar, fails open on anything untestable (missing optional library, unrecognized format) rather than blocking a legitimate upload. - FEATURE: FileFix bot — the file-echo counterpart to the existing AreaFix bot, so a downstream peer can self-service their own file-echo subscriptions via netmail the same way AreaFix already does for message echoes. The underlying data model and the outbound half already existed; only the inbound netmail-robot dispatch was missing.
- FEATURE: menu translation wiring.
User.languageandMenuTranslationexisted as schema but nothing ever read them — the data-driven terminal menu system now looks up per-language overrides when a user's language isn't English, falling back to the source text. - 51 new tests across
tests/test_admin_gate_consolidation.py,tests/test_evaluate_access.py,tests/test_archive_integrity.py,tests/test_file_dedup.py,tests/test_search_access_and_files.py,tests/test_menu_translation.py, andtests/test_filefix.py.
v1.0b2.67 — Fix netmail import crash on duplicate-check (CI-caught) (July 2026)
- FIX (caught by GitHub Actions CI, not local testing — no Flask available in the sandbox this was built in):
anetbbs/echomail/poller.py's_import_netmail()— used whenever inbound netmail is imported via the poll-response path — queriedNetmailMessage.query.filter_by(msg_id=msg_id)for its duplicate-message check, but the model's actual column ismsgid(no underscore, confirmed by the very next line's constructor call using it correctly). This raisedAttributeErrorunconditionally any time an inbound netmail carried a MSGID kludge — which is virtually all real FTN netmail — meaning this function crashed before importing the message at all, on this specific path, whenever a MSGID was present. Pre-existing bug (not introduced by v1.0b2.65's notification fix, which touched the same function but not this line) — first actually exercised by the new regression test added for that release, and the crash only surfaced once that test ran somewhere with pytest/Flask installed (the CI Docker image), not in this sandbox. Fixed to query the correctmsgidcolumn. Full local test suite (434 tests) now verified green in a real venv with Flask installed, not justpy_compile.
v1.0b2.66 — ClamAV scan timeout now sysop-configurable (July 2026)
- FEATURE: the ClamAV scan timeout (how long
anetbbs/features/virus_scan.py'sscan_path()waits forclamscanon one file before giving up and letting it through) was a hardcoded 30 seconds with no way to change it short of editing source. AddedCLAMSCAN_TIMEOUT— a new field at Admin → Settings (and the matching env var), defaulting to 60 seconds, same pattern as the existingIDLE_TIMEOUT_SECONDS/BOT_GATE_TIMEOUTsettings. Takes effect immediately on save, no service restart needed — every scan call site (per-upload scanning in the file areas and file-queue routes, plus the Admin → Bulk Virus Scan page) already re-reads it per call. 7 new tests intests/test_clamscan_timeout.py.
v1.0b2.65 — Fix missing notification for inbound netmail (July 2026)
- FIX: new inbound netmail (FTN point-to-point private mail) never triggered any notification — the in-app bell and the terminal "You have new mail" banner both stayed silent even when netmail addressed to you had actually arrived. Root cause was two separate gaps in the two inbound-netmail import paths: the QWK/poll-response path (
anetbbs/echomail/poller.py) resolved the message to a local recipient but never called the notification helper, and the real-time BinkP listener path (anetbbs/echomail/binkp_server.py, used whenever another system polls in live) never resolved a local recipient at all — so netmail received that way wasn't even linked to aUseraccount, only findable later by manually browsing and string-matching the "To" name. Both paths now share one recipient-resolution helper (anetbbs/echomail/routing.py:resolve_netmail_recipient, matching by AKA address, then username, then display name, then the network's configured DefaultRecipient) and both now create a Notification for the resolved recipient, which the existing web bell and terminal banner already know how to display — no changes needed on that side. 8 new tests intests/test_netmail_notification.py.
v1.0b2.64 — Terminal sysop tools: Node Monitor, broadened Sysop menu, MSP picker, profile redesign (July 2026)
- FEATURE: new terminal (telnet/SSH/rlogin) Node Monitor at Sysop Tools → Node Monitor — a scrollable, Synchronet-UNIX-Monitor-style live list of connected nodes (slot/user/protocol/current page+action/idle time), with per-node Kick and Message actions. Data comes from
NodeActivity(already updated on every menu render/action), the same source the web NodeSpy panel uses, so both views stay in sync. Kick sets the same DB flag NodeSpy's kick button does, picked up by the existing 5-second watchdog. Messaging a node reuses the existing sysop-reply inbox; a gap where a pushed message would never surface for installs still on the legacy hard-coded main menu (as opposed to the data-drivenBbsMenuone) was closed at the same time. - FEATURE: the terminal Sysop Tools menu was 3 static items (Manage Users, Manage Boards, Server Status) — now a scrollable menu of 14 categories, bringing most of the terminal-feasible slice of the web admin UI into telnet/SSH/rlogin for the first time: Users, Boards/Bulletins, Echomail/Hub (networks & areas, QWK node-request approval, bad-area review), Games (active session disconnect, Trade Wars 2002 universe reset), Wall moderation, File Upload Queue (approve/reject), Scheduled Events (cron-style maintenance jobs), RSS Feeds, Login/Logoff Modules, Notifications (Webhooks, Sysop Broadcast, Message of the Day, Sysop Pages), Registry/Peers (federation applicant approval, peer probes), Caller Log, Node Monitor, and Server Status. Deliberately left web-only: the ANSI art editor/theme builder, file/avatar upload, backup restore, in-place upgrades, and full network-join applicant approval (creates multiple node records and emails credentials).
- FEATURE: sending an Inter-BBS Instant Message from the terminal (
New PM) no longer requires already knowing the exactuser@hostto type — it now offers a scrollable BBS directory, live-probes the picked BBS over SYSTAT for who's currently online there, and lets you pick a name from that list. Manualuser@hostentry is still available at every step (empty directory, failed/empty probe, or just backing out). - FEATURE: Edit Profile in the terminal was a blind sequential prompt-per-field walk (easy to miss settings like Sixel mode entirely). Replaced with a single summary screen showing every current setting plus a scrollable picker for which one to change. Also added three settings that existed on the web profile editor but were unreachable from the terminal: FTN tagline, "show email publicly", and date of birth, plus two that weren't editable from either front end yet (codepage, language preference — currently unused by anything else in the codebase, but now at least settable). Password is never shown or editable here, same as before.
- FIX: the AreaFix log's Tags column could overflow its table cell on long comma-separated area lists — now wraps.
- 30+ new tests across
tests/test_terminal_node_monitor.py,tests/test_terminal_sysop_menu.py,tests/test_systat_parser.py, andtests/test_terminal_profile_redesign.py.
v1.0b2.63 — Option to hide sysop from Last Callers (July 2026)
- FEATURE: new "Hide sysop logins from the Last Callers displays" toggle at Admin → Last Callers, off by default. Keeps a sysop who logs in several times a day from flooding the user-facing Last Callers list (terminal screen, terminal inline block, and the web one-liners page) with themselves instead of real users. The admin audit list always shows everyone regardless of this setting, and InterBBS-shared entries from other BBSes are never affected by it. 7 new tests.
v1.0b2.62 — InterBBS Game Score Sharing (July 2026)
- FEATURE: door/web game high scores can now be shared with other ANetBBS installs, the same InterBBS pattern as the Graffiti Wall and Last Callers (riding echomail, opt-in, one BinkP network at a time). Only new personal bests relay, not every submission — a game's first score is trivially a personal best; after that, only a strictly higher score relays again. Each game has its own on/off switch (Admin → Games → edit a game, defaults on), plus the install-wide switch at Admin → Games → InterBBS Scores (now linked from the main admin dashboard, along with the previously-unlinked InterBBS Wall and Last Callers pages). Casino games (Blackjack/Slots/Video Poker/Hold'em) are included, tracked as "highest weekly balance" and shown as a dollar amount rather than a bare number, both locally and for synced entries. Enabling score sharing resets the four casino weekly starting balances to a shared standard value across every participating install (so a "$50,000 peak" means the same thing everywhere); changing any of those four afterward automatically turns score sharing back off rather than letting the numbers silently drift out of sync. 20 new tests.
v1.0b2.61 — QWK packet format cleanup for better reader compatibility (July 2026)
- FIX: cleaned up a few spots in the QWK packet writers/readers where fields weren't laid out quite right, which could affect compatibility with some third-party QWK reader software. No changes needed on the sysop side. 4 new tests.
v1.0b2.60 — Updated handshake/CMD_EOB for older bink clients (July 2026)
- FIX: BinkP session close-out could end a bit too early for some strict FTN mailers, causing a successful file transfer to still be reported as failed on the other end. Adjusted the M_EOB handshake and connection close-out on both the client and server side to match. Also switched outbound packet filenames to the conventional FTN naming style.
v1.0b2.59 — Fix BinkP handshake announcing a hardcoded, permanently-stale version (July 2026)
- FIX: found while diagnosing a real FTN interop report — a peer's binkd log showed our BinkP handshake announcing
VER ANetBBS/1.0a binkp/1.1. The version in that line was a literal, hardcoded string in bothbinkp.py(client handshake) andbinkp_server.py(server handshake) — it has said1.0asince this code was first written, regardless of which release is actually running, making it useless for diagnosing what version a peer is on from their own connection logs. Fixed by pulling the real version fromanetbbs.__version__in both places. 2 new tests.