#AsmBB project
NOTE: this particular repository is a fork of the upstream asmbb hosted at https://asm32.info/fossil/repo/asmbb.
This is an engine for web based message board (forum) implemented entirely in assembly language, using FastCGI interface and SQLite database as a storage.
It is aimed to be the fast and light message board engine and still to be able to provide modern look and feel and all needed features for high active Internet community.
asmbb's documentation ranges from outdated to nonexistent. These are my notes on version 3.0 and the changes I've made to it.
Compiling and installing asmbb is written in flat assembler (fasm) for x86 (that is, 32-bit x86). This has several implications:
- asmbb will not run natively on any architecture that doesn't natively
support x86.
- if the machine is x86-64 and the operating system supports running x86 programs natively, that should work. Note that running x86 programs natively is not the same thing as the x32 ABI, which is where x86-64 programs are run using 32-bit pointers. I know that x86-64 linux kernels support x86 programs by default, I don't know about any other operating systems.
- ARM, RISC-V, POWER, etc will not be able to run asmbb without using some sort of transpiler such as qemu, either in user or system mode.
- You will need x86 versions of the libraries that asmbb uses, in the event that your system is x86-64. The official build instructions have asmbb download and compile these libraries at build-time, though it is possible to unvendor them with some work.
- The source code is less universally-understandable (fewer people have experience with assembly language, though still generally readable). Assembly programming has more programmer-controlled concerns (register allocations, managing the stack, etc) than higher-level languages, so it's also more difficult to modify, particularly in complex and tightly-coupled places.
- asmbb's source uses Intel syntax instead of AT&T syntax: OP DST, SRC.
- Compiling asmbb requires fasm, of course.
- While in principle asmbb could support any x86 system, in practice its supporting library, freshlib, only seems to support Linux and Windows currently.
- asmbb will not run natively on any architecture that doesn't natively
support x86.
** The asmbb source code asmbb uses fossil as its version control system. The official upstream repository is located at https://asm32.info/fossil/repo/asmbb, and ours is located at https://fossil.shadow.lifestyle/repos/asmbb.
To clone it:
$ fossil clone 'https://fossil.shadow.lifestyle/repos/asmbb' asmbb.fossil
To check out trunk:
$ mkdir asmbb $ cd asmbb $ fossil open ../asmbb.fossil trunk
IMPORTANT NOTE: before you do this, run
$ fossil settings --global allow-symlinks 1
asmbb makes heavy use of relative symlinks in the www/templates/ subdirectory, despite the fact that fossil will by default mangle them whenever it writes them.
You may be wondering "does it have to be --global?" And the short answer is that that's the most straightforward way to do it - "fossil help settings" doesn't list a way to specify the repository to configure, which means you need to run it with a checkout as the current directory, but checking out without setting allow-symlinks to 1 will produce the mangled symlinks. Hopefully there exists a better way.
** Dependencies asmbb has at minimum 3 build-time dependencies:
- clessc (a less --> css compiler, I found it at https://github.com/BramvdKroef/clessc)
- fasm (the Flat Assembler, https://flatassembler.net/)
freshlib, a purely-compile-time fasm library that supports x86 Windows and Linux. It expects two environment variables to be set by the user at compile-time: "lib", which should be the filename of the top-level directory of the freshlib source, and "TargetOS", which should be either "Windows" or "Linux" in accordance with the platform type. The freshlib source is also managed by fossil, and can be found at https://fresh.flatassembler.net/fossil/repo/fresh, but be aware that this repository actually houses two packages, freshlib and freshIDE. The default branch, "trunk", is freshIDE, and freshlib is on the "FreshLibDev" branch. freshlib isn't guaranteed to be stable, and compiling 3.0 with newer versions has failed in the past, so I recommend using a3caaf7ad85033489e668b5bfa37185b013cfa2e, which is from 2023-03-13, since that's closest in time to when asmbb-3.0 was released.
If you want to use glibc, be aware that it no longer by default provides the "crypt" function, which asmbb requires. In that case you will need to edit $FRESHLIB_TOP/freshlib/imports/Linux/libc.inc to put the "crypt" prototype with a separate import_proto directive for the appropriate libcrypt.so.X.
If the dynamic linker can't by default find libc, libpthread, or (if applicable) libcrypt, you will need to edit the filenames following the "import_proto" in libc.inc and pthreads.inc to be absolute paths. This is most likely the case only on guix and nix.
If you are going to be letting asmbb download and compile the runtime dependencies, you will also need a gcc toolchain, tar, gzip, wget, rsync, and internet access to port 443 of www.sqlite.org and www.musl-libc.org.
asmbb has two runtime dependencies:
- sqlite3 (must be configured to be threadsafe, e.g. -DSQLITE_THREADSAFE=1, and must support the FTS5 sqlite extension, which in recent versions is enabled by default). The filename of this library is in source/Linux/sqlite3.inc - if you're not letting asmbb build its own sqlite, edit this to some value that the linker can find.
- libc / linker (can be any libc to my knowledge, but must be x86). The filename of this is in source/engine.asm, right at the top (on the line with LINUX_INTERPRETER). Again, if you're not letting asmbb build its own libc, edit this to point to whatever linker you're using.
** Configuring source/engine.asm has several options at the top that can be edited, primarily concerning debugging information. options.DebugMode will give some good information, but note that it is rather noisy and, if stderr is being logged, may potentially cause sensitive information to be logged.
** Building *** The easy way After setting the "lib" and "TargetOS" environment variables as described in the freshlib entry in "Dependencies", in the top-level asmbb directory, run: $ fasm -m 100000 ./source/engine.asm ./www/engine $ cd install $ rm -f asmbb.tar.gz $ ./create_release.sh
If this succeeds, there should be a "asmbb.tar.gz" in the current
directory.
*** The hard way Compile an x86 sqlite3 and libc yourself or use one provided by a package manager. Edit all the asmbb and freshlib files mentioned above to point them at the correct libc, libsqlite, libpthread, libcrypt, and linker (ld). $ fasm -m 100000 ./source/engine.asm ./www/engine $ ( cd www/templates ; ./build_styles.sh )
Then read the bottom half of install/create_release.sh to see how to create
the release tarball.
** Installing Pick a document root (a directory where asmbb's files will go), which I'll refer to as $ASMBBWWW. Then:
$ tar xf path/to/asmbb.tar.gz $ mv ./asmbb $ASMBBWWW $ cd $ASMBBWWW
There is a www/.nginx subdirectory in the asmbb source that isn't included in the tarball for some reason. Go ahead and copy that to $ASMBBWWW/.nginx. Then run
$ nginx -c $PWD/.nginx/nginx.conf -e $PWD/error.log
Note that nginx.conf may require some tweaking since in my experience it tends to treat "." in filenames as meaning "the nginx install directory" instead of "the current directory of the nginx process".
Then in another terminal (or after backgrounding nginx), run
$ umask 077 $ ./engine
It should now be possible to visit localhost:8090 in a browser. It will present you with an admin account setup page. Create an admin account (admin setup will be available with no authentication until you do!).
Once you've logged in to an admin account, forum configuration becomes available (see below). Once you've configured it to your liking, stop nginx and asmbb (ctrl+C should work fine). Read .nginx/nginx.conf and adjust and integrate it into your system's nginx configuration, and set up asmbb to start automatically from your system's init system.
- Settings Note: this documents the upstream 3.0 settings, the settings that are new in this version are described farther down.
Most of the settings should be fairly self-explanatory. One exception, though, is the email-related settings in the "Server" tab of the settings.
- Host :: The host portion of the email address asmbb sends from. This is a domain name, not a URL, so there shouldn't be a "http://" or "https://" prefix. This is also used to construct activation links in the default email template. If you can't send mail with a "From:" header using this domain, you may have to use a custom email command that will fix that header to use what you actually want. This must be set in order for asmbb to send activation emails.
- SMTP account :: The user portion of the email address asmbb sends from. The full email address used will be <smtp account>@<host>. This must be set in order for asmbb to send activation emails.
Pipe the emails through :: this is internally referred to as smtp_exec. If set, it must be a string that will be split at whitespace boundaries into an array of strings which is then executed and receives an email on its stdin. PATH is not searched. Either smtp_exec or both smtp_addr and smtp_port must be set before asmbb will send activation emails. If all are set, smtp_exec will be preferred.
If using a sendmail-compatible mail client for this, make sure to pass the "-t" flag so that it will take the envelope recipients from the "To:" header.
SMTP server / port :: the domain name / ip address of an smtp server to send emails to (smtp_addr), and the port to connect on (smtp_port), respectively.
Confirm by email :: whether email information must be confirmed before an account is made active. When enabled, this also makes it mandatory for new registrations to provide an email address. When disabled, email addresses are not mandatory (in fact, they're not even allowed for new signups).
- Templates TODO writeme
- Selected issues ** Email integration As long as the host and smtp account are set up, and either smtp_exec or smtp server / port are set, and email activation is enabled, asmbb will try sending activation emails. When not using smtp_exec, no authentication of any kind will be performed; asmbb expects to connect to an open relay.
In vanilla asmbb-3.0, there is a bug that may hit anybody who has run an instance and switched from email activation being enabled to being disabled. If an account was registered while email activation was disabled, it won't have an email address associated with it. If email activation is enabled before it is activated, then when asmbb tries sending a batch of activation emails, it will crash on encountering this nonexistent email address. One of my changes fixes this, but if you need to deal with this in the wild, it should work to just clear everything in the WaitingActivation table that doesn't have an email address.
Also, in vanilla asmbb-3.0, smtp_exec won't properly separate the mail headers from the body with a blank line. Really, it's best to just use the changes in this version of asmbb if you want to use email integration. ** Security *** Sanitization asmbb assumes that the http server it communicates with using the fastcgi protocol will sanitize all locations passed to it - for example, to disallow lines like "GET /../../../etc/passwd". Nginx does this by default, I don't know about other http servers.
The "skin" cookie is used to construct template filenames to try, and the
only checking that is done by default is that there exists a
$DOCUMENTROOT/templates/$skin/main_html_start.tpl file, which means it's
possible to pass something like "skin: ../../../../../tmp/maliciousskin"
provided that "/tmp/maliciousskin/" has been created somehow. I have changed
it to ignore values that are either ".", "..", or contain a "/" or "\"
inside them. In those cases it will fall back to the default skin.
*** Default file permissions asmbb will create the forum database, board.sqlite, in the current directory when it is launched if it doesn't already exist. When it does this, the permissions it is created with are restricted solely by the umask.
Also, asmbb will create its cgi socket file to be globally-accessible. This
is so that it is possible for it to run as a separate user from nginx, but
it has the side effect of making it possible for any user on the system to
submit requests to it, with full control over the fastcgi parameters
(e.g. DOCUMENT_ROOT). It likely wouldn't be difficult for a local user to
trick asmbb into either having its user fully compromised or at minimum
exfiltrating the forum database.
Because of this, I recommend that $ASMBBWWW be inside a directory that is
only user-and-group-executable, that is owned by nginx, and that has as its
group a dedicated group for asmbb. That way it can only be accessed by the
asmbb user and the nginx user, while still being able to run as a non-nginx
user.
*** Sandboxing asmbb requires /dev/urandom to be available.
*** Pending public disclosure There are other, more serious security issues I've found, and I'm hoping that Mr. Found will read my emails and incorporate the fixes sometime soon.
- Changes from asmbb 3.0
I've made several changes to improve the stability, security, and features of asmbb. Some of them just fix bugs, but others require some explanation.
** Extensions to the template language [param:PARAM_NAME] will do a lookup of a string in the "Params" table in the database. If no parameter with the given name is found, it expands to the empty string instead.
[head:COMMA_SEPARATED_LIST] will extract the first (possibly-empty) element from a comma-separated list. For example: "[head:a,b,c]" becomes "a", and "[head:,b,c]" becomes the empty string. If the empty string is given, and empty string is produced.
[tail:COMMA_SEPARATED_LIST] will extract the string following the first comma in COMMA_SEPARATED_LIST. For example: "[tail:a,b,c]" becomes "b,c", and "[tail:,b,c]" becomes "b,c", and "[tail:,,,b,c]" becomes ",,b,c".
** The "mirrors" parameter The "forum engine settings" page in the "server" tab now allows you to specify mirrors in the field labeled "Alternate domains for the host". This is a comma-separated list, and no whitespace is modified. The activation emails will send out links for each of these in addition to the "host" field. This way activation emails can also include links to, for example, onion and i2p addresses, or just regular alternate domains.
Note that it is your responsibility to ensure that nginx or whatever your web server is will actually route requests for these hosts to asmbb!
** Alternate activation methods Also in the "server" tab of the settings page, there are now two additional fields: "Types of addresses that can be used for account activation with the pipe command" and "Pipe all activation messages (generic) through". These correspond to entries in the Params table with id "address_types" and "message_exec", respectively.
"address_types" is a comma-separated list of address type strings. By default only one is present: "smtp". When an account contact address is specified to asmbb, it will verify that the address type specified is one of the listed strings. This matching is done case-insensitively, but address types are canonicalized to lowercase before being stored. In other words, as far as asmbb is concerned, these are all lowercase, but if you want an address type to show up as uppercase in a drop-down menu for the user, you can write it in uppercase here.
Note that currently no checking is done of addresses of any type other than "smtp".
message_exec is a generalized form of smtp_exec, and is how messages of any type other than smtp (that is, email) must be sent. Like smtp_exec, it gives a command to be run. Unlike smtp_exec, it passes additional arguments to this command:
- --type :: The argument following this one is the address type. The address type is always lowercase.
- --from :: The argument following this one is the name of the user that should send the message. This is taken from the smtp_user parameter, as with smtp_exec, and should include only the name of the user, not a full address (e.g. "noreply" instead of "noreply@myforum.com").
- --to :: The argument following this one is the full address to which the message should be sent.
- --host :: The argument following this one is the value of the "host" parameter from the Params table, as specified in the "Server" tab of the forum engine settings page.
- --subject :: The argument following this one is the subject of the message, if the address type supports such a notion.
Additional named arguments may be added later, so any programs or scripts written to be run via message_exec should accept unknown named arguments (that is, arguments preceded by a string that starts with "--") if forward compatibility is desired.
If message_exec is not set, only "smtp" address types will have activation messages sent for them, and the same requirements as before will apply: either smtp_exec is set or smtp_addr and smtp_port are set. If message_exec is set, it will always be used instead of either of those - there is no way to use message_exec for non-smtp messages and use one of the other settings for smtp messages.
An example shell script, send-message.sh, that can be used as message_exec is included alongside these notes. It uses msmtp and xmppc to send activation messages via both SMTP (that is, email) and XMPP. It does require that both of them have configuration files written for them that provide the necessary account credentials, though.
In some situations, upstream xmppc won't suffice, because it always connects to the host in the sending user's JID and it refuses to disable TLS unless a development build is used. Both of these cause problems when trying to connect to a local xmpp server that may be behind a reverse proxy. The first patch in the xmppc-patches directory, 0001-xmppc-add-configuration-keys.patch, therefore adds support for two new xmppc configuration keys: 'disable_tls' and 'host'. The effect is that you can configure your ~/.config/xmppc.conf (or wherever you choose to have xmppc find its config file) like so:
[no-reply] jid=my-message-account@my-provider.tld pwd=SOMEGOODPASSWORD disable_tls=true host=localhost
and now when xmppc is instructed to send a message using the 'no-reply' account it will connect to localhost without any tls.
Note that this patch is based on commit 194b0f1fef6c99ea6304f077b32170416d993448 of upstream xmppc, from July of 2023, which was the most recent upstream commit as of December of 2025, so if your xmppc source is much older than that you may need to use sources from git in order for the patches to apply cleanly.
** Language issues The [special:...] commands aren't available for activation message templates. This is because activation messages are sent in batches - first some minimal information about the pending activation is stored in a table, then all pending activations with unsent messages are processed. The information stored doesn't include the guest's language, so [special:lang] will remain unexpanded. Due to the details of how [case:...] works, this means it will always select clause 14 (since "[special:lang]" is 14 characters long), or the last clause if there aren't 14 clauses.
In practice, this means the activation messages would always be in German. This is not a reasonable default, so I've deleted all the non-English activation messages for now. In the future hopefully this can be resolved.
Additionally, some of the templates had to be adjusted to add the support for multiple address types. This sometimes included adding new named constant strings using [equ:...], and since I am only fluent in English, I added copies of the English translation for those constants as placeholders into the other translations.
** Thread locking Asmbb now supports thread locking. This restricts creating, deleting, and editing of posts in a thread, as well as voting in a thread, to those who have permission to ignore locks (in addition to the already-required permissions). There are two new permissions, one for locking / unlocking threads and one for ignoring locks. Any user with permission to lock / unlock threads (or admin) should see a button at the top of a thread that can be clicked to lock or unlock it, depending on whether it is currently locked. Lock status is indicated both at the top of the posts when viewing a thread and in the thread list.
This also adds the template commands [special:canlock] and [special:canignorelock], which work how you would expect.
** Migration The transition to supporting multiple address types for account activation and the transition to supporting locking of threads both involved some changes to the database schema. Two sql scripts, migration4.sql and migration5.sql, must be run to make an existing asmbb-3.0 board.sqlite database usable with this version of asmbb. These scripts can be found at $ASMBB_SRC/source/migration4.sql and $ASMBB_SRC/source/migration5.sql.
When you are ready to upgrade asmbb:
- stop asmbb, optionally make a backup of board.sqlite just in case
- run: sqlite3 /path/to/board.sqlite '.read /path/to/migration4.sql'
- run: sqlite3 /path/to/board.sqlite '.read /path/to/migration5.sql'
- upgrade asmbb
- start asmbb
Provided that the migration goes off without a hitch, no further changes should be necessary for compatibility.