Architecture
A 10,000-ft overview of how ANetBBS is wired up.
Processes
+---------------------+ +------------------------+
| anetbbs-web | | anetbbs |
| eventlet WSGI | | asyncio listener |
| Flask app + sock.IO| | telnet/ssh/rlogin |
| | | (one process, unified)|
+----------+----------+ +-----------+------------+
| |
| SQLite (shared) |
+--------- data/anetbbs.db ------+-----------+
| |
+---------+--+ +------+-----+
|anetbbs-mrc-| |anetbbs- |
|bridge | |finger/binkp|
+------------+ +------------+
Two core services (web, the unified anetbbs terminal process),
plus three optional ones (mrc-bridge, finger, binkp) — all
sharing the same SQLite database. Cross-process signals (like
NodeSpy kick) go via DB flags polled by the other process.
Tech stack
| Layer | Choice |
|---|---|
| Web framework | Flask 3 + Flask-Login + Flask-SocketIO |
| WSGI | eventlet's native WSGI server (deploy/serve.py, socketio.run()), single process — gunicorn's eventlet worker class is broken on Python 3.12, so it's deliberately not used |
| Realtime | Socket.IO (web) / asyncio (terminal) |
| DB | SQLite via SQLAlchemy 2 |
| Migrations | "Auto-sweep" — read model metadata, ALTER TABLE for missing cols |
| Frontend | Bootstrap 5, xterm.js, vanilla JS |
| Markdown | python-markdown + bleach |
| RSS | feedparser |
| ANSI | custom CP437 helpers in anetbbs/features/ansi_ui.py |
Repo layout
anetbbs-rebuilt/
anetbbs/
core/ session, terminal protocols (telnet/ssh/rlogin/finger)
features/ BBS sub-systems (menus, doors, multinode, ANSI helpers...)
web/ Flask blueprints (one per feature)
wiki/ wiki renderer + slug + seed
models.py SQLAlchemy models (one file, ~3000 lines)
web_app.py Flask app factory
config.py env-driven config
templates/ Jinja templates
static/ images, css overrides
data/ SQLite + uploads (runtime; gitignored)
docs/ markdown docs served at /docs/
deploy/ systemd units, sudoers, gallery viewer script
tools/ one-off scripts
install.sh / update.sh
Auto-sweep migrations
web_app.py walks db.metadata.sorted_tables on startup. For each
model column the live DB is missing, it issues ALTER TABLE ADD
COLUMN. New columns are added permissively (nullable, no default)
so the migration always succeeds on SQLite. ORM defaults apply for
new inserts.
This means: adding a column = bump the model, restart anetbbs-web,
done. No alembic incantations needed for the common case.