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.89 — Full terminal file area: area browsing + ZMODEM/YMODEM/XMODEM (June 2026)
Terminal file library rewritten. F in the main menu now shows a proper
area browser (numbered list of all file areas with file counts), paginated file
listing (20/page with N/P navigation), and full protocol download/upload.
ZMODEM / YMODEM / XMODEM download. Selecting a file offers Z/Y/X protocol
choice. Server runs sz/sb/sx from lrzsz, piping raw binary through the
terminal connection with --escape for 8-bit-safe telnet transfer. Falls back
to web URL if lrzsz not installed.
ZMODEM / YMODEM / XMODEM upload. U in any area that allows user uploads
runs rz/rb/rx, saves the file, and registers it in the DB. FILE_ID.DIZ
auto-extracted for description if user leaves it blank.
New anetbbs/features/xfer.py — transfer engine. available_protocols(),
send_file(session, path, proto), recv_file(session, proto).
Requirement: apt install lrzsz on the server.
v1.0a2.88 — Terminal: profile editing + file library URL lookup (June 2026)
Terminal profile view wired up. Y. Your Profile now offers inline E=Edit
Profile and W=Change Password options instead of "telnet edit next iteration."
change_password now uses read_password() (masked input) instead of plaintext.
Terminal file library: real filenames + download URL. Files now display their
original_filename instead of the storage UUID hash. Entering a file number
shows the full web download URL. "telnet download next iteration" placeholder removed.
v1.0a2.87 — Remove internet email; calendar user submissions; password-reset admin panel (June 2026)
Internet email / LMTP removed. anetbbs/mail/ module, MAIL_ENABLED config
key, LMTP thread in main.py, email_enabled/email_local_part user columns,
InternetMail DB model, and aiosmtpd/aiosmtplib deps are all removed.
No user-visible features were removed.
Admin > Password Resets panel. New admin page at /admin/password-resets
lists all pending (unused, unexpired) reset tokens with a Copy button for the
URL and a Revoke button. Sysop can now pass the link to the user out-of-band
without grepping the journal. The forgot_password flash message updated to
remove "email integration pending" wording.
Calendar user event submissions. Any logged-in user can now submit a
calendar event. Non-admin submissions land in a pending queue (is_published=False).
New /calendar/pending sysop review page with Approve/Reject buttons.
Admin submissions still go live immediately.
v1.0a2.86 — Fix: door_dos path resolution + headless dosbox-x (June 2026)
door_dos relative executable_path resolved against wrong directory.
_resolve_path() was hardcoded to /home/stingray/anetbbs as the base for
relative paths, so any install at a different path got "executable_path does
not exist: /home/stingray/anetbbs/GAME.EXE". Fixed: base now derived from
__file__ at runtime. Additionally, when working_directory is set, a
relative executable_path is now resolved against it — so sysops can use
executable_path=TW2002.EXE + working_directory=/path/to/TW2002 as expected.
door_dos dosbox-x failed with EPERM/SDL errors on headless servers.
The child process now sets SDL_VIDEODRIVER=dummy and SDL_AUDIODRIVER=dummy
before exec'ing dosbox-x when no DISPLAY is set. Prevents SDL from trying to
open a display or audio device in containers and headless VPS environments.
v1.0a2.85 — Fix: ANSI menu overlays double-rendering stock menus + missing screen clear (June 2026)
Stock menu appeared beneath custom ANSI art. When load_menu_ansi() found
a .ans file the ANSI bytes were written correctly, but the stock menu items
(items for-loop, footer box) were outside the if/else block and ran
unconditionally every time. Fixed by moving all stock-only output inside the
else branch. Affected menus: door_games, game_center, chat, dialout.
Screen not cleared before ANSI write. ANSI art was written over existing
terminal content without first sending an erase sequence, so leftover text
showed through wherever the art didn't fill every cell. Fixed by prepending
\x1b[2J\x1b[H (erase display + cursor home) to the ANSI bytes across all
five ANSI-override slots.
v1.0a2.84 — Fix: custom ANSI menu files not loading (DATA_DIR path bug) (June 2026)
load_menu_ansi() always returned None. It used os.environ.get('DATA_DIR')
which is never written to .env — DATA_DIR is derived from __file__ at
runtime in config.py, not stored in the environment. Fixed by computing the
data directory path from __file__ directly (same logic as config.py).
v1.0a2.83 — Feature: custom ANSI headers for all hard-coded menus/submenus (June 2026)
All hard-coded telnet/SSH/rlogin menus now support optional .ans file
overrides via data/text/menus/<slot>.ans. Supported slots: game_center,
door_games, chat, irc_chat, dialout (plus main and any BbsMenu name
already supported by the menu engine). If the file is absent the built-in
menu renders unchanged.
v1.0a2.82 — Fix: custom ANSI menu screens showing garbled CP437 block characters (June 2026)
Custom ANSI menu .ans files displayed wrong characters (Ü, ß, etc. instead
of block graphics). The menu engine decoded the file as latin-1 (correct and
lossless), but then passed the string to session.write() which re-encodes as
CP437 — a non-symmetric transformation that mapped e.g. ▄ (0xDC) → Ü (0x9A)
and ▀ (0xDF) → ß/β (0xE1). Fixed by writing the ANSI content as raw
latin-1-encoded bytes via session.writer.write() directly, same as
_show_ansi_screen() already did.
v1.0a2.81 — Fix: DOSBox-X detection (revised) + web auto-update tar failure on VPS hosts (June 2026)
DOSBox-X detection fix revised. The v1.0a2.80 fix (SDL_VIDEODRIVER=dummy)
was insufficient — dosbox-x on headless Debian/Ubuntu still hangs during the
subprocess version probe (audio init, not just display). Detection now simply
checks file existence and executable bit. If apt installed the binary, it's
the right arch; exec-format errors surface clearly at door launch time.
Web auto-update "tar extract failed" on VPS hosts fixed. On LXC containers
and some cloud VPS hosts, root cannot chown files to the uid baked into the
tarball (uid 1000 = developer's build machine). run_upgrade.sh now passes
--no-same-owner to tar, letting extracted files take the ownership of the
extracting process instead.
v1.0a2.80 — Fix: duplicate Admin Caller Log entry + DOSBox-X detection on headless servers (June 2026)
Duplicate "Caller Log" entry in Admin menu removed. The Admin dropdown
showed two Caller Log links — a plain-text duplicate and the correct icon
version. The duplicate has been removed.
DOSBox-X detection fixed on headless servers. Running dosbox-x --version
without a display could hang on SDL display init, timing out the 5-second
runability check and making dosbox-x appear absent even when installed via
apt install dosbox-x. The detection subprocess now sets
SDL_VIDEODRIVER=dummy so SDL skips display init. The "No usable DOSBox"
error message now lists the paths that were actually tried, and dosbox-x
is now the first install recommendation.
v1.0a2.79 — Fix: nginx immutable caching + MRC-optional update + web update completion (May 2026)
nginx Cache-Control: immutable caused permanently-cached 404s. The
/static/ nginx block shipped with public, immutable which tells browsers
the URL will never change. When anetbbs/static/mrc/client.js didn't exist
(pre-v1.0a2.76), nginx 404'd the file with the immutable header — browsers
cached that 404 forever and never re-fetched the real file after upgrading.
Fixed: immutable removed, expires 7d removed, replaced with
max-age=86400. update.sh now auto-patches the running nginx config and
reloads nginx.
install.sh nginx block fixed — MRC bridge proxy_pass corrected from
/mrcws to /ws; MRC auth_request block added (prevents unauthenticated
WebSocket connections); client_max_body_size 110m added (nginx's 1m default
was rejecting avatar uploads and large file posts).
update.sh no longer force-starts MRC bridge on servers that don't use
it. Optional services (anetbbs-mrc-bridge, anetbbs-finger) are now only
restarted if they were running before the update. Servers where MRC is
stopped or failed skip MRC bridge restart entirely. systemctl reset-failed
added before each restart to clear start-limit-hit state.
Web update UI completion detection fixed. The "Check for Updates" web
upgrade was polling forever after a successful update: the exit N marker
was written by a Python thread that dies when gunicorn stops mid-update
(Step 3). After the new gunicorn restarted, the UI saw running=false but
no exit N, then polled for 30 minutes and gave up. The UI now also accepts
[upgrade] upgrade to X complete (written by run_upgrade.sh) as a
terminal condition.
v1.0a2.78 — Fix: web MRC full UI (real index.html + client.js wired to Flask) (May 2026)
The web MRC chat UI was rendering from a 406-line stub template rather than
the real 2300-line mrc/web/index.html that runs on the live server. The stub
was missing themes, the user sidebar, macros, mentions panel, reconnection, pipe
color rendering, server selection, mobile layout, and the full slash-command UI.
- Replaced
anetbbs/templates/mrc/index.htmlwith the realmrc/web/index.html - Three minimal Jinja2 injections added:
url_forfor client.js static path,
window.RETURN_TO_BBS_URLfor the back-to-BBS link,{{ suggested_handle }}
pre-fills the handle input with the logged-in username anetbbs/static/mrc/client.jsupdated to the production version from
mrc/web/client.js(reconnection, pipe colors, auto-rejoin)
v1.0a2.77 — Fix: web MRC WebSocket connection error on HTTPS installs (May 2026)
Web MRC chat connected for the sysop (direct HTTP on port 5000) but failed for
all users on HTTPS installs with a WebSocket connection error. Two bugs:
Bug 1 — wrong protocol (ws:// vs wss://): Flask sits behind nginx, so
request.is_secure is always False — Flask only sees plain HTTP from the
proxy. The template was generating ws:// WebSocket URLs on HTTPS sites, which
browsers block as mixed content. Fixed by checking X-Forwarded-Proto: https
header in mrc_web.py.
Bug 2 — nginx proxied to wrong bridge path: The nginx template forwarded
/mrcws to http://127.0.0.1:8080/mrcws, but the bridge only registered its
WebSocket handler at /ws. Added /mrcws as an alias route in
mrc/bridge/main.py and corrected the nginx template to proxy to /ws.
v1.0a2.76 — Fix: web MRC chat "MRCClient is not defined" (May 2026)
anetbbs/static/mrc/client.js was never created, so every web user opening
/mrc/ got a JS ReferenceError: MRCClient is not defined and the chat page
was non-functional.
Added anetbbs/static/mrc/client.js with the full MRCClient WebSocket
class:
- connect() — opens the WebSocket to the bridge, resolves on open
- joinRoom(handle, room) — sends join_room to the bridge
- sendMessage(text) — sends send_message to the current room
- sendServerCommand(cmd) — sends server_cmd (slash commands)
- leaveRoom() — sends leave_room
- disconnect() — tears down the WebSocket cleanly
- 30-second keepalive ping so the bridge doesn't drop idle connections
- Tracks current room/handle from joined / room_changed / handle_changed
events so callers don't need to pass the room on every message
v1.0a2.75 — Raspberry Pi full install guide (May 2026)
Added docs/INSTALL-PI.md — a comprehensive guide for running ANetBBS on
Raspberry Pi 4/5, covering hardware selection, OS recommendations, DDNS setup,
port forwarding, Let's Encrypt SSL, moving data/ to a USB SSD, service
management, door game compatibility, and a troubleshooting section covering
all known Pi-specific issues (SQLite path bug, CSRF cookie, disk space).
Linked from README.md documentation section.