Architecture
A 10,000-ft overview of how ANetBBS is wired up.
Processes
+---------------------+ +------------------------+
| anetbbs-web | | anetbbs-telnet |
| gunicorn+eventlet | | asyncio listener |
| Flask app + sock.IO| | telnet/ssh/rlogin |
+----------+----------+ +-----------+------------+
| |
| SQLite (shared) |
+--------- data/anetbbs.db ------+
|
+---------+----------+
| anetbbs-mrc-bridge|
| MRC client (long |
| poll) |
+--------------------+
Two main services (web, telnet), one secondary (mrc-bridge),
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 | gunicorn + eventlet workers |
| 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/core/ansi_ui.py |
Repo layout
anetbbs-rebuilt/
anetbbs/
core/ session, ANSI, terminal protocols
features/ BBS sub-systems (menus, doors, multinode...)
web/ Flask blueprints (one per feature)
wiki/ wiki renderer + slug + seed
models.py SQLAlchemy models (one file, ~2000 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, logrotate
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.