ANetBBS Changelog

Current release: v1.0.87 (September 2026). This file covers v1.0.0
onward, which follows standard semantic versioning — patch releases are
v1.0.1, v1.0.2, and so on. The full internal beta build-number
history (v1.0a1.1 through v1.0b2.239) that got the project to this
release is preserved in
CHANGELOG-beta.md.

v1.0.87 — Session-hang fix / Game Center prompt fix (September 2026)

Fixed a bug where a stalled client connection could leave a session
frozen indefinitely with no recovery. Also fixed the Game Center's
page-picker prompt showing page-navigation options on game lists that
only have one page.

v1.0.86 — MRC chat disconnect / door-cleanup fixes (September 2026)

Fixed a bug where a dropped MRC chat connection could spike CPU usage,
plus a related door-cleanup error when running multiple multiplexors
/ bridges.

v1.0.85 — Fixed the new "Auto-abort on Inactivity" checkbox being invisible for most door types (September 2026)

A real live bug, found right after v1.0.84 shipped: the new checkbox
was placed in the Admin → Games edit form right next to the (genuinely
dosemu2-only) FOSSIL driver setting — inside a per-game-type section
that the page's own JavaScript hides and disables for every game
type except dosemu2. A door_native game (confirmed live: a real
uMRC door) never showed the checkbox at all, and even checked, a
disabled input never submits with the form. Moved it into the form's
common section every game type shares, and added a regression test
that specifically checks the field's position in the rendered page
relative to the per-game-type sections — the value-round-trip tests
added in v1.0.84 POST form data directly and never would have caught
this, since the hide/disable behavior only matters to a real browser.

v1.0.84 — Per-door opt-out for the idle-timeout auto-abort (September 2026)

A real live report: a sysop sitting idle in a chat-type door (an MRC
client) got auto-kicked for inactivity — exactly the behavior every
OTHER door legitimately wants (an abandoned session shouldn't tie up a
node forever), but wrong for a door whose entire point is sitting
there reading/waiting without typing anything.

  • New "Auto-abort on Inactivity" toggle per door (Admin → Games,
    and anetbbs-cfg's Games section), on by default so every existing
    door keeps today's exact behavior. Turn it off for chat-type doors
    (MRC clients, IRC bridges, and similar) where idle time is normal.
  • Closed a pre-existing drift while touching this: anetbbs-cfg's
    drop-file-type choices were missing chain.txt, sfdoors.dat, and
    the new GAP-style DOOR.SYS option the web admin already offered.

v1.0.83 — Door dropfiles report real time budgets, plus a GAP-style DOOR.SYS option (September 2026)

A full, field-by-field audit of every dropfile format ANetBBS
generates, verified directly against the real OpenDoors source
(github.com/RealDeuce/OpenDoors), prompted by a live report of a door
saying "no time left" immediately on launch.

  • Every door dropfile now reports a real, per-user remaining-time
    value
    instead of a flat, hardcoded 60 minutes. Admins and accounts
    with no configured time budget get an effectively-unlimited value;
    accounts with a real UserTimeBudget get their actual remaining
    session/daily time, mirroring the same calculation the terminal
    session's own time-limit enforcement already uses. Previously every
    door launch reported exactly one hour left, regardless of the
    account's real access — confirmed live by a sysop testing as an
    admin account (which has no time limit at all) and still seeing
    every door report exactly 60 minutes, every time.
  • New GAP-style DOOR.SYS option (Admin → Games, Drop File Type).
    DOOR.SYS was never a single standardized format — the existing
    DOOR.SYS generator was built and verified against real TW2002
    validation warnings (confirmed live: LORD and TradeWars both launch
    correctly against it), but a different door reading DOOR.SYS via the
    other real convention read the existing generator's line 19 — the
    literal string "GR" in that convention — as its time-remaining
    field, and refused to launch with "no time left". Rather than rewrite
    the existing, working generator and risk breaking LORD/TradeWars, a
    second, independently-selectable DOOR.SYS generator now implements
    that other real convention, verified line-by-line against the actual
    OpenDoors source.
  • BBSDEV.DRP now reports a real logoff deadline. Its time-remaining
    field is a timestamp, not a relative minute count — this format
    accepted a minutes_remaining value but never actually used it,
    always leaving that field blank (a valid "no time limit" per spec,
    but not connected to the user's real access). It's now converted to
    a real deadline.
  • CHAIN.TXT, DORINFO1.DEF, DOOR32.SYS, and SFDOORS.DAT were all
    re-verified field-by-field against the real OpenDoors source during
    this audit and confirmed already correct — no changes needed.

v1.0.82 — door menu items now accept a Game slug, not just its id (September 2026)

A sysop building a custom Chat Systems menu item flagged that a
door action's action_args needed a numeric Game.id, which isn't
shown anywhere in either admin UI — only buried in the edit-page URL.
The slug, by contrast, is a labeled field right on that same page.
door items now accept either an id (existing menu items keep working
unchanged) or a slug — see docs/03-menus.md.

v1.0.81 — Fixed data/mods/text/menus/ art overrides for database-driven menus (September 2026)

A second real live bug, found right after v1.0.80: a sysop's existing
data/mods/text/menus/chat.ans custom art (and its 132-column
widescreen variant) stopped rendering once chat_systems became a
real, admin-editable menu. Root cause: the menu-engine's file-based art
lookup for database-driven menus only ever checked the older
data/text/menus/ location — data/mods/text/menus/, the documented,
update-safe location every other override in the project uses, was
never checked there at all. Fixed so it's checked first, same
precedence as everywhere else. Note the filename now matches the
menu's own name (chat_systems.ans/chat_systems132.ans), not the
old chat.ans — see
docs/35-mods-directory.md.

v1.0.80 — Fixed the new picker menus not backfilling on an upgraded install (September 2026)

A real live bug, confirmed on an upgraded Pi install right after
v1.0.79: the new chat_systems/game_center/sysop_tools menus (see
v1.0.79 below) never appeared in Admin → BBS Menus or anetbbs-cfg.
Root cause: the default-menu seeding function's upgrade path was only
ever designed to add new items to an already-existing menu (e.g. a
new hotkey added to main in a later release) — it had never needed
to handle a whole new top-level menu appearing in a release before,
so it silently skipped creating one on any install that already had
main seeded. A fresh install was unaffected (a different code path
always creates every default menu from scratch). Fixed so an upgrading
install creates any default menu that's missing entirely, not just new
items on ones that already exist.

v1.0.79 — Admin/anetbbs-cfg-editable Chat/Game Center/Sysop Tools menus, JAM message base export (September 2026)

  • Chat Systems, Game Center, and Sysop Tools are now admin-editable
    menus
    , the same way the main menu already was — add, remove,
    reorder, and relabel options from Admin → BBS Menus or
    anetbbs-cfg's BBS Menus section, with no code required, including
    adding a genuinely new option (any existing action type — exec,
    ansi, door, and so on) rather than just the bundled defaults.
    The list of available action types is now defined once and shared by
    both the web admin and anetbbs-cfg, closing a gap where the cfg
    tool's own list had already drifted and was missing several action
    types the web UI offered. data/mods/core/ remains available
    underneath for genuinely custom control flow a menu item can't
    express — see docs/35-mods-directory.md
    and docs/03-menus.md.
  • JAM message base export. A per-echo-area opt-in toggle
    regenerates a real .jhr/.jdt/.jdx JAM message base on a
    schedule, so classic JAM-API door games can read ANetBBS's echomail
    directly. Read-only and one-way — the SQL database stays the sole
    source of truth; nothing a door writes into the exported files feeds
    back in. See docs/34-jam-export.md.

v1.0.78 — SSH key login, translations, achievements, door-dev tooling, TUI accessibility (September 2026)

A feature round following up the ninth audit's competitive gap
analysis, plus two live accessibility bugs reported and fixed during
the same window.

  • SSH public-key login. Register a public key at Profile →
    Security → SSH Keys and log in over SSH with no password prompt at
    all. Purely additive — password login is completely unchanged for
    every account that doesn't register a key.
  • Web UI translations. The terminal menu system's existing
    translation mechanism now also covers the web UI (a new t()
    template helper), with an admin page (and a matching anetbbs-cfg
    section) to manage overrides. Spanish, German, and Portuguese are
    seeded by default for the navigation bar and a few other high-
    traffic labels; more template coverage can be added incrementally.
  • Achievements documented and expanded. The existing badge system
    (previously undocumented) now has a README section, and grew from
    10 to 18 rules — new ones tied to Game Center high scores, wiki
    edits, file uploads, and door-game variety.
  • anetbbs-export-users — the export-direction counterpart to
    anetbbs-import-users, for getting user account data (never
    passwords) out to a portable CSV/JSON file.
  • Windows install guide (docs/INSTALL-WINDOWS.md) covering the
    WSL2 and Docker Desktop paths.
  • builtin_python door-writing tutorial and a new scaffolding
    script (anetbbs-scaffold-door) that generates a starter door
    module plus a draft (inactive-until-reviewed) catalog entry.
  • anetbbs-cfg accessibility fixes, both from a live report by a
    screen-reader user: long help text in edit forms was being silently
    cut off at the terminal width instead of wrapping (affected several
    sections, not just the one reported); and every edit form now has
    explicit on-screen [ Save ]/[ Cancel ] rows navigable with the
    same arrow keys as everything else, alongside the existing F2/Esc
    shortcuts, since a function-key sequence not matching what a given
    terminal actually sends could silently discard unsaved edits with no
    warning.
  • Fixed a stale "(beta)" label and an outdated log-path reference in
    the GitHub bug-report issue template.
  • Closed a real correctness gap in the terminal/SSH login layer's
    database-connection handling, in the same class as a similar fix
    earlier this release cycle — dormant in normal production use (a
    deployed process only ever opens one database connection at
    startup), surfaced while testing the SSH key-login feature above.

v1.0.77 — Ninth audit pass: every script, full docs sweep, cfg/monitor hardening review (September 2026)

A ninth audit pass, widening scope to ground not systematically covered
by the eight prior rounds: every standalone maintenance script in
tools/, the shell/Docker deployment layer, a complete accuracy sweep
of every file in docs/, and a closing vulnerability-class review of
the two standalone in-package tools (the terminal config editor and
node monitor) that had seen comparatively less direct attention than
the web blueprints. Same severity-tiered approach as the eight prior
rounds; this entry again omits vulnerability specifics in the interest
of responsible disclosure. Every code fix has a dedicated regression
test.

  • Added integrity verification (checksum compare against known-good
    values, with automatic deletion on mismatch) to the DOS-game-support
    asset downloader, which previously wrote whatever a remote host
    served with no verification at all.
  • Extended the automated code-scan gate (bandit + pyflakes) to cover
    tools/, which had been silently out of scope since the scanning
    step was first added — closing a real, previously unnoticed coverage
    gap rather than just fixing what the wider scan happened to find.
  • Hardened a credential-rotation maintenance script to prompt for its
    password interactively instead of accepting it as a command-line
    argument, and removed a real credential that had been sitting in the
    script's own usage example.
  • Corrected numerous stale references across docs/ and deploy/ to
    the project's pre-v1.0a2.67 process-management approach, which was
    deliberately replaced for Python 3.12 compatibility; a standalone
    entry-point file left over from that era is now clearly marked
    deprecated rather than silently unreferenced.
  • Closed a real correctness bug in a database-configuration class
    affecting any process that constructs more than one app instance
    with a different database in sequence — the second and later
    instances silently reused the first instance's database. Dormant in
    normal production use (a deployed process only ever constructs one
    app instance), but real, and the direct cause of a diagnostic script
    failing outright.
  • Reviewed the terminal config-editor and node-monitor tools against
    the established recurring-vulnerability checklist (privilege
    escalation via a shared launch chokepoint, credential handling, path
    safety, injection) — confirmed already correctly hardened from a
    prior round's fix, no new issues found.
  • Corrected a documentation/code mismatch in a rate-limit docstring, a
    stale feature-count in the themes documentation, and a broken
    version-pinned download URL in the Raspberry Pi install guide, among
    numerous smaller documentation accuracy fixes found in the full
    docs/ sweep.

v1.0.76 — Eighth audit pass: security infrastructure, performance, docs (September 2026)

An eighth audit pass, this time focused less on finding one-off code
bugs (the last several rounds already covered a lot of that ground) and
more on infrastructure-level questions: were there whole categories of
security or performance hardening genuinely missing, not just individual
bugs in the code that's there. Same severity-tiered approach as the
seven prior rounds; this entry again omits vulnerability specifics in
the interest of responsible disclosure. Every finding has a dedicated
regression test.

  • Added site-wide security response headers (Content-Security-Policy,
    X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
    Permissions-Policy) — previously absent entirely. The public embed
    page is deliberately exempted from the framing restriction, and HSTS
    is only sent over an actual HTTPS connection so it can never lock out
    an install that hasn't set up TLS yet.
  • Reviewed session cookie hardening, rate-limiting, and auto-ban
    coverage end to end — confirmed already correctly configured, no
    changes needed.
  • Closed a couple more real database/query gaps found in a systematic
    audit of every foreign-key column in the schema: one more missing
    index on a routinely-queried per-user column, and an eager-loading
    fix for a file listing that was issuing one extra query per row.
  • Added response compression and static-asset cache headers where they
    were missing, at both the reference reverse-proxy layer and as an
    application-level fallback.
  • Reviewed the deliberate choice to auto-apply schema changes on every
    app start rather than use a manual migration-command workflow, and
    confirmed it's the right fit for this project's self-hosted-sysop
    deployment model; documented the pattern more completely for future
    contributors.
  • Reviewed whether to add persistent (Redis/DB-backed) rate limiting or
    two-factor authentication — decided against both for now, for
    documented reasons, with the existing limitations already accurately
    described in docs/SECURITY.md.
  • Fixed a real, live bug on the Release Downloads page: clicking the
    checksum button on an already-generated checksum sidecar file hashed
    the sidecar itself instead of refusing, letting repeated clicks pile
    up an unbounded chain of nested checksum files. Checksum/signature
    sidecars no longer appear as their own downloadable entry, and the
    checksum endpoint now refuses to checksum a checksum file outright.

v1.0.75 — Seventh audit pass: templates, models, deploy hardening, CI coverage (September 2026)

A seventh audit pass, broader in scope than the two prior rounds:
Jinja templates across the whole app, the core data model and app
factory, the reference systemd deployment units, and CI's dependency
vulnerability scanning coverage. Same severity-tiered approach as the
six prior rounds (v1.0.38, v1.0.39, v1.0.48, v1.0.66, v1.0.71, v1.0.72);
this entry again omits vulnerability specifics in the interest of
responsible disclosure. Every finding has a dedicated regression test.

  • Hardened several sysop- or peer-suppliable URL fields rendered as
    clickable links across the profile, RSS, peer-directory,
    ANSI/postcard editor, and IRC web-client pages against
    javascript:-URI and script-injection payloads — one of these was
    reachable by any registered user against a reviewing sysop's own
    browser session.
  • Capped the size of an inbound message the MRC web chat client will
    parse, matching the same cap already applied server-side to the
    BinkP network listener.
  • Closed a gap where a revoked account's already-open web session kept
    access until its next login, instead of losing access immediately —
    matching the existing behavior already in place for a banned or
    locked account.
  • Fixed a missing foreign-key constraint on a message-tracking column,
    and added missing database indexes on two columns queried on every
    leaderboard view and door-game launch attempt, with an automatic
    backfill for existing installs.
  • Extended CI's dependency vulnerability scanning to cover every
    requirements file this project ships, not just the runtime one —
    closing a gap where the Docker-image and developer-tooling
    dependency sets were unaudited.
  • Brought the reference deploy/*.service systemd unit files (used as
    install documentation; the real install/update scripts always
    generate the actual units directly) back in sync with the
    sandboxing directives those scripts actually apply, and added the
    one previously undocumented unit that was missing entirely.

v1.0.74 — CI-only fix: Node-dependent test didn't skip without Node (September 2026)

Fixed a genuine Docker build CI failure on v1.0.73: a new regression
test added during the sixth audit pass (verifying a generated
Synchronet-compat script is syntactically valid JavaScript) shelled
out to node --check unconditionally, unlike every other Node-
dependent test in the same file, which already skip gracefully when a
real Node.js binary isn't present. The CI Docker image doesn't (and
shouldn't need to) have Node.js installed, so the test failed there
with a plain "no such file" error rather than skipping. Brought into
line with the existing convention; the test's other half (a byte-level
check that needs no external tool at all) already fully verifies the
real fix and is unaffected. No runtime behavior change, no functional
bug.

v1.0.73 — CI-only fix: unused import (September 2026)

Fixed a genuine code-scan CI failure on v1.0.72: dead-code removal in
anetbbs/games/dos_bridge.py during the sixth audit pass deleted the
code that used its os import but left the import itself behind.
Caught by pyflakes in CI, not by the local test suite (pyflakes/bandit
aren't part of the local pytest-based verification loop) — no runtime
behavior change, no functional bug.