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.148 — Inbound and outbound BinkP still weren't confirming delivery to the hub (July 2026)
Reported live: even after v1.0b2.147's fix, the same Fidonet hub kept resending its entire backlog every poll — the file list never shrank, even across sessions ANetBBS logged as clean successes in both directions (hub polling in, and ANetBBS polling out).
- FIX: ANetBBS only ever sent one BinkP end-of-batch signal (M_EOB) per session, sent before any files changed hands. Real file activity that followed voided it, and nothing ever sent a second, post-transfer confirmation — so a peer that never explicitly replies with its own end-of-batch signal (confirmed live: this hub doesn't) was left with no way to know the transfer was actually done, no matter how many files were individually acknowledged. Now sent unconditionally after every transfer completes, in both the inbound listener and the outbound poller.
- FIX: the inbound BinkP listener's address announcement never checked the per-network domain override (
ftn_domain), always deriving one from the network's display name instead — a long name like "ANotherNetwork" always announced as@anothernregardless of any override already configured.
8 new regression tests, each verified to fail without its corresponding fix.
v1.0b2.147 — Inbound BinkP sessions leaked a database connection every time (July 2026)
Reported live: a Fidonet hub kept resending the same packets dozens of times, even though ANetBBS was actually receiving and acknowledging every one of them successfully. A second sysop's own install hit the identical symptom.
- FIX: every inbound BinkP connection opened its own database connection and never closed it — twice per connection, in fact. The leak accumulated over hours of polling from multiple networks, occasionally causing just enough delay to trip a peer's own session timeout, even though our side finished and logged the poll as a success. Connections are now explicitly closed after every session, success or failure.
3 new regression tests, each verified to fail without the fix.
v1.0b2.146 — Netmail flood was arriving through a second, unpatched path (July 2026)
Reported live: the same "Area Management Request"/"List of Available Areas" flood kept appearing every ~10 minutes even after v1.0b2.145 shipped, confirmed via direct database inspection.
- FIX: the peer polls into ANetBBS's inbound listener independently of — and around the same cadence as — the outbound poll of the same hub. That's a separate code path (
binkp_server.py) from the one fixed in v1.0b2.143/145 (poller.py, used when ANetBBS dials out), and it never had any content-based dedup fallback at all — only the exact-MSGID check, which the peer's fresh-MSGID-per-resend behavior always defeats. Both directions now share the same sender+subject+network dedup approach.
3 new regression tests, each verified to fail without the fix.
v1.0b2.145 — Web UI freezing for every user, and the netmail flood fix from v1.0b2.143 wasn't actually catching everything (July 2026)
Reported live, urgent: the entire web UI started freezing solid (unresponsive tab, not even a slow page) for minutes at a time, shortly after v1.0b2.143 shipped.
- FIX:
NetmailMessage.received_athad no database index, but v1.0b2.143's new content-based netmail dedup check filters on it. This app runs under eventlet, which does not make SQLite non-blocking — so an unindexed, growing scan on every inbound netmail blocked the entire process for every user on every page until it finished. Added the missing index, plus an automatic backfill so already-installed databases pick it up on next startup with no manual step required. - FIX: v1.0b2.143's dedup check also required an exact match on message body — confirmed live this wasn't sufficient (the same "Area Management Request"/"List of Available Areas" netmail kept creating new entries every ~10 minutes even with that fix deployed), which was also what fed the freeze above by continuously growing the unindexed table. Now matches on sender+subject+network within the dedup window instead, without requiring the body to be byte-identical.
If you already applied the manual CREATE INDEX workaround, this release's migration is idempotent and won't error on top of it.
9 new regression tests, each verified to fail without its corresponding fix.
v1.0b2.144 — Inbound BinkP: crashed sessions left zero diagnostic trace (July 2026)
Reported live: a peer sysop's own binkd log showed our inbound listener closing the connection abruptly, mid-transfer, with none of his files ever getting an acknowledgment from our side — but there was no corresponding ANetBBS-side record of what happened, anywhere a sysop could see it.
- FIX: an inbound BinkP session only ever wrote a Poll Log entry on successful completion — that write happened as the very last step of handling a connection, so any exception before it (a network error, a bug, anything going wrong mid-transfer) meant the entire session vanished with no trace beyond a bare stack trace in the raw application log. A crashed session now writes an error-status Poll Log entry with the full frame-by-frame transcript captured up to the failure point, matching how the outbound poller has always behaved on failure.
- Also: the Poll Log transcript viewer loaded scrolled to the top of a fixed-height box — for any real file transfer (hundreds of frame-by-frame lines), this made it easy to believe a transcript ended wherever the visible area happened to stop, when it actually continued below. Now auto-scrolls to the end (the diagnostically relevant part) on load, with a "jump to start" button for the handshake.
2 new regression tests, each verified to fail without the fix.
v1.0b2.143 — BinkP: peers resending their entire backlog forever, even after every file was correctly acknowledged (July 2026)
Reported live: a real FTN peer kept resending its complete inbound backlog on every single poll, indefinitely, despite ANetBBS correctly M_GOT-acknowledging every file every time — visible as a specific netmail ("Area Management Request" / "List of Available Areas") repeating every ~10 minutes.
- FIX: a shared internal helper used by both the outbound poller (
binkp.py) and inbound listener (binkp_server.py) opportunistically captures frames a peer interleaves while we're still waiting on our own acknowledgment — but it only recognized file-offer frames. A peer's own end-of-batch signal (M_EOB), which a spec-compliant mailer sends the instant its own outbound queue empties — independent of whether it's still waiting on us — fell straight through and was silently discarded, with zero trace. Our own end-of-batch handshake would then wait indefinitely for a signal the peer had already sent once and had no reason to resend unprompted. From the peer's own side, its session never registered as successfully completed (the binkp spec requires both sides to receive the other's end-of-batch signal), so it kept requeuing and resending everything on every subsequent connection — root-caused against a complete real session transcript and the official binkp protocol specification. Fixed on both the outbound and inbound sides: an early end-of-batch signal is now recorded and credited instead of waited for a second time. - Also added a content-based dedup fallback for inbound netmail: if a message's MSGID doesn't match anything on file, but its sender, subject, and body exactly match something received within the last 48 hours, it's now treated as a duplicate too — a backstop for any peer software that regenerates its MSGID on every resend.
15 new regression tests across three files, each verified to fail without its corresponding fix.
v1.0b2.142 — Echomail web preview didn't match what actually gets sent (July 2026)
Reported live: after the v1.0b2.139/141 origin-line fixes, a newly
composed message still showed no address in its "* Origin:" line when
viewed in the web UI — the fix looked like it wasn't working.
- FIX: the message-read page displayed the raw stored
origin_line
field, which never includes the FTN address — that gets appended
separately, per network, at actual BinkP send time. The preview
simply didn't reflect what would really go out over the wire. The
read page now computes the same address-append logic for outbound
messages, so what a sysop sees in the web UI matches what a peer
actually receives.
2 new regression tests, each verified to fail without the fix.
v1.0b2.141 — BinkP: stop re-sending the same netmail/echomail forever when a peer talks a lot before acking (July 2026)
Reported live: an AreaFix subscription request to a real SBBSecho hub
kept getting re-sent on every single poll, forever — the peer replied
fresh each time, since as far as it could tell it was receiving a
brand-new request every time.
- FIX: the wait for a peer's M_GOT acknowledgment was bounded by a
fixed count of frames (20), not wall-clock time. A peer that
responds with substantial content of its own before finally
acking — exactly what SBBSecho does, replying with real content
spread across many small frames — could exhaust that budget with
our own GOT never reached, even though the peer had already fully
received and processed what we sent. The packet then got treated as
unacknowledged and left queued for retry, so the next poll sent the
identical request again — and the peer, having no reason to think
otherwise, replied again. Now bounded by wall-clock time instead, so
any amount of peer chatter before the real acknowledgment still gets
fully drained.
3 new regression tests, each verified to fail without the fix.
v1.0b2.140 — Echomail origin line: show how to reach the BBS, not just its name (July 2026)
Follow-up to v1.0b2.139's address fix — requested live: the default
origin-line text itself should follow the common FTN sysop convention
of listing how to actually connect, not just the BBS's name.
ECHOMAIL_ORIGIN_LINE's default is now<hostname>, Telnet:N SSH:N HTTP:N(e.g.joesbbs.com, Telnet:23 SSH:22 HTTP:80), using
whichever of those services are actually enabled and their real
configured ports. Combined with v1.0b2.139's per-network address
fix, a full origin line now reads e.g.joesbbs.com, Telnet:23 SSH:22 HTTP:80 (1:342/201)— matching the standard FTN format.
5 new regression tests.
v1.0b2.139 — Outbound echomail origin lines were missing the FTN address entirely (July 2026)
Reported live: outbound messages showed * Origin: ANetBBS - A Modern
BBS System with no address at all, instead of the FTN convention
<text> (<our address>) a peer's own reply routing depends on.
- FIX: the address-inclusive fallback only ran when the message's
origin line was completely empty — but the web compose form always
populates it from a single global tagline, so that fallback never
actually fired for any real outbound message, on any network. The
network's own address (already resolved per-network for multi-hub-
identity installs) is now always appended, regardless of what origin
text is configured. - Fresh installs also no longer default to generic ANetBBS-branding
text for this — the origin line now defaults to the sysop's own
configured BBS name instead.
4 new regression tests, each verified to fail without the fix.
v1.0b2.138 — Scheduled events: one hung handler could silently kill the entire scheduler forever (July 2026)
Reported live: a sysop's scheduled events — including the stock
defaults, not just the one they'd added — simply stopped firing
entirely, with no error anywhere. A 4-day log showed the scheduler's
own startup line exactly once and nothing after.
- FIX: a handler that hung (a
shellcommand waiting on input, a
network call with no effective timeout, anything) froze the
scheduler thread forever — silently, with no crash logged — taking
down every other scheduled event with it, including ones that had
nothing to do with whatever hung. This was a known, deliberate
tradeoff ("a runaway is a sysop bug, not a security threat"), but
the real-world consequence turned out to be worse than that framing
accounted for. Every handler call is now bounded to a generous
5-minute ceiling — comfortably above what any of the built-in
handlers actually need — so one hung handler can no longer take
every other event down with it.
4 new regression tests, each verified to fail without the fix.
v1.0b2.137 — BinkP inbound listener: stop making peers wait in silence (July 2026)
A peer sysop's own binkd log showed it delivering files to our
inbound listener successfully — every one individually acknowledged —
then sitting in total silence for over 2 minutes hearing nothing back
from us, before giving up, closing the connection, and marking the
whole transfer failed. It kept resending the same backlog on every
subsequent connection as a result.
- FIX: the inbound listener used to fully drain everything a peer
sent (waiting up to 120s for the peer's own end-of-batch signal)
before ever sending our own outbound mail or announcing that we
were done. If the peer's own mailer was itself waiting to hear from
us first — which nothing in the protocol requires either side to
do — neither side would say anything until somebody's timeout fired.
We now send our own outbound mail (if any) and announce we're done
immediately after authenticating, before waiting on the peer's
stream at all. - As a consequence of sending earlier, a peer may now interleave its
own file delivery while we're still waiting on an acknowledgment for
ours — those were previously logged and silently dropped; they're
now received correctly, same as the outbound side already handled.
5 new/updated regression tests, each verified to fail without the fix.
v1.0b2.136 — Docs/wiki full accuracy pass (July 2026)
First full docs+wiki pass since v1.0b2.45-46 — 5 parallel audit agents
checked all 29 docs/*.md files and all 47 wiki pages against actual
current code.
- Fixed stale references to gunicorn (dropped for eventlet's native
WSGI server back at v1.0a2.67, several docs never caught up), a
badly outdated Service Control Center wiki page, and a factually
wrong claim about echomail duplicate handling. - Documented several real, previously-undocumented features: a new
Scheduled Events wiki page, ANetCRAFT (the bundled door game),
the Ebook Reader, and the terminal Node Monitor's message capability. - Found and fixed 43 broken internal wiki links, plus assorted smaller
path/port/version-number corrections.
No code changes — docs and wiki content only.
v1.0b2.135 — BinkP inbound listener: fix unbounded duplicate-message import (July 2026)
Reported live: a peer sysop's poll log showed the same ~570-message
backlog "received" fresh on every inbound BinkP connection, a few
minutes apart, with no sign of slowing down.
- FIX: the inbound BinkP listener (a peer connecting into this BBS
to deliver mail) imported every message in a delivered packet
unconditionally — no deduplication at all. For echomail it didn't
even capture the MSGID kludge onto the row in the first place; for
netmail it captured MSGID but never checked for an existing one
before inserting. The outbound poller's own import path already did
both correctly (poller.py) — this second, separate import path
(binkp_server.py, used specifically when a peer dials in rather
than when we dial out) never got the same treatment. Any redelivery
of a backlog — a peer's own retry logic, a flaky link, anything —
got re-imported as entirely new messages, unbounded, every time.
Now deduplicates by MSGID the same way the outbound path already
does.
3 new regression tests, built from a real FTS-0001 packet round-trip
(not a hand-built dict), each verified to fail without the fix.
v1.0b2.134 — BinkP GOT-ack fix, Service Control Center + scheduled-events reliability (July 2026)
Three unrelated reports: a peer sysop's poll log showing a real
FidoNet hub rejecting our file acknowledgments mid-session, a Control
Center screenshot showing a terminal-services crash with an alarming
(but misleading) 99.3% CPU reading, and a peer sysop reporting his
nightly/hourly scheduled events just weren't firing.
- FIX: receiving files during a poll we initiated ourselves (as
opposed to a peer polling us) acknowledged each one with a bare
GOT: filename— no size or timestamp. FTS-1026 definesM_GOTas
filename size time, the same three fields asM_FILE; our own
inbound listener already sent this correctly, but this second,
separate code path never got the same fix. Tolerated by most peers,
but confirmed live against a real, stricter binkd: the first two
files' bare GOT acks were silently accepted, the third was rejected
outright (ERR: M_GOT: cannot parse args) and the peer hung up —
reproduced identically across two independent sessions. - FIX: the Service Control Center's CPU%/RAM/thread pills kept showing
a dead service's last-known reading indefinitely, with nothing
marking it stale — a crashed process could look like it was still
pinning the CPU for up to 5 minutes after it actually exited. Now
blanked to "—" the moment the sampler's most recent tick comes back
empty. Same bug also silently inflated the aggregate CPU/RAM totals
at the top of the panel. - FIX: the Control Center's own
/status.jsonpolling (systemctl
state + journal reads, once per known unit, every 5s while the panel
is open) ran rawsubprocess.run()under gunicorn+eventlet — the
same tight-loop shape already found crash-looping in the metrics
sampler (v1.0b2.131, "Second simultaneous read on fileno N
detected"). Routed througheventlet.tpool.execute()the same way. - FIX: one scheduled event with an out-of-range time value (the
daily/weeklyschedule kinds were never format/range-validated,
unlike hourly/weekly-day/interval) could crash the entire event
scheduler sweep every single tick — silently blocking every other
scheduled event (nightly maintenance, log rotation, VACUUM, custom
events) from ever running again, with only one generic log line to
go on. The scheduler now isolates each event so one bad row can't
affect the others, and the form validates daily/weekly times the
same way the other schedule kinds already were.
17 new regression tests across the three testable fixes (the stale-
metrics display fix is front-end JS with no test harness in this
project), each verified to fail without its fix.