Documentation
Getting Started
Daily Operations
Networks & Mail
Look & Feel
Doors & Games
Web Features
Development
Reference
Other

ANetBBS Changelog

Current release: v1.1.67 (November 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.1.67 — Orphaned door session cleanup fix (November 2026)

A door game session whose underlying process had already exited (for
example, after a service restart following an unrelated crash) could be
left permanently stuck in the "active" state, shown as still connected
with no way for a sysop to disconnect it from the admin UI — the only
automatic recovery was a background sweep that only considers sessions
over an hour old. The sysop-initiated disconnect/kick path now finishes
cleaning up a door session itself whenever it finds the recorded process
already gone, instead of assuming another process will eventually do it.

v1.1.66 — CI security-scan fix (November 2026)

A security scan (bandit) caught a real finding in v1.1.65's own fix, after that release had already shipped: the new helper that gives door games a reliable writable storage location built its fallback path from a hardcoded /tmp literal inline, rather than through the same pattern already used (and already annotated) for the equivalent fallback a few lines above in the same file. Not a functional bug — the fallback path is only ever used when application config isn't loaded yet (effectively test-only), and the resulting path was always correct. This release simply consolidates the fallback through the existing, already-reviewed pattern so the scanner's static analysis (and anyone reading the code) can see that clearly, with no behavior change for live installs.

v1.1.65 — Door-game SSH/telnet write-path fix, plus a follow-up audit (November 2026)

Door games launched over SSH, telnet, or rlogin could fail to save
player progress, scores, or other persistent data — or in some cases
fail to launch at all — even though the same game worked fine through
the web interface. The cause was a mismatch between what the two
access methods are allowed to write to on disk: the SSH/telnet/rlogin
service only grants write access to the application's own data and
log directories, not to a door's own install directory. Native door
games now get a dedicated, always-writable storage location
automatically, with no sysop configuration needed. DOS- and
DOSEMU-emulated door games that aren't installed inside the writable
data directory now report a clear, actionable error at launch instead
of failing silently or unpredictably; see docs/14-door-games.md for
the recommended install location if you see this error.

Also: a routine follow-up security/performance audit hardened a
handful of other areas, including network message-size limits and the
sandboxing applied to an optional add-on service. No sysop action is
required; applying this release picks these up automatically.

v1.1.64 — CI security-scan fix (October 2026)

A security scan (bandit) caught a real finding in v1.1.63's own fix, after that release had already shipped: the code that lets both the web process and the detached root upgrade unit write to the same progress-log file loosened that file's permissions to world-writable (0o666) to guarantee both sides could always write to it. That's unnecessary — root already bypasses standard file permission checks regardless of mode, so the only direction that genuinely needed defending (the web process needing to reclaim a file a past root-owned write left behind) was already covered by a separate remove-and-recreate fallback. A world-writable progress log is needless exposure on a box with other local accounts, so the loosening step was simply removed rather than suppressed. No sysop-visible behavior changes; applying this release corrects the file's permissions going forward. The exposure window was a transient progress log (install-step text only, no credentials or secrets ever written to it), not a credential or data leak.

v1.1.63 — Third auto-update outage, plus a preflight false warning (October 2026)

A third real outage, found live while verifying v1.1.62's fix: with the sudoers problem genuinely resolved, clicking the auto-update button failed with a new error — systemd exit 209/STDOUT, "Failed to set up standard output: Permission denied."

Root cause: anetbbs-web.service runs with PrivateTmp=yes, which gives it its own private, isolated view of /tmp. The upgrade progress log used to live at a fixed /tmp/anetbbs_upgrade.log — a path the web process could create/truncate fine in its own private /tmp, but which the separately-launched, detached systemd unit that does the real upgrade work (see v1.1.62's fix) resolves against the REAL, system-wide /tmp instead. Same literal path, two different, inconsistent views of it depending on which process looked at it. Fixed by moving the log to <install dir>/data/anetbbs_upgrade.log — a real, non-virtualized directory every process already agrees on identically, regardless of any unit's PrivateTmp setting.

Also found live immediately after applying v1.1.62, and fixed in this same release: the Preflight page's "sudoers grant present" check flagged /etc/sudoers.d/anetbbs's own freshly-refreshed, correct 0440 permissions as "too open," then suggested fixing it by setting it to the exact same 0440 it had just rejected. The check's permission mask covered the group-read bit that 0440 itself sets (and that install.sh/update.sh always apply), so a correctly-permissioned file could never pass it. Fixed by narrowing the mask to only flag group write/execute or any access at all for "other" — the actual security-relevant cases — leaving group-read, which is expected and correct, alone.

v1.1.62 — Second auto-update outage, structural fix (October 2026)

If your auto-update button fails with sudo: a password is required, apply this release manually over SSH once (sudo bash update.sh from inside the extracted tarball) — the button cannot fix this on its own, but this is also the last time it should ever need manual help for this specific reason.

A second real outage, separate from the one v1.1.59 fixed: on a box where a past upgrade's sudoers refresh had silently failed while the rest of that upgrade still succeeded, the auto-update button was left permanently stuck — the running code built a command the stale sudoers grant no longer matched, and the button is the one feature that can't repair a sudoers problem by itself.

Root cause: v1.1.59's fix moved the mechanism for surviving anetbbs-web's own restart (needed because the upgrade stops and restarts the web service partway through itself) into the sudo-gated command's exact flags. Every time that mechanism needed to change, every existing install's sudoers grant had to change with it, via a successful update.sh run — and when that one refresh step failed quietly on one box, the mismatch had no way to self-correct.

Fixed by moving that mechanism out of the sudo-gated command entirely. sudoers now only ever grants one permanently simple thing — the upgrade wrapper script and its plain arguments — and the wrapper itself, once it already has root, detaches into its own systemd unit internally with no further sudo call involved. This can't drift out of sync again regardless of how the detachment mechanism itself needs to evolve in the future, because changing it never again requires every existing install's sudoers file to be rewritten in lockstep.

v1.1.61 — Follow-up audit pass (October 2026)

Following the string of sudo-escalation and auto-update fixes shipped
over the past several releases (v1.1.54 through v1.1.60), ran a
focused audit over everything that changed in that window to catch
anything those fixes missed or introduced. Six issues found and
fixed, each with new regression test coverage added:

  • The pre-launch system check (Admin → Preflight) could report the
    auto-update sudo grant as healthy while the real upgrade path
    stayed broken — it was testing the wrong one of two sudoers grants.
    Fixed by having both the real upgrade code and the preflight check
    build the exact same command from one shared source, so they can't
    drift apart again.
  • The pre-login anti-bot challenge could, in a narrow case, be
    satisfied by ordinary terminal-negotiation traffic alone, with no
    real keystroke involved. Fixed by routing it through the same
    telnet-negotiation-aware read path already used elsewhere in the
    terminal stack.
  • A scheduled stale-session cleanup task could, if one specific row
    hit an unexpected error, skip cleaning up every other unrelated
    stale row for that run too. Fixed by isolating each row's handling.
  • The admin BinkP-node creation form's duplicate-address check could
    miss that two differently-formatted address strings name the same
    real FTN address, allowing an accidental duplicate node entry.
    Fixed to use the same address normalization already applied
    elsewhere.
  • A reference systemd unit file (a hand-maintained example, not used
    directly by the installer/updater) still showed an older setting
    without the newer explanatory guidance added elsewhere — corrected
    to match.
  • A new automated test added alongside last release's auto-update fix
    only checked that the right pieces existed, not that they were in
    the right order. Tightened so it actually catches the class of
    mistake it was meant to catch.

Also corrected two documentation pages that still described the old,
now-removed --scope-based upgrade mechanism from before v1.1.59.

No sysop action is required beyond the normal update — this release
doesn't change the recommended deploy path established in v1.1.58/
v1.1.59.

v1.1.60 — CI-only test fix (October 2026)

v1.1.59's new test coverage for the auto-update button fix included a
check that cross-references deploy/sudoers.anetbbs against the
upgrade command the web button builds. That file is deliberately
excluded from the Docker image (.dockerignore — it's a bare-metal-only
concern), so the Docker-build CI workflow's in-container test run failed
with a missing-file error. Fixed by skipping that specific check when
the file isn't present, matching the same exclusion already documented
in .dockerignore. No functional/runtime code changed; this release
exists purely to keep the Docker-build CI check accurate.

v1.1.59 — Fix the web auto-update button itself (October 2026)

v1.1.58 fixed the underlying sudo privilege-escalation outage, but the
web-triggered "Check for Updates → Install" button had a second, separate
bug: it could pass every preflight check and still silently die partway
through an upgrade, leaving the box on the old version with no error
shown. Manually running sudo bash update.sh over SSH was never
affected by this — only the browser button was.

Root cause: the button launched the privileged upgrade wrapper via
systemd-run --scope, intended to let the wrapper escape the web
service's own process group so that update.sh's own restart of the web
service couldn't kill the upgrade along with it. Per systemd-run's own
documentation, --scope actually makes systemd-run itself the parent
process of the wrapper — so the wrapper remained a descendant of the
same process tree it was supposed to escape, and could still be killed
as collateral damage when the web service stopped.

Fixed by launching the wrapper as a fully independent transient
service instead (the default systemd-run mode, not --scope), which
is never a descendant of the calling process at all, with --wait
restoring completion tracking without re-introducing that coupling.
Applying this release through the web button itself should work now
that v1.1.58 fixed the underlying sudo issue, but carries a small
chance of hitting the exact bug this release fixes on the way in — a
manual sudo bash update.sh over SSH sidesteps that risk entirely and
is the safer way to pick up this specific release.

v1.1.58 — Correct fix for the sudo escalation outage (October 2026)

This supersedes v1.1.56 and v1.1.57's fix for the sudo "no new
privileges" error — both of those releases shipped a fix that did not
actually work.
If your Install/Restart/Backup buttons are currently
failing with that error, this release still cannot be applied through
the web upgrade button itself, for the same reason as before. Apply it
manually: download the tarball and run sudo bash update.sh from an
SSH session.

Background: v1.1.56 added an explicit NoNewPrivileges=no to the web
service's systemd unit, believing that would override the implicit
force several other hardening directives trigger. It didn't — confirmed
on live production by restarting the service and reading
/proc/<pid>/status, which still showed the kernel's privilege-block
flag set despite the override. v1.1.57 then removed the directives that
looked like the obvious seccomp-backed culprits, which also didn't
fully fix it. A real bisection against the live production unit — each
directive tested alone, through a real restart and a real /proc read,
not reasoned about — found that ProtectKernelTunables=,
ProtectKernelModules=, ProtectKernelLogs=, ProtectClock=, and
ProtectHostname= each independently force the flag too, since each
blocks a specific kernel syscall via its own seccomp filter layered on
top of its mount/capability restriction — not something the systemd
documentation makes obvious up front. Only PrivateTmp=, ProtectHome=,
ProtectSystem=, ReadWritePaths=, and ProtectControlGroups= are
actually safe to keep on this one unit, which needs to retain the
ability to escalate privileges via sudo. This release removes the five
directives that were still silently breaking it.

  • Fixed the web service's systemd unit to only include sandboxing
    directives confirmed safe for a unit that also needs sudo escalation.
    The updater self-heals any existing install automatically on the next
    manual update.
  • Corrected the admin Preflight Checklist page's guidance text, which
    previously suggested a fix (sudo systemctl edit anetbbs-web) that
    does not work on its own if a seccomp-backed directive is present
    elsewhere on the unit.

v1.1.57 — File Areas column alignment fix (October 2026)

  • Fixed the web File Areas listing rendering each category's table
    with independently auto-sized columns, so the Tag/Name/Files/Total
    Size/Description columns landed at different horizontal positions
    from one category group to the next. Columns now use fixed,
    consistent widths across every group, on every theme.

v1.1.56 — Fix a real sudo escalation outage for the Service Control Center (October 2026)

If the Service Control Center's Install/Restart/Backup buttons are
currently failing with a sudo "no new privileges" error, this release
cannot be applied through the web upgrade button itself
— that
button relies on the exact privilege escalation this release fixes, so
it's blocked the same way the other buttons are. Apply this one
update manually instead: download the release tarball and run
sudo bash update.sh from an SSH session. After that one manual
update, the web-based Install/Restart/Backup buttons work normally
again, including for every future update.

  • Fixed a real production outage where the Service Control Center's
    web-triggered restart, upgrade, and backup actions could silently
    stop working on an install with systemd sandboxing hardening
    applied to the web service, failing with a "no new privileges"
    sudo error. Several of the hardening directives involved implicitly
    force a systemd setting that blocks privilege escalation unless
    explicitly overridden, which earlier releases never accounted for.
  • The updater now automatically applies this fix to any existing
    install the next time it runs, with no manual steps required beyond
    the one-time manual update described above for an install already
    affected.
  • Fixed the admin Preflight Checklist page's own sudo-escalation
    check, which could previously report a false "ok" in exactly this
    situation; it now asks systemd directly for the effective
    configuration instead of inspecting the unit file's raw text.

v1.1.55 — Call log and activity log audit (October 2026)

  • Fixed web caller sessions permanently recording a zero-second
    duration unless the caller used the explicit Logout link. Most web
    sessions simply close the browser tab, which never recorded a real
    duration before; duration is now backfilled from the existing daily
    stale-session cleanup job once an abandoned session is reaped, via a
    new correlation between the presence-tracking table and the caller
    log. Terminal (telnet/SSH/rlogin) sessions already recorded duration
    correctly and are unaffected.
  • Closed a wide gap in the admin caller log drill-down (introduced in
    a previous release to show what a caller did during their session),
    which was sparsely populated because most areas of the BBS never
    logged an activity entry at all. Added activity logging across web
    echomail (viewing, posting, replying, deleting, netmail), web
    message boards (viewing, posting, replying), private message
    sending, QWK hub pack transfers, and fifteen hub administration
    actions (node/identity management, polls, approvals).
  • Added the same activity logging to the terminal client (both ANSI
    and PETSCII) for echomail composing, inline replying, and private
    message sending, including a fourth, previously unlogged local
    compose path reachable only from inside the message reader.
  • Fixed existing file-download activity logging, on both the terminal
    client and the web file areas, so it correctly correlates to its
    caller log session and actually appears in the drill-down. The main
    web file-area download route had never logged a download at all
    before this fix.
  • Added a shared activity-logging helper for web routes, so future web
    features log activity consistently rather than each one reinventing
    (and potentially breaking) the pattern.
  • Fixed the online updater refusing to update at all on a system
    without the sqlite3 command-line package installed. The updater's
    pre-update database backup step now falls back to Python's built-in
    SQLite support — which uses the same safe, consistent backup method
    as the command-line tool, not a plain file copy — when that package
    isn't present, instead of aborting the update entirely.

v1.1.54 — Telnet negotiation hardening and installer fix (October 2026)

  • Hardened the telnet protocol negotiation handler against malformed
    or oversized input: a subnegotiation sequence is now bounded to a
    fixed maximum size before being abandoned, rather than being allowed
    to grow without limit while waiting for its terminator. Normal
    terminal-resize, terminal-type, and MUD-protocol negotiation are
    unaffected.
  • Fixed single-keystroke menu prompts (the main "Choice:" menus and
    lightbar-style navigation screens) so that telnet protocol
    negotiation — most commonly a terminal-resize notification — can no
    longer be misread as a real keystroke. Previously, a resize arriving
    while one of these menus was on screen could occasionally be
    misinterpreted as a menu selection; it is now handled the same way
    normal text-entry prompts already handled it, with no effect on
    real keystrokes, arrow-key navigation, or terminal resizing itself.
  • Fixed the installer/updater's systemd unit generation so that an
    install located under a user's home directory no longer risks
    failing to start after an update or a fresh install — a sandboxing
    directive meant to restrict filesystem access didn't account for
    that install location, and a first revision of this fix did not yet
    work correctly in every case. This now also applies automatically to
    an already-existing install that has only ever been upgraded, not
    just a fresh install. Installs using the default system location are
    unaffected either way.
  • Improved the updater's database backup step so that it always
    verifies a backup's integrity before reporting success, with no
    fallback to a less safe copy method if the normal one is ever
    unavailable.
  • Fixed the web file areas listing so that categories are displayed in
    their configured order, rather than an order influenced by which
    area happened to be listed first.

v1.1.53 — BinkP hub-recognition fix for FTN point notation (October 2026)

  • Fixed a bug in the inbound BinkP listener's peer-recognition logic:
    an incoming caller's announced address was matched against a
    network's configured hub address using a plain string comparison,
    with no awareness that FTN point notation allows the same address to
    be written either with an explicit point (e.g. .0) or with the
    point omitted. A hub announcing one form against a hub address
    configured in the other form was treated as a completely
    unrecognized caller — its echomail silently dropped on every
    connection attempt, even though the password would have verified
    correctly. Both forms are now recognized as equivalent in both
    directions. A related, non-blocking false-positive warning in the
    outbound client's own address cross-check is fixed the same way.