{"id":28414990,"url":"https://github.com/interlinked1/lbbs","last_synced_at":"2025-06-25T09:31:28.446Z","repository":{"id":65321004,"uuid":"586314933","full_name":"InterLinked1/lbbs","owner":"InterLinked1","description":"Lightweight BBS For Linux - Bulletin Board System server software","archived":false,"fork":false,"pushed_at":"2025-04-29T23:42:18.000Z","size":5002,"stargazers_count":52,"open_issues_count":2,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-03T21:28:26.415Z","etag":null,"topics":["ascii","bbs","bulletin-board","bulletin-board-system","c","chanserv","dialup","discord-relay","finger-protocol","gopher-server","http-server","imap-server","irc-server","nntp-server","pop3-server","rlogin","sieve","smtp-server","ssh-server","telnet-server"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/InterLinked1.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-01-07T17:44:04.000Z","updated_at":"2025-05-31T19:42:06.000Z","dependencies_parsed_at":"2023-12-14T18:48:52.746Z","dependency_job_id":"fdf8314b-8550-4624-8188-c00dba890869","html_url":"https://github.com/InterLinked1/lbbs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/InterLinked1/lbbs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InterLinked1%2Flbbs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InterLinked1%2Flbbs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InterLinked1%2Flbbs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InterLinked1%2Flbbs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/InterLinked1","download_url":"https://codeload.github.com/InterLinked1/lbbs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InterLinked1%2Flbbs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261844446,"owners_count":23218350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ascii","bbs","bulletin-board","bulletin-board-system","c","chanserv","dialup","discord-relay","finger-protocol","gopher-server","http-server","imap-server","irc-server","nntp-server","pop3-server","rlogin","sieve","smtp-server","ssh-server","telnet-server"],"created_at":"2025-06-03T12:30:25.859Z","updated_at":"2025-06-25T09:31:28.428Z","avatar_url":"https://github.com/InterLinked1.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"============================================\nLBBS - The Lightweight Bulletin Board System\n============================================\n\n.. contents:: Contents\n.. section-numbering::\n\nWelcome! Whether you're new to BBSing or a veteran sysop, LBBS was written to be a highly configurable, modular BBS for developers, sysops, and users alike.\n\nLBBS is a BBS (bulletin board system) package and personal server written from the ground up to be extensible, modular, and, of course, lightweight.\nThe codebase is relatively small (~80K SLOC), with relatively few dependencies. It is designed to be easy for sysops to administer, easy for users to use and navigate, and easy for developers to read, understand, maintain, and contribute to the source code.\n\nWhile LBBS is first and foremost a BBS server, its different components can also be used individually: for example, you could use the mail modules as a private mail server, and not load the BBS-related functionality.\n\nFeatures\n========\n\nKey features and capabilities include:\n\n* Fast and lightweight, written entirely in C\n\n* Terminal access via Telnet, RLogin, SSH, and UNIX domain socket support *(note that Telnet and RLogin are plain text protocols and thus insecure)*\n\n* ANSI art support\n\n* File transfers via FTP, SFTP, Gopher, HTTP/HTTPS, and ZMODEM\n\n* HTTP 1.1 web server, with WebSocket and forward-proxy support\n\n* User home directories\n\n* Container environment for executing programs\n\n* Password and public key authentication\n\n* Config-file driven configuration\n\n* Submenu \"skip menu navigation\" - select options in multiple nested menus at once\n* Automatic menu screen generation and resizing\n* Electronic mail (SMTP, POP3, IMAP4)\n\n  * Aliases and subaddressing\n  * Mailing lists\n  * Mailbox quotas\n  * Shared mailboxes and ACL controls\n  * Multi-domain support\n  * Relay support\n  * Advanced queuing support\n  * IMAP NOTIFY support\n  * RFC 4468 BURL IMAP and server-side proxied append support, for more efficient (bandwidth saving) email submission\n  * Remote mailboxes (IMAP proxy)\n\n    * Built-in OAuth2 proxy, allowing the BBS to log in to remote IMAP servers using OAuth2, while your IMAP client uses your normal BBS credentials\n\n  * SPF, DKIM, ARC, DMARC, and SpamAssassin support\n\n  * Filtering\n\n    * Sieve filtering scripts and ManageSieve service\n    * `MailScript filtering engine \u003cconfigs/.rules\u003e`_ for flexible, custom, dynamic mail filtering rules (Sieve alternative)\n\t* Intelligent sender/recipient analysis - prevent yourself from ever sending an email to the wrong people by mistake!\n\n  * Webmail client backend\n\n* Newsgroups (NNTP)\n\n* Native realtime chat\n\n* Internet Relay Chat client and server (including ChanServ), with native IRC, Slack, and Discord relays\n\n* Queue agent position system for Asterisk\n\n* Terminal autodetection (ANSI support, link speed)\n\n* Emulated slow baud rate support\n\n* TDD/TTY (telecommunications device for the deaf) support\n\n* Sysop capabilities\n\n  * Node spying\n  * Interrupt nodes\n  * Kick nodes\n\nUsage\n=====\n\nInstallation\n~~~~~~~~~~~~\n\nTo install LBBS, you will need to compile it from source. Fortunately, we've made this as easy as possible::\n\n     cd /usr/local/src\n     git clone https://github.com/InterLinked1/lbbs.git\n     cd lbbs\n     ./scripts/install_prereq.sh\n     make modcheck\n     make modconfig\n     make\n     make install\n     make samples\n     make service\n\n(Running :code:`make modcheck` is optional. It will tell you all the modules that are available and which will be disabled for the current build.\nRunning :code:`make modconfig` is what actually makes changes to the build environment, disabling any modules with unmet dependencies.)\n\nIf you are setting up a Linux server from scratch, you may also want to refer to :code:`scripts/server_setup.sh` for a more complete script to set up your BBS server.\n\nAfterwards, you may optionally choose to use :code:`scripts/setup_wizard.sh`, a simple utility to do some basic configuration initialization for you. However, this tool is not comprehensive.\n\nTo start the BBS with the sysop console in the foreground, you can then run :code:`lbbs -c`. To daemonize it, just run :code:`lbbs`.\n\nAt the console, press :code:`?` or :code:`h` for a list of available commands. You can also run :code:`lbbs -?` or :code:`lbbs -h` for a list of startup options.\n\nSome configuration of the BBS will be needed before you can use it. Consult the sample configs in :code:`/etc/lbbs` for an overview of settings you may need to configure. At a minimum, you will need to add a menu to the BBS (:code:`menus.conf`).\n\nLBBS is best run on a modern version of Debian Linux (Debian 11 or 12). It should also compile on most other commonly used Linux distros. A recent version of gcc is required (e.g. \u003e= 11).\nThe BBS core should compile and install on FreeBSD, but not all module dependencies may be available and some functionality may be degraded.\n\n**WARNING: Do not run the BBS as root!** Create a non-root user and configure the BBS to run as that instead. See :code:`lbbs -?` or :code:`/etc/lbbs/bbs.conf` to configure the run user and run group.\n\nSysoping\n~~~~~~~~\n\nSysops can monitor and control the BBS using the sysop console provided by the :code:`mod_sysop` module. For example, you can list information about configured BBS menus, spy on nodes, or restart the entire BBS. Most commands are available by typing :code:`/` followed by a string, although some common commands are available by single-press hotkeys. Press :code:`?` in the console for a list of available options and commands.\n\nIf the BBS is started in the foreground, a sysop console is available on STDIN/STDOUT.\n\nAdditionally, regardless of how the BBS is started, the sysop console can be accessed remotely (so called since the access originates from outside the BBS process) by running the :code:`rsysop` program. This program is part of the external utilities and is installed to :code:`/var/lib/lbbs/external/rsysop`.\n\n**WARNING:** Note that anyone that can access the :code:`rsysop` program is able to perform sysop tasks on the BBS. Even if the BBS is not running as root, it should be running under an account that is secured to the sysop.\n\nSystem Configuration\n~~~~~~~~~~~~~~~~~~~~\n\nConfiguration of LBBS and modules are done entirely through INI config files. Different parts of LBBS have their own config files, as does each module that uses one.\nConfig files go in :code:`/etc/lbbs` and sample configuration files exist in the :code:`configs` subdirectory of the source tree.\nEach sample config file documents all available options. Refer to the sample configs for all relevant configuration.\n\nA few especially important configuration files:\n\n* :code:`bbs.conf` - key startup settings\n\n* :code:`mail.conf` - Email configuration\n\n* :code:`menus.conf` - BBS menus, menu items and options.\n\n* :code:`mod_auth_mysql.conf` - MySQL/MariaDB auth provider module config\n\n* :code:`mod_mail.conf` - General email server configuration\n\n* :code:`mod_smtp_filter_dkim.conf` - DKIM signing\n\n* :code:`modules.conf` - module loading settings (to disable a module, you do it here)\n\n* :code:`net_smtp.conf` - SMTP server configuration\n\n* :code:`net_ssh.conf` - SSH and SFTP server configuration\n\n* :code:`nodes.conf` - Node-related configuration\n\n* :code:`tls.conf` - SSL/TLS configuration\n\n* :code:`transfers.conf` - File transfer configuration\n\nAdditionally, the MailScript rules engine uses a script file called :code:`.rules` in the user's root maildir or user's :code:`~/.config` (and :code:`before.rules` and :code:`after.rules` in the root maildir for global filtering) for manipulating messages.\nA sample MailScript rules file is in :code:`configs/.rules` (though this is not a config file, but a sample rule script file).\n\nUser Configuration\n~~~~~~~~~~~~~~~~~~\n\nUser configuration goes in :code:`~/.config`, which is a subdirectory of each user's BBS home directory (unrelated to any system home directories).\n\nUsers can edit these files either via the BBS shell (if configured by the sysop) or via any enabled file transfer protocols (e.g. FTP, FTPS, SFTP).\n\n* :code:`.imapremote` - IMAP client proxy configuration\n\n* :code:`.oauth.conf` - OAuth authentication configuration (used for IMAP client proxy and SMTP submission)\n\n* :code:`.plan` - UNIX .plan file, used by the Finger protocol\n\n* :code:`.project` - UNIX .project file, used by the Finger protocol. Limited to 1 line.\n\nNetwork Login Services\n~~~~~~~~~~~~~~~~~~~~~~\n\nNetwork login or comm drivers are modules in the :code:`nets` source directory, responsible for implementing a network login service. These are what allow users to actually connect to the BBS itself.\n\nGenerally speaking, the comm drivers implement some kind of standardized TCP-based protocol. There are builtin drivers for Telnet, RLogin, and SSH. **Note that Telnet and RLogin are plain text protocols and thus insecure!** Using SSH is recommended for any public connections.\n\nLBBS also includes a UNIX domain socket module (:code:`net_unix`). One use case for this is if you want to \"proxy\" connections to the BBS through the main, public-facing network login service. For example, say you run OpenSSH on port 22 (and you don't want to change the port), but you still want people to be able to connect to your BBS on port 22. You can create a public user account on your server that executes the BBS as a program, rather than providing a login shell. If you do this, you don't need any of the network drivers loaded or running besides :code:`net_unix` (UNIX domain sockets provide the least overhead for these kinds of loopback connections). That said, the UNIX domain socket driver is quite primitive. Using one of the other drivers, particularly the SSH driver, will provide a far superior experience.\n\nDo note, should you choose to proxy connections in the manner described above, there are several important security implications of doing this that you *must* understand, or you open your system up to vulnerabilities. See the comments at the top of the source file :code:`nets/net_unix.c`\n\nUnless you really know what you are doing, you are probably better off using LBBS's builtin network login services, rather than proxying the connection through your system's primary network login services. This will provide a more seamless user experience and mitigate potential security vulnerabilities described above.\n\nEach comm driver handles window resizing in its own way.\n\n* :code:`net_ssh` - full support for window size at login and resizing later\n\n* :code:`net_telnet` - support for window size at login, but currently no support for resizing later (could be added as an enhancement)\n\n* :code:`net_rlogin` - broken support for window size at login (doesn't work)\n\n* :code:`net_unix` - no support for window size. UNIX domain sockets are similar to a raw TCP socket, there is no terminal protocol riding on top of the socket here. If you need (or want) window size support, use a different network comm driver.\n\nNone of the network comm drivers are mutually exclusive - you can enable as many or few as you want, and users can use whatever protocol they want to.\n\nGenerally speaking, for the reasons listed above, SSH is the recommended protocol. Apart from being the only protocol secure to use over the Internet, it also fully handles terminal resizing.\n\nThe BBS also comes with some network services that aren't intended for terminal usage, e.g. FTP, HTTP, IMAP, etc. See the :code:`nets` directory for a full listing.\n\nUsing mod_auth_mysql\n~~~~~~~~~~~~~~~~~~~~\n\nThe BBS needs at least one authentication provider to be able to authenticate users.\n`mod_auth_mysql` is an included module that authenticates users against a MySQL/MariaDB database.\n\nYou'll need to create a user for the database, if you haven't already::\n\n    CREATE USER 'bbs'@'localhost' IDENTIFIED BY 'P@ssw0rdUShouldChAngE!';\n    GRANT ALL PRIVILEGES ON bbs.* TO 'bbs'@'localhost';\n    FLUSH PRIVILEGES;\n\nThen, create a database called :code:`bbs` and a table called :code:`users` - the SQL to do so is in :code:`scripts/dbcreate.sql`.\n\nDon't forget to also add your DB connection info to :code:`mod_auth_mysql.conf`!\n\nFAQ\n===\n\nCan I try it out without installing anything?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSure! The reference installation of LBBS is the PhreakNet BBS, reachable at :code:`bbs.phreaknet.org`. Guest login is allowed.\n\nHow can I bind BBS services to privileged ports if it's not running as root?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you are running your BBS as a non-root user (which you *should*!), you may encounter errors binding to particular ports.\nThere are a few different methods you can use to bind to privileged ports (1 through 1023) when running the BBS as a non-root user.\n\nThe first is as simple as explicitly granting the BBS binary the right to do so, e.g.::\n\n    sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/sbin/lbbs\n\nThis is the recommended approach if it works for you. If not, you can also explicitly allow\nall users to bind to any ports that are at least the specified port number::\n\n    sudo sysctl net.ipv4.ip_unprivileged_port_start=18\n\nThis example would allow any user to bind to ports 18 and above.\nThe lowest standard port number currently used by the BBS is 18 (FTP).\n\nNote that this method is not as secure as the first method, but is likely to work even if other methods fail.\n\nFinally, note that many systems already have daemons running on the standard ports, e.g.\nsshd, telnetd, Apache web server, etc. If these are present, you will need to resolve the conflict, as only one\nprogram can bind to a port at any given time.\n\nHow do I run the BBS as a service under systemd?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRun :code:`make service`, and this will install the service file for systemd to use.\n\nCan I run SSH and SFTP on the same port?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYes (and, in fact, you must, if you wish to enable both).\nOriginally, SSH and SFTP were provided by 2 independent modules. They are now combined, allowing for same-port usage, which users expect.\n\nWhat terminal emulators are supported?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nMost common terminal emulators should work fine. The emulator's terminal type is used, if sent, and some terminal autodetection is also performed.\n\nSome emulators are particularly good. Of all the well-known ones, these three terminal emulators are particularly recommended for BBSing on Windows:\n\n* **SyncTERM** - Works well, looks nice. You **must** use the `newer 1.2 version \u003chttps://github.com/bbs-io/syncterm-windows/releases/tag/dev\u003e`_. The more commonly downloaded 1.1 version has major bugs.\n* **qodem** - Initial configuration slightly unintuitive, but otherwise works very well, with excellent support for non-standard display sizes. Set :code:`doorway_mode_on_connect = mixed` in :code:`%userprofile%\\Documents\\qodem\\prefs\\qodemrc.txt`.\n* **PuTTY** (and forks, like KiTTY) - Works well, no known issues. Not \"retro\" at all, but does the job fine.\n\nMost other terminal emulators tested tend to have various setup, compatibility, or runtime issues. In particular:\n\n* **NetRunner** - Not recommended. Poorer support for ANSI escape sequences and Telnet options. Does not send a terminal type! Poor support for ncurses applications.\n\nI see warnings about a terminal type not being in the terminfo database.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis typically happens for terminal emulators that report non-standard terminal types that are not installed by default on the system.\nThis can be resolved by installing the appropriate terminfo file. See :code:`scripts/server_setup.sh` for an example of adding :code:`syncterm` support in this manner.\n\nWhat is the difference between :code:`door_chat` and :code:`door_irc`?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n:code:`door_chat` is a fully self-contained, isolated chat module that can only be used from within the BBS.\n:code:`door_irc` is an IRC client that can be used to connect to the local IRC server (provided by :code:`net_irc`) or to another IRC server.\nIn most cases, :code:`door_irc` is likely what you want; however, :code:`door_chat` can still be used on its own, if it meets your needs.\n\nWhen using private namespace IRC channels, channel messages get sent to me as private messages.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIt is likely that your IRC client does not properly support all the standardized channel prefixes (#, \u0026, +, and !).\nMany clients only support the first two, if even that. Because of this limitation, you can override the prefix used\nfor the per-user namespace prefix near the top of :code:`include/net_irc.h`, by defining :code:`PRIVATE_NAMESPACE_PREFIX_CHAR` appropriately.\nIf your client only supports the # prefix properly, then unfortunately you cannot use this feature, unless you can fix your client.\n\nThe Discord relay seems to exit immediately after being started.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe bot you created likely doesn't have all the necessary permissions. Make sure \"Privileged Gateway Intents\" are enabled as appropriate.\n\nI have multiple hostnames. Is SNI (Server Name Indication) supported?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nYes, LBBS supports SNI as both a client and a server. Refer to :code:`tls.conf` for configuration details.\n\nHow can I serve webpages using the embedded web server?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nThere are 3 methods supported by the web server:\n\n* Embedded server applications - these are dynamic applications that run within the BBS itself\n\n* Static files - static files on disk that the web server sends to clients\n\n* CGI (Common Gateway Interface) - CGI can be used to dynamically send a webpage from an external program\n\nEmbedded dynamic scripting engines (e.g. a la Apache HTTP server's mod_php) are not currently supported.\n\nHow does the container enviornment (isoexec handler) work?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe :code:`isoexec` handler creates the specified process in a separate namespace so that is isolated from the root namespace\nin which the BBS is running. Essentially, it creates a container, similar to how technologies like Docker work.\n\nThis enhances security by providing isolation between your system and whatever may be executed within the environment,\nsuch as a shell or other arbitrary program. For example, you can use this to provide users shell access on your BBS,\nbut without actually granting them access to the main filesystem.\n\nThe container does require that you provide a root filesystem for it to use. An example of how to do this is\nin :code:`configs/menus.conf`. Please also read the caveats, notes, and warnings about :code:`isoexec` in the sample config file.\n\nThe :code:`isoroot` program in the :code:`external` directory also demonstrates how this functionality works in a standalone manner,\nif you want to test your container environment separately.\n\nHow do I set up TLS certificates?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou will need to get TLS certificates from a certificate authority to support protocols that use TLS for encryption.\n\nWe recommend using a free certificate authority, like Let's Encrypt.\n\nThe below steps show how you can get free 3-month TLS certificates from Let's Encrypt that will renew automatically as needed.\n\nThere are multiple ACME clients you can use; Certbot is another one. acme.sh is used here because it's lightweight; certbot installs quite a bunch of stuff (like snapd) that you probably don't otherwise need or want.\n\nThe guidance here uses a webroot in the BBS itself. There is an option to use a port, but this is misleading; if you run the ACME client in standalone mode, the BBS web server CANNOT be running at the same time. While this may be fine initially, it will be problematic for renewals. The webroot method ensures that certificates can be renewed without issue, as long as the BBS is running.\n\nFinally, certificates will be stored in /etc/letsencrypt (just like Certbot), rather than inside your home directory (the default). You can obtain a certificate for multiple hostnames at the same time (see example in step 4):\n\n1. Enable HTTP (but not HTTPS (yet), which will fail without a TLS certificate configured) in :code:`net_http.conf`.\n\n2. Start the BBS (or reload net_http if it's already running)\n\n3. :code:`curl https://get.acme.sh | sh`\n\n4. :code:`~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --always-force-new-domain-key --issue -w /home/bbs/www --cert-home /etc/letsencrypt -d example.com -d example.net -d example.org`\n\n5. Run :code:`crontab -e` and inspect the :code:`--home` argument in the cron job that was added. It should be :code:`/etc/letsencrypt` (or whatever path you chose for :code:`--cert-home`). If not, update it.\n\n6. Update permissions: :code:`chown -R bbs /etc/letsencrypt/ \u0026\u0026 chgrp -R bbs /etc/letsencrypt/`\n\n7. Now, update :code:`tls.conf` with the path to the cert and key (cert key) that ACME spits out.\n\n8. Restart the BBS for TLS changes to take effect. In the future, you can also run :code:`/tlsreload` to reload certificates without a full restart.\n\nWhat format does the BBS use to store email?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe BBS mail servers use the maildir++ format. This is similar to what software like Dovecot and Courier use by default,\nalthough certain implementation details may differ.\n\nDoes the BBS provide a sendmail binary, for submitting local mail?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNo, it does not. Consequently, you may see messages like this in your cron logs, for example:\n\n:code:`(CRON) info (No MTA installed, discarding output)`\n\nThis is because cron did not detect :code:`/usr/bin/sendmail`, which is used by default to submit outgoing mail from outside of the local MTA.\n\nInstalling the actual :code:`sendmail` is overkill and not recommended, since it also includes the Sendmail MTA, which will conflict with LBBS.\nHowever, you can install a lightweight client like :code:`ssmtp` or :code:`msmtp` (a more actively maintained variant) to do this.\nYou just need to ensure you install an SMTP client consistent with the Sendmail interface, so that programs expecting sendmail\nwill work properly.\n\nIf you install msmtp, be sure to `configure it system-wide \u003chttps://marlam.de/msmtp/msmtp.html#A-system-wide-configuration-file\u003e`_.\n\nThe below is a good default :code:`/etc/msmtprc` for most systems::\n\n   account default\n   host 127.0.0.1\n   port 25\n   from root@example.com\n   tls off\n   logfile /var/log/msmtp.log\n\nMake sure to substitute the default \"from\" address with something appropriate for your server.\n\nThen, you can symlink msmtp to sendmail, and things should \"just work\": :code:`ln -s /usr/bin/msmtp /usr/sbin/sendmail`.\n\nCan I check email using the terminal instead of using IMAP/POP3?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYes, `evergreen \u003chttps://github.com/InterLinked1/evergreen\u003e`_ is the officially recommended terminal mail client for LBBS.\nThe :code:`door_evergreen` module automatically wraps execution of the mail client as appropriate for usage within the BBS.\n\nDoes the BBS provide any kind of webmail access?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nYou can use `wssmail \u003chttps://github.com/InterLinked1/wssmail\u003e`_, a fast and efficient webmail client designed with the BBS's mail server in mind (but may be used with any mail server).\nLBBS comes with the mod_webmail module, which is a backend module for wssmail.\n\nNote that only the webmail backend is a BBS module. The corresponding webmail frontend is a required but separately maintained project. (In theory, the frontend could have multiple implementations as well.)\n\nIf you don't want to use mod_webmail, you can also use any other open source webmail package, e.g. SquirrelMail, RoundCube, etc. and that should work just fine.\nSquirrelMail is extremely simple (no JavaScript used or required); RoundCube comes with more features and extensibility.\nIn particular, RoundCube comes with a built-in graphical ManageSieve editor, which can be useful for managing your Sieve scripts.\n\nDo keep in mind that webmail offers significantly reduced functionality compared to a standard mail client (e.g. something in the Thunderbird family,\nlike Interlink/MailNews).\n\nHow do I fully set up the webmail service?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nYou will need to set up both the frontend and the backend for the webmail.\n\nThe frontend refers to a frontend website that provides the user-facing HTML, CSS, and JavaScript.\n\nThe backend refers to a backend service which interfaces between the frontend and the IMAP/SMTP servers.\n\nThe backend is :code:`mod_webmail`, though it runs on top of :code:`net_ws`, which itself depends on\nthe BBS's web server modules. The frontend is a separate project as the frontend is not coupled to\nthe backend, other than through the requirement that the WebSocket interface be consistent with both.\n\nNo configuration is required of the backend. Only the frontend needs to be configured.\n\nThe frontend does not need to be run under the BBS's web server. For example, you can\nrun the frontend under the Apache HTTP web server, just like any other virtualhost. You'll want\nto secure the site using TLS just like any other site if it's public facing.\n\nApart from the frontend site itself, you can also configure a WebSocket reverse proxy under Apache HTTP\nto accept WebSocket upgrades on your standard HTTPS port (e.g. 443) and hand those off to the BBS WebSocket\nserver. That might look something like this::\n\n   RewriteEngine On\n   RewriteCond %{HTTP:Upgrade} =websocket [NC]\n   RewriteRule /(.*)           ws://localhost:8143/webmail [P,L]\n\nThis example assumes Apache is running on 443 (or whatever client facing port),\nand :code:`net_ws` is listening on port 8143. Note that this connection is\nnot encrypted, but this is a loopback connection so that does not matter.\n\nWhy use mod_webmail over a more popular, established webmail package?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nRefer to the webmail package documentation for more information: https://github.com/InterLinked1/wssmail\n\nWhy is there a non-standard filtering engine (MailScript) included?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe MailScript filtering language was explicitly designed to be very simple to parse, unlike filtering languages with\nslightly more complicated syntax, such as Sieve. MailScript also allows for basic testing of filtering primitives\nindependent of the filtering language used, which can be useful for testing. MailScript was added before Sieve support\nwas added due to the easier implementation.\n\nCurrently, some capabilities, such as executing system commands or processing outgoing emails, are only possible with MailScript, not with Sieve.\nAlthough there are Sieve extensions to do this, the Sieve implementation in the BBS does not yet support this\n(or rather, the underlying library does not). Eventually the goal is to have full feature parity.\n\nIt is recommended that Sieve be used for filtering if possible, since this is a standardized and well supported protocol.\nMailScript is a nonstandard syntax that was invented purely for this software, so it is not portable to other mail servers.\nHowever, if the current Sieve implementation does not meet certain needs but MailScript does, feel free to use that as well.\nBoth filtering engines can be used in conjunction with each other, and they each have their advantages depending on\nthe use case.\n\nWhere do Sieve and MailScript filter scripts reside?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSieve rules reside in one of two locations. For personal mailboxes, they rise in :code:`~/config/*.sieve` and can\nalso be edited by users directly using the ManageSieve protocol (net_sieve). For non-user mailboxes,\nthey reside in the maildir.\n\nMailScript rules may reside in either a mailbox's maildir or in a user's :code:`~/.config/.rules` file. Originally,\nonly the maildir version existed, and this version can only be edited by the sysop since users do not have access\nto their maildirs. Users can directly modify the version in their home directories, and both scripts are evaluated.\nThe maildir version still exists because in non-user associated mailboxes (e.g. shared mailboxes), this is the only\nversion that exists, as there is no corresponding home directory for the mailbox. If a maildir script exists,\nit is executed before the rules in the user's home directory.\n\nThere are three passes of filtering performed:\n\n1. Pre-mailbox pass. Useful for setting default actions.\n2. Mailbox pass (only for messages that correspond to a mailbox, for example, messages accepted to relay to another server do not)\n3. Post-mailbox pass. Useful for enforcing required actions.\n\nThe following are all the locations that can contain filter scripts:\n\n* Global rules (can only be modified by the sysop)\n\n  * :code:`$ROOT_MAILDIR/before.rules` - MailScript rules to run in pre-mailbox pass. Always executed.\n  * :code:`$ROOT_MAILDIR/after.rules` - MailScript rules to run in post-mailbox pass. Always executed.\n  * :code:`$ROOT_MAILDIR/before.sieve` - Sieve rules to run in pre-mailbox pass. Always executed.\n  * :code:`$ROOT_MAILDIR/after.sieve` - Sieve rules to run in post-mailbox pass. Always executed.\n\n* Mailbox rules, only for messages corresponding to a mailbox\n\n  * :code:`$MAILDIR/.rules` - MailScript rules to run for mailbox. Always executed. Not user-editable.\n  * :code:`~/.config/.rules` - MailScript rules to run for mailbox. Only exists for personal mailboxes. User-editable.\n  * :code:`$MAILDIR/.sieve` - Active Sieve script (or symlink) for mailbox. Not user-editable, but for personal mailboxes, can be changed using the ManageSieve protocol.\n  * :code:`~/.config/*.sieve` - All Sieve scripts for mailbox. Only exists for personal mailboxes. User-editable, including via ManageSieve protocol.\n\nNote that :code:`$ROOT_MAILDIR` is not a real variable defined by the BBS, but here refers to the root maildir, the directory that contains all the individual mailbox maildirs.\nLikewise for :code:`$MAILDIR` referring to the mailbox's maildir. :code:`~` refers to the user's home directory.\nFinally, note that \"always executed\" should be interpreted as \"always executed if the script exists, and unless a previous global rule terminated rules processing altogether\".\n\nHow do I enable spam filtering?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere is a builtin module for SpamAssassin integration. SpamAssassin installation and configuration is largely beyond the scope of this document, but here is a decent quickstart:\n\nInstallation\n------------\n\nInstall SpamAssassin: :code:`apt-get install -y spamassassin`. You do not need :code:`spamass-milter` since milters are not currently supported.\n\nTIP: If you have multiple mail servers in an internal hierarchy, we recommend installing SpamAssassin on the \"outermost\" SMTP server, i.e. the one that receives mail directly from other MTAs on the Internet. This way, you have the ability to refuse acceptance of certain spam emails during the SMTP transaction itself (which is \"cheap\"), rather than accepting it, relaying it to a downstream server, determining the email should be rejected, and then having to generate a \"bounce\" messages, since the original connection has already been closed (which is \"expensive\", and not as reliable). The first server can run SpamAssassin, and downstream servers with user mailboxes can then actually do filtering based on the headers added previously by SpamAssassin.\n\nNote that if the incoming mail server running SpamAssassin is hosted on DigitalOcean, you will need to `sign up for a DQS key and follow the instructions in order to make Spamhaus's DNSBLs functional \u003chttps://www.spamhaus.org/resource-hub/email-security/if-you-query-the-legacy-dnsbls-via-digitalocean-move-to-spamhaus-technologys-free-data-query-service/\u003e`_.\n\nDeployment Considerations\n-------------------------\nSpamAssassin is best used before-queue, since this prevents backscatter by ensuring spam results are available for filtering rules to use (allowing recipients to outright reject highly suspected spam, for instance). :code:`mod_spamassassin` invokes SpamAssassin during the SMTP delivery process to allow this.\n\nWhen invoked directly (e.g. as :code:`/usr/bin/spamassassin`), SpamAssassin will read the message from the BBS on STDIN and output the modified message on STDOUT. Because the BBS only needs SpamAssassin to prepend headers at the top, it will *not* use the entire returned body from SpamAssassin. Instead, it will prepend all of the SpamAssassin headers and ignore everything else, since that would just involve copying the remainder of the message back again for no reason. This contrasts with with more conventional facilities that mail transfer agents provide for modifying message bodies on delivery.\n\nConfiguration\n-------------\n\n* Load the language plugin by adding :code:`loadplugin Mail::SpamAssassin::Plugin::TextCat` to :code:`/etc/spamassassin/local.pre`\n\n* Create your custom preference file, e.g. :code:`/etc/spamassassin/config.cf`::\n\n   # Required score to be considered spam (5 is the default, and should generally be left alone, fine tune your Junk threshold using mail filtering rules instead)\n   required_score      5\n\n   # English is the only language that won't trigger the UNWANTED_LANGUAGE_BODY rule\n   ok_languages en\n\n   # SPF hard fail, always reject\n   score SPF_FAIL 10.0\n\n   # SPF soft fail, always send to Junk\n   score SPF_SOFTFAIL 5.0\n\n   # Heavily penalize mail from domains with no SPF record\n   score SPF_NONE 3.0\n\n   # No valid author signature and from-domain does not exist\n   score DKIM_ADSP_NXDOMAIN 5.0\n\n   # No valid author signature, domain signs all mail and suggests discarding the rest (DISCARD)\n   score DKIM_ADSP_DISCARD 5.0\n\n   # No valid author signature, domain signs all mail (ALL)\n   score DKIM_ADSP_ALL 5.0\n\n   # Penalize missing DMARC policy\n   score DMARC_MISSING 2.0\n\n   # Email is not in English\n   score UNWANTED_LANGUAGE_BODY 3.5\n\n   # Penalize HTML only emails\n   score MIME_HTML_ONLY 1.8\n\n   # Penalized heavily abused freemail\n   score FREEMAIL_FROM 0.5\n\n   # Don't modify original message (apart from adding headers)\n   report_safe 0\n\n   # Add X-Spam-Report to all emails, including ham, not just spam\n   add_header all Report _REPORT_\n\n   # Add X-Spam-Score to all emails, including ham, not just spam\n   add_header all Score _SCORE_\n\n   # Bayes DB (specify a path and sa-learn will create the DB for you)\n   bayes_path /var/lib/spamassassin/bayesdb/bayes\n\nIf you choose not to sign up for a DQS key as described above, SpamHaus may reject your RBL/URIBL requests, in which case you can disable RBL/URIBL checks by adding::\n\n   # Skip RBL checks\n   skip_rbl_checks 1\n\n   # Skip URIBL checks\n   skip_uribl_checks 1\n\n* Go ahead and run :code:`sa-compile` to compile your rule set into a more efficient form for runtime (if you modify :code:`config.cf` in the future, rerun this command).\n\nTo regularly update SpamAssassin with the latest rules, enable the cron job by adding :code:`CRON=1` to :code:`/etc/default/spamd`.\n\nFiltering Spam\n--------------\n\nSpamAssassin will tag spam appropriately, but not do anything to it. That's where filtering rules can help filter spam to the right place (or even reject it during the SMTP session). There are a few headers that SpamAssassin will add, e.g. :code:`X-Spam-Level`. Users can customize what they want to do with spam and their threshold for spam filtering using a filter. The most common rule is to move suspected spam to the user's Junk folder.\n\nOur recommendation is to ignore the :code:`X-Spam-Flag` header entirely. Instead, you can use the :code:`X-Spam-Level` header in mail filtering rules to handle spam, by either moving them to Junk (at a lower threshold) and outright rejecting them (at a higher threshold). This gives you much more fine-grained control, and allows different users to customize their filtering.\n\nThe :code:`X-Spam-Level` header contains one asterisk for each whole positive spam score level (i.e. it is the value of the spam score (also available directly in the :code:`X-Spam-Score` header), rounded down, and empty if less than 1.0, including negative. For instance, :code:`****` denotes the message has a spam score of between 4.0 and 4.9. Since spammier messages have more :code:`*`s, you can easily use a simple substring match on this header value, for example::\n\n   # This MailScript rule will outright reject any messages with a spam score of 10.0 or greater (and set a custom refusal message)\n   RULE\n   MATCH DIRECTION IN\n   MATCH HEADER X-Spam-Level CONTAINS **********\n   ACTION REJECT Message refused, appears to be spam\n   ENDRULE\n\n   # This MailScript rule will move any messages with a spam score of 5.0 or greater (and implicitly 9.9 or less, if the above rule is present) to the user's Junk folder\n   RULE\n   MATCH DIRECTION IN\n   MATCH HEADER X-Spam-Level CONTAINS *****\n   ACTION MOVETO Junk\n   ENDRULE\n\nYou could also use a standard Sieve rule instead of a MailScript rule::\n\n   require \"fileinto\";\n   if header :contains \"X-Spam-Level\" \"*****\" {\n      fileinto \"Junk\";\n   }\n\nNote that :code:`X-Spam-Level` only gives you the ability to filter by intervals of 1. If you want more granular control than that, you should use the :code:`X-Spam-Score` header instead::\n\n   # This MailScript rule will reject any messages with a spam score of 7.7 or greater\n   RULE\n   MATCH DIRECTION IN\n   MATCH HEADER X-Spam-Score \u003e= 7.7\n   ACTION REJECT\n   ENDRULE\n\nBoth Sieve and MailScript rules can also be configured globally (system-wide), in addition to per-mailbox. This is useful if you as the postmaster want to reject all mail above a certain spam level. There are two global Sieve scripts that can be configured and one global MailScript script. All of these files must be named as follows and placed in the root maildir:\n\n* :code:`before.sieve`: Sieve rules that will be executed before any per-mailbox rules are executed. This is usually better for default settings that users may override, such as moving spam to Junk.\n* :code:`after.sieve`: Sieve rules that will be executed after any per-mailbox rules are executed. This is usually better for settings that you do not want users to override.\n* :code:`before.rules`: MailScript rules that will be executed before any per-mailbox rules. This is usually better for default settings that users may override, such as moving spam to Junk.\n* :code:`after.rules`: MailScript rules that will be executed after any per-mailbox rules. This is usually better for settings that you do not want users to override.\n\nThe order with which Sieve and MailScript rules run with respect to each other is consistent between rule engines, e.g. both global Sieve and MailScript \"before\" rules will run before any per-mailbox rules, which will run before any global \"after\" rules. The order with which Sieve and MailScript rules are evaluated within a single pass (e.g. the before rules) is not defined and should not be relied upon.\n\nOne special case that can only be handled in MailScript is filtering outbound mail. The Sieve implementation does not currently support this. A special case of outbound filtering that may be useful is refusing acceptance of spam in a multi-server mail network. If your primary incoming mail server runs SpamAssassin (as recommended), but user mailboxes reside on another server downstream, then normal user mail filtering to outright refuse definite spam messages normally wouldn't be performed until the message is delivered to the local mail server, by which time the incoming server has already accepted the message, only for it later to be rejected, requiring a bounce to be generated. To work around this, you can configure a global MailScript rule to refuse acceptance of confirmed spam. The downside to this approach is users no longer have the ability to override this, since the rule is being run on a different server. Therefore, use caution and only refuse messages with a very high probability of being spam (e.g. spam score of 10 or greater). This could be done as follows::\n\n   # This MailScript rule will reject any messages with a spam score of 10 or greater\n   RULE\n   MATCH DIRECTION OUT\n   MATCH HEADER X-Spam-Score \u003e= 10\n   ACTION REJECT\n   ENDRULE\n\nNote this is similar to an above rule, except the direction is :code:`OUT`. For incoming mail, this rule will only be executed for mail that is accepted and sent onwards to another server. (It could also apply for local submissions that are sent to external parties, though such mail shouldn't have an :code:`X-Spam-Score` header at this point, so this is unlikely to cause an issue.) Note, however, that not all filter actions apply to all mail; for example, in the case of mail accepted by an edge server and then relayed to another server housing the actual mailbox, no mailbox exists locally on the edge server for the message while it is being processed. Mailbox rules thus cannot be run on these messages, but global rules (before/after Sieve and MailScript rules) can still be run. Certain actions, like REJECT, can be used without issue, while some actions, such as :code:`fileinto` (Sieve) or :code:`MOVETO` (MailScript) cannot be used since there is no corresponding mailbox within which to move messages (if such rules are, they will be ignored and trigger a warning). Currently, in Sieve, it is not actually possible to target such mail; in MailScript, the condition :code:`MATCH DIRECTION IN` should currently suffice to ensure the rule is skipped for non-mailbox mail.\n\nThe :code:`mod_smtp_recipient_monitor` plugin module also performs outbound filtering. If the :code:`.config/.recipientmap` file exists in a user's home directory, this module will automatically screen outbound mail and warn the user if sending mail to a brand-new from/recipient combination. This can help prevent mail from accidentally being sent to the wrong users, or from the wrong email address.\n\nTraining\n--------\n\nSpamAssassin can work reasonably well out of the box, but will get better with training. It is best trained on real spam (and ham, or non-spam) messages. You can tell SpamAssassin about actual spam (:code:`sa-learn --spam /path/to/spam/folder`) or ham (:code:`sa-learn --ham /path/to/ham/folder`).\n\nIf you receive spam, don't delete them - put them in a special folder (e.g. Junk) and rerun :code:`sa-learn` periodically.\n\nYou can also run on multiple folders - careful though, if users have a filter to move suspected spam to Junk, this could train on false positives if this is run before they react and correct that. Therefore, if your mail server is small, you may just want to do this manually periodically after receiving Spam::\n\n   sa-learn --spam /home/bbs/maildir/*/Junk/{cur,new}\n   sa-learn --ham /home/bbs/maildir/*/cur\n\nOnce you've trained the Bayes model, you can delete the spam messages if you wish. Rerunning the model on existing messages is fine too - the model will skip messages it's already seen, so there's no harm in not deleting them immediately, if you have the disk space.\n\nEmail sent from the BBS keeps going to people's spam!\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nEmail deliverability is beyond the scope of this guide, but there are a few things you'll want to ensure:\n\n* SPF records are configured for any domains from which you send email\n\n* MX records are configured for any domains from which you send email\n\n* rDNS is configured for any IP addresses from which you send email (used for FCrDNS). If you use DigitalOcean, your `Droplet name must be the rDNS hostname \u003chttps://docs.digitalocean.com/products/networking/dns/how-to/manage-records/#ptr-rdns-records\u003e`_. The rDNS hostname must resolve to your IP but does not need to match your mail domain, nor encompass all of them.\n\n* DKIM is configured (see :code:`mod_smtp_filter_dkim.conf`)\n\nAdditionally, there are many online tools that can do some deliverability checks for you, which may catch common configuration errors and mistakes:\n\n* `Mail Tester \u003chttps://www.mail-tester.com\u003e`_\n\n* `Postmastery \u003chttps://www.postmastery.com/email-deliverability-test/\u003e`_\n\nHow can I improve the efficiency of my email submissions?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou *could* use RFC 4468 BURL, but this is not supported by virtually any mail client (besides Trojita).\n\nThe recommended setting is to use MailScript rules to \"filter\" your outgoing emails.\nYou can define a rule for each account to save a copy in your IMAP server's Sent folder.\nFor your local BBS email account, you can use :code:`MOVETO Sent`; for remote IMAP servers,\nyou can specify an IMAP URL like :code:`MOVETO imaps://username@domain.com:password@imap.example.com:993/Sent`.\nThe BBS's SMTP server will then save a copy of the message in the designated location before relaying or sending it.\n\nThis can be faster since normally your mail client uploads messages twice: once to your SMTP server to send it,\nand once to the IMAP server to save a copy of it (in the Sent folder). BURL IMAP was created to address this inefficiency,\nbut unfortunately lacks widespread client support (although LBBS and several other IMAP servers do support it).\nInstead, the SMTP server can save the copy to the IMAP server (basically the inverse of BURL).\n(Gmail's SMTP server does something like this as well.) This doesn't require any special client support.\n\nIf you synchronize your Sent folder locally, you'll still end up downloading the message, but it'll use your download bandwidth\ninstead of your uplink bandwidth, the latter of which is typically more limited.\n\nIf you do have the SMTP server save copies of your sent messages, make sure to *disable* \"Save a copy of sent messages to...\" in your mail client, to avoid saving a duplicate copy.\n\nAs noted above, currently Sieve and MailScript do not have feature parity, so you cannot use Sieve to do this; you must use MailScript rules.\n\nDonations\n=========\n\nLBBS is developed entirely by volunteers on their own time.\n\nIf LBBS is useful to you, please `consider donating \u003chttps://interlinked.us/donate\u003e`_ to fund further development and features. Thank you!\n\nLicensing\n=========\n\nIf you intend to run an LBBS system or make modifications to LBBS, you must understand the license.\n\nLBBS is licensed under the `GNU General Public License version 2 (GPLv2) \u003chttps://choosealicense.com/licenses/gpl-2.0/\u003e`_. At a high level, GPLv2 is a copyleft license (sometimes referred to as a more restrictive license) that requires that any modifications to the source code be distributed to any users to whom the resulting program is made available. This contrasts with more permissive licenses such as the Apache License or MIT License that do not have such requirements. See the link for more details.\n\nThere are a few reasons I opted to license LBBS under the GPL, some out of choice, others less so:\n\n* The reality is that the days of commercial BBSes are long over. There is no money in running a BBS these days, nor is there any money in writing BBS software. LBBS is no exception. The majority of BBS users, sysops, and developers are all hobbyists doing this for fun, not to make a living. A copyleft license better suits the environment of BBSes today, encouraging contributors to share modifications and improvements with the community.\n\n* I considered licensing the LBBS core under the Affero General Public License (AGPL) and modules under the GPL, since BBS users are not entitled to the source code under the GPL unless the binaries are distributed to them. However, it was (and is) important to me that modules not be licensed under the AGPL, but something more permissive such as the GPL, so that sysops and developers could create their own custom modules and not be required to disclose the source code to their users, in order to provide more freedom for users and sysops. Rather than complicating things with split-licensing, licensing everything under the more permissive GPL is simpler.\n\n* Parts of the LBBS source code and binary have dependencies on components that are themselves licensed under the GPL. For example, the history functionality for the sysop command line, which depends on :code:`history(3)`, a component of the GNU readline library (licensed under the GPL). So, LBBS is required to be licensed with a copyleft license at least as strong as the GPL.\n\nNote that these are merely the rationales for licensing this project under GPLv2, but the vast majority of users and sysops do not need to be concerned about the license, unless you intend to distribute compiled versions of LBBS or make modifications to it. If you make modifications to the source and distribute the result, you must make the source code available under a license at least as restrictive as the GPLv2. If you are merely using LBBS or are a sysop running LBBS, then there is nothing special you need to do to comply with the GPL. Obviously, this is not legal advice, and you should consult a lawyer if you have licensing questions or concerns.\n\nDevelopment Notes\n=================\n\nArchitecture\n~~~~~~~~~~~~\n\nLBBS is a single-process multithreaded program. The BBS \"core\" is the :code:`lbbs` binary comprised of all the source files in the :code:`bbs` directory. The core is designed to be small, with additional functionality provided by modules that can be dynamically loaded and unloaded as desired. This makes it easy for functionality to be added in a self-contained manner.\n\nFor example, the sysop console is provided by the :code:`mod_sysop` module. It is not built in to the core. This makes it easy to modify the sysop console, and you could even write your own sysop console and use that instead!\n\nThis approach is also relied on for key functionality that could be implemented in different ways. For example, the :code:`mod_auth_mysql` is an *authentication provider* that can process user login requests, backed by a MySQL/MariaDB database. However, maybe you use a PostgreSQL database instead, or SQLite, or some other kind of authentication mechanism entirely. LBBS doesn't dictate that users be stored in a certain type of file on disk, or even locally at all. Since auth providers can use any DBMS, API, etc. you could easily set up a BBS server fleet, all sharing the same users. The point is authentication is handled in a very flexible manner. (Obviously, somebody will need to write a module to handle authentication the way you want to, but this can be done without touching the BBS core at all.)\n\nAt a high level, incoming connections are accepted by a network comm driver using a socket. The connection is accepted and each network driver does its own preliminary handling of the connection, such as getting the terminal size. Then, a thread is spawned to handle the node and a pseudoterminal (PTY) is created, with the master side connected to the socket file descriptor and the slave side used for all node I/O. For example, to put the terminal in non-canonical mode or enable/disable echo, these operations are performed on the slave side of the node's PTY.\n\nSome network drivers, such as :code:`net_ssh` currently create a pseudoterminal internally, such that the master end of the SSH pseudoterminal is connected to the libssh file descriptor, and the slave side is used as the node's master PTY fd (as opposed to the socket fd directly).\n\nLBBS does not use ncurses to draw to the screen, partly for simplicity, and partly because ncurses is not multithread safe. While it is possible to compile ncurses such that it has support for threading, this version is not highly portable or often used, and even the maintainer of ncurses discourages using it. Instead, menus are generally generated dynamically directly by LBBS, based on the node's terminal dimensions, although sysops may also manually create menus that are displayed instead.\n\nMenus are the heart of the BBS and where a lot of the action is, both for users and from an architecture perspective. After a user logs in, the BBS node is dropped into the menu routines which handle all the work of generating and displaying menus and options, reading options from users, and taking the appropriate action, such as executing a program, another module, or displaying a submenu.\n\nDirectory Structure\n~~~~~~~~~~~~~~~~~~~\n\nMost code is documented using doxygen, and each source file describes its purpose. The LBBS source is organized into several key directories:\n\n* :code:`bbs` - Source files that comprise the main :code:`lbbs` binary. This is the \"BBS core\".\n\n* :code:`configs` - Sample config files for LBBS modules and settings\n\n* :code:`doors` - Door modules (both internal and external doors). In BBSing, the concept of a \"door\" refers to an interface between the BBS and an external application, used to access games, utilities, and other functionality not part of the BBS program itself. In LBBS, door modules are actually BBS modules, but they are not part of the BBS core, so are external in that sense only. Door modules can call LBBS functions, however, and run within the BBS process, so LBBS door modules offer enhanced functionality beyond that provided with a raw door. To execute a true external program, use :code:`exec` rather than :code:`door` in :code:`menus.conf`.\n\n* :code:`external` - External programs that are not part of the BBS itself, but may be useful supplements or programs to use in conjunction with it. For example, these can be executed as external programs from within the BBS, but they could also be run on their own.\n\n* :code:`include` - Header files for core files\n\n* :code:`modules` - General modules\n\n* :code:`nets` - Network login services / communication driver modules\n\n* :code:`scripts` - Useful scripts for use with LBBS\n\n* :code:`terms` - Reserved for possible future terminal modules, not yet used\n\n* :code:`tests` - Test framework for black box testing\n\nLBBS, once installed, uses several system directories:\n\n* :code:`/etc/lbbs/` - config files\n\n* :code:`/usr/sbin/lbbs` - LBBS binary\n\n* :code:`/usr/lib/lbbs/modules/` - shared object modules\n\n* :code:`/var/lib/lbbs/` - General LBBS resources\n\n  * :code:`/var/lib/lbbs/external` - External programs\n  * :code:`/var/lib/lbbs/scripts` - Useful scripts for use with LBBS\n\n* :code:`/var/log/lbbs/` - log directory\n\nAdditionally, modules (e.g. the mail server, newsgroup server, etc.) may use their own directories for storing data. These directories are configurable.\n\nMake Targets\n~~~~~~~~~~~~\n\nYou can compile and link all the files in a directory containing source files simply by specifying the directory, e.g.:\n\n* :code:`make bbs`\n\n* :code:`make doors`\n\n* :code:`make modules`\n\n* :code:`make nets`\n\nTo compile everything, run :code:`make all`, or simply :code:`make`.\n\nTo install the LBBS binary, all shared object modules, and all external programs, run :code:`make install`.\n\nTo create the config directory with sample configuration files, run :code:`make samples`.\n\nTo delete all compiled code to ensure all source code is cleanly recompiled, run :code:`make clean`.\n\nSome targets are also included to aid developers in debugging the BBS or sysops in tracking down bugs. You will need valgrind installed (:code:`apt-get install valgrind`):\n\n* :code:`make valgrind` - Run valgrind and log all results to :code:`valgrind.txt`. If you suspect a memory leak, you must attach this file when opening an issue.\n\n* :code:`make valgrindsupp` - Generate suppression list from valgrind findings. You should not do this without a good understanding of the findings from the previous step.\n\n* :code:`make valgrindfd` - Run valgrind but show findings in the foreground, rather than redirecting them to a log file.\n\n* :code:`make helgrind` - Run helgrind in the foreground. This is useful for debugging locking.\n\nMost stuff is commented for doxygen. You can generate the doxygen docs by running :code:`make doxygen` (you may need to run :code:`apt-get install -y doxygen graphviz` first)\n\nDebugging\n~~~~~~~~~\n\nLBBS includes a number of builtin tools to assist with debugging, in addition to using :code:`valgrind` as described above. You can turn on debugging by using the :code:`-d` option on startup (up to 10 :code:`d`'s), setting a debug level in :code:`bbs.conf`, or changing the debug level at runtime using the :code:`/debug` command. **If you submit an issue, you must provide full debug (:code:`debug=10`)**.\n\nFrom the sysop console, you can run :code:`/threads` to show running threads, helpful if you suspect threading-related issues. Running :code:`/fds` will show all open file descriptors.\n\nTests\n-----\n\nLBBS includes unit tests for functionality that can be tested individually. These can be run using :code:`/runtests` from the sysop console.\n\nA test framework is also included for black box testing of modules. The tests can be compiled using :code:`make tests` and run using :code:`tests/test` from the source directory.\nTo run just a specific test, you can use the :code:`-t` option: consult the help (:code:`tests/test -?`) for program usage.\n\nNote that although the tests use isolated configuration and runtime directories, they currently do not log to a separate log file, so you may wish to avoid running the test framework on a production system to avoid any \"mingling\" of test executions and normal production usage. The test framework will also stop the BBS before running, so it is best run in a dedicated development environment.\n\nThe test framework will return 0 if all tests (or the specified test) completed successfully and nonzero if any test(s) failed.\n\nDumper Script\n-------------\n\nThe :code:`/var/lib/lbbs/scripts/bbs_dumper.sh` script can be helpful when trying to get backtraces of LBBS.\n\nUsage:\n\n* :code:`./bbs_dumper.sh pid` - Get PID of running BBS process\n\n* :code:`./bbs_dumper.sh term` - Terminate running BBS process (SIGKILL)\n\n* :code:`./bbs_dumper.sh term` - Quit running BBS process (SIGQUIT)\n\n* :code:`./bbs_dumper.sh postdump` - Obtain a backtrace from a core dump file\n\n* :code:`./bbs_dumper.sh livedump` - Obtain a backtrace from a currently running LBBS process\n\nNote that if the BBS was compiled with optimizations enabled (anything except -O0, e.g -Og, -O1, -O2, -O3), then some variables may be optimized out in the backtrace.\nIf you submit an issue, please recompile the BBS without optimization (change to :code:`-O0` in the top-level Makefile) and get a backtrace from an unoptimized system. Otherwise, important details may be missing as the backtrace is incomplete.\n\nIf you are not getting core dumps, ensure the current directory (in which the BBS was started or is currently running) is writable by the BBS user. Otherwise, it cannot dump a core there.\n\nABI Compatibility\n~~~~~~~~~~~~~~~~~\n\nSome projects strive to preserve ABI (Application Binary Interface) compatibility as much as possible when making changes (e.g. no breaking ABI changes allowed within a major revision).\n\nWhile it is certainly not an objective to break ABI, it should be preferred to break ABI if necessary when making changes (e.g. adding\narguments to a function) when doing it a different way would result in less maintainable or clunkier code in the long run.\n\nFor example, if the original function is still useful, it can still call the new function under the hood (which would preserve ABI), but if not,\nthe original prototype should simply be expanded.\n\nLikewise, when adding members to a struct (which can break ABI if not placed at the end), members should be added at the most logical place,\nnot necessarily at the end.\n\nIn essence, changes will not strive to preserve ABI if that is the sole purpose of making a change a particular way.\n\nThe implication of this development philosophy is that users *should not expect* any ABI compatibility between versions from different points in time.\nMixing files from different source revisions may result in an unstable system. You should always fully recompile LBBS from source when building\na new or updated version.\n\nTo make it easier for people to keep track of breaking changes, the following policies should be adhered to:\n\n- If any ABI compatibility (i.e. C code) is broken, at least the minor version number (and possibly the major one) *must* be incremented.\n\n- In general, if any user-facing functionality becomes backwards-incompatible, the major version number *must* be incremented.\n\nCoding Guidelines\n~~~~~~~~~~~~~~~~~\n\nPlease follow the coding guidelines used in this repository. They are by and large K\u0026R C, importantly:\n\n* Use tabs, not spaces.\n\n* Indent properly. Functions (only) should have the opening brace on their own line.\n\n* Braces denoting code blocks are always required, even for single-statement if, for, while, etc. where the braces are technically optional.\n\n* Use :code:`/* multi-line C89 */` comments only, not :code:`// single-line C99 comments`.\n\n* Trim all trailing whitespace.\n\n* All public functions (anything in header files) should be documented using doxygen.\n\n* Add unit tests if possible (modules only).\n\n* For complex functionality, add black box tests in the test framework.\n\n* Avoid C functions that are not multi-thread safe.\n\n* Do not typedef structs\n\n* If there is a BBS function to do something, use it. (e.g. use the :code:`bbs_pthread_create` wrapper, not :code:`pthread_create` directly).\n\n* All source files should use UNIX line endings (LF). However, config files should use DOS/Windows line endings (CR LF). This is so that if Windows users open a config file in an old version of Notepad, it displays properly.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finterlinked1%2Flbbs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finterlinked1%2Flbbs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finterlinked1%2Flbbs/lists"}