ANetBBS Changelog

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

v1.0a2.114 — Fix: web door-game terminal animation ghosting (June 2026)

Full-screen animation door games (slot machines, etc.) showed ghost artifacts
in the web game terminal — previous animation frames persisting visually while
new frames drew on top. SSH/telnet sessions were unaffected.

Root cause: xterm.js retains a 1000-line scrollback buffer by default. When a
door game fills the 25-row screen, the terminal scrolls, pushing the current
frame into the scrollback. The game then homes the cursor and redraws, but
the scrollback content remains visible above the viewport, creating doubled
headers/footers. Fix: scrollback: 0 — DOS door games are 80×25 full-screen
apps that reuse the same grid via cursor addressing and need no scrollback.

v1.0a2.113 — Fix: terminal DATABASE_URL; clean release tarball (June 2026)

Terminal service (anetbbs/main.py) hardcoded anetbbs.db instead of
respecting DATABASE_URL from .env, splitting terminal and web onto
different databases. Menu changes, password resets, and door configs made
via the web had no effect in terminal sessions. Fix: resolution order is now
ANETBBS_DB_URLDATABASE_URL (from .env) → absolute-path fallback.

Release tarball now excludes doors/dos/ (x86 DOS game binaries, sysop-
installed) and data/ftp_root/ (created by installer). Tarball returns to
~29 MB. Pi/ARM installs no longer fail on oversized tarball with wrong-arch
binaries.

dosemu2 door launch now auto-creates the game working directory if missing.
%P in command_line_args now correctly maps to I:\ (per-node scratch
drive) instead of a Linux path in the generated bat file. FOSSIL drivers
(BNU.COM etc.) are auto-loaded if present in the game directory.

v1.0a2.112 — Fix: dosemu2 bat generic for non-TW2002 games; TW2002 ANSI via DORINFO1.DEF (June 2026)

dosemu2 _ANET.BAT was TW2002-only for all door games. _build_dosemu_command() was
injecting SET TWNODE=1, MKDIR I:\NODE2, and COPY I:\DOOR.SYS I:\NODE2\DOOR.SYS into
the generated batch file for every door_dosemu game. These lines are TW2002-specific
(node config environment variable + TW2002's per-node drop-file path). Fixed: the three
lines are now conditional on the game slug containing "tw2002". Any other DOS game
launched via dosemu2 now gets a clean generic bat. Also added DOOR32.SYS and
DORINFO1.DEF copies to the generic path so games using those drop file types find them
in their working directory.

TW2002 ANSI colors require DORINFO1.DEF, not DOOR.SYS. When using DOOR.SYS, TW2002
detects ANSI by sending an ESC[6n cursor-position probe via COM1 and waiting for the
terminal reply. This round-trip through dosemu2's pts COM1 is timing-sensitive and
unreliable — TW2002 times out and falls back to ASCII. DORINFO1.DEF carries an explicit
ANSI flag (line 10 = 1) with no probe needed. Set Drop File Type = DORINFO1.DEF
and Drop File Path = %P in the ANetBBS game admin. In TEDIT.EXE set BBS Drop file
type
= RBBS (TW2002's name for the DORINFO1.DEF format), I/O Type = Standard,
Comport = 1. Confirmed working with full ANSI color output.

v1.0a2.111 — Fix: SQLite path on new installs; MRC defaults SSL+5001; security questions for password recovery (June 2026)

Terminal "unable to open database file" on new installs fixed. main.py now always
derives DATABASE_URL from its own __file__ location (same as serve.py has done since
v1.0a2.70), rather than trusting whatever the EnvironmentFile supplies. An .env with a
stale or relative path can no longer cause every telnet/SSH session to fail at DB open time.
Custom Postgres or alternate SQLite paths still use ANETBBS_DB_URL.

MRC bridge default connection changed to SSL port 5001. Fresh install.sh runs now
generate mrc/bridge/config.json with "mrc_port": 5001 and "use_ssl": true.
Bottomless Abyss MRC supports unencrypted (5000) and SSL (5001); SSL is the better default.
Existing installs are unaffected (their config.json is preserved on update).

Password recovery via security questions. Users can now set up to 3 security
question/answer pairs on their profile (/profile/security-questions). On the Forgot
Password page, accounts with security questions configured are offered a self-service
"Answer security question" path that issues a reset token directly — no sysop required.
Answers are stored hashed (werkzeug); matching is case- and whitespace-insensitive.
Accounts without security questions continue to use the existing sysop-copy-link flow.
New DB table: user_security_answers (auto-created by db.create_all()).

Install wizard simplified. The wizard no longer asks for a separate admin username —
the sysop display name chosen at the start is used as the account login, removing a
redundant prompt that confused fresh installs.

v1.0a2.108 — Door categories + security levels (June 2026)

Game categories (dynamic): New GameCategory DB table replaces the hardcoded
category dropdown. Sysop manages categories at /admin/games/categories — add, rename,
reorder, delete. Default categories seeded on first start: Action, Classic DOS, Puzzle,
RPG, Space, Strategy, Other. Terminal door menu now groups games by category with
separator headers. Web lobby filter dropdown loads from DB.

Min access level per game: New min_access_level field on Game (0=all users,
10=regular, 50=power, 100=sysop). Set it in the game add/edit form. Terminal door menu
and web lobby both hide games the user's access level doesn't reach.

v1.0a2.105 — Fix: upgrade runner eventlet RuntimeError (June 2026)

upgrade runner: "Second simultaneous read" RuntimeError from eventlet
during proc.wait() is now caught (non-fatal; upgrade runs in its own scope).

v1.0a2.98 — Fix: DOS door games never connect on headless servers (June 2026)

"DOSBox never connected; closing bridge" on headless servers fixed. Three changes:
(1) Added output=surface to the generated dosbox-x [sdl] config when running
headless (SDL_VIDEODRIVER=dummy, no DISPLAY). Without this, dosbox-x may fail SDL
init silently and exit before running the autoexec — so it never loads BNU.COM or
connects to the TCP nullmodem bridge.
(2) DOSBox stdout/stderr now go to logs/dosbox_<slug>_nodeN.log instead of being
silently discarded via the PTY no-op lambda. Sysops can inspect this file to see any
SDL errors, missing-file messages, or BNU output from a failing door launch.
(3) The exact dosbox command and config file path are now logged at INFO level, visible
in journalctl -u anetbbs-telnet and the web admin log viewer.

v1.0a2.97 — Fix: native Linux door games (RMDoor/DDPlus/FPC) carrier drop (June 2026)

Instant carrier drop on door_native launch fixed. Doors built with the
RMDoor / DDPlus Free Pascal toolkit use fpSend/fpRecv (socket syscalls) for
all I/O on Linux. ANetBBS was writing DOOR32.SYS with CommType=1 (FOSSIL),
CommNum=0fpSend(0, …) on a PTY fd returns ENOTSOCK, which set
FCarrier=false and triggered the hangup handler immediately on startup.
Fix: door_native games now get CommType=2, CommNum=-1 (Mystic Linux STDIO
convention). RMDoor checks ComNum==-1 on UNIX and switches to
Write(StdOut)/ReadKey (PTY-safe). Applies automatically; DOS doors unaffected.
Config for RMDoor doors: Drop File Type=door32.sys, Drop File Path=%P,
Command Line Args=-D%f.

v1.0a2.96 — Web file area: ANSI/CP437 art renders correctly (June 2026)

FILE_ID.DIZ ANSI art now renders as colored HTML in the web file area.
Previously the expanded description showed raw escape codes (□[0;40;37m…).
Added ansi_art Jinja2 filter that calls the existing _ansi_to_html pipeline
directly on already-decoded Unicode strings (no latin-1 round-trip that would
corrupt block-drawing characters above U+00FF). Added strip_ansi filter for
the inline short description. <pre> block uses black background + Courier New
so block art columns align correctly.

v1.0a2.95 — File browser page size fix for 80×25 terminals (June 2026)

File browser page size reduced from 20 to 9. Files with descriptions take
2 lines each; 20 items = 40+ lines, causing items 1-9 to scroll off-screen in a
standard 80×25 terminal. New page size: 9 items worst-case = 24 lines total.

v1.0a2.94 — ZMODEM definitive fix, file desc view, batch downloads (June 2026)

ZMODEM root cause fixed. asyncio.wait_for with a 150ms timeout polled
StreamReader.read() dozens of times per second during transfers. Each cancelled
read left StreamReader._waiter in a pending state; after the transfer the stale
waiter caused RuntimeError in read_line(), which became "Menu action failed".
Replaced with asyncio.ensure_future + asyncio.wait — one persistent read task,
cancelled only when the transfer ends and awaited to completion so _waiter is
guaranteed clean. Fix applied to both send_file and recv_file.

V# — view extended file description. Type V3 in the file browser to see the
full description for file 3. ANSI art (FILE_ID.DIZ with ESC sequences) renders
in CP437; plain-text descriptions word-wrap at 76 characters.

Batch downloads: 1,3,5 or 1-5. Enter a comma list or range in the browser
to queue multiple downloads. Protocol selected once; files transfer sequentially.

Area list count-column alignment fixed. "0. General / Top-level" now pads
its name to 38 chars, matching the numbered rows.

Web UI: expandable full descriptions. File area page shows the first line
inline; files with multi-line or ANSI descriptions get a collapsible "full
description" <pre> block.

v1.0a2.93 — File area: clear pages, ZMODEM fix attempt, FidoNet name cleanup (June 2026)

ZMODEM/YMODEM/XMODEM transfers fixed. -Z was an invalid lrzsz flag;
sz crashed immediately, the terminal sent binary handshake bytes, and they
corrupted the following read_line() call causing "Menu action failed". Fixed
flags: --escape --binary for send, --escape for receive. Added post-transfer
input drain to flush stale binary bytes.

Clear screen on every page. Area list and file browser now clear the terminal
before each draw so content from previous pages doesn't remain visible.

FidoNet "0 !" prefix stripped from area names. Pattern ^\d+\s*!\s* removed
at display time.

v1.0a2.92 — Terminal file areas read from disk (storage_path) (June 2026)

Root cause found for 0 file counts. Terminal queried only FileUpload +
TicFile DB tables. Most file areas store files on disk under storage_path
with no DB records — exactly how the web works. Terminal now uses the same
_scan_area() function the web uses. File counts and browser listings now
match the web view. Web URL for disk-based files now uses /file-areas/ path.

v1.0a2.91 — CP437 encoding fixes and file area 0-count bug (June 2026)

No more ? characters in the terminal. Em dashes, ellipsis, and checkmarks
throughout the terminal UI were not in CP437 and silently became ?. All replaced
with ASCII equivalents (-, >, [OK]). Affects bbs_ui.py, menu_engine.py,
ansi_ui.py, mrc_chat.py, dialout.py, xfer.py, archive_meta.py.

File areas showing 0 files fixed. Files uploaded before is_public column
was added have NULL in that column; filter_by(is_public=True) missed them.
Terminal queries now use isnot(False). Startup migration sets existing NULLs to 1.

v1.0a2.90 — File area: ANSI colors, sysop visibility, FidoNet TicFile display (June 2026)

ANSI colors throughout file area. Area list and file browser now use full color
output: cyan headers, yellow item numbers, white filenames, gray counts, red/magenta
flags for inactive/sysop-only areas.

Sysop sees all file areas. Previously sysop was filtered to is_active=True
same as regular users. Sysop now sees all areas with status flags shown.

TicFile records shown. FidoNet hatched files (TicFile, status='filed') are
now counted in area totals and shown in the file browser merged with FileUpload
records, sorted by date. Downloads use stored_path; falls back to web URL.

lrzsz status notice shown at bottom of area list.