Architecture

Last edited 2026-08-03 01:34 EDT · history · 2 views

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.