#comments #blog #static-website #website #email

app bandurria

Self-hosted lightweight comment system for static websites and blogs

14 releases (4 stable)

new 1.2.1 Jan 29, 2025
0.6.0 Jan 27, 2025

#24 in Web programming

Download history 96/week @ 2025-01-15 738/week @ 2025-01-22

834 downloads per month

MPL-2.0 license

140KB
3K SLoC

Rust 2K SLoC // 0.1% comments JavaScript 760 SLoC // 0.1% comments Handlebars 105 SLoC Shell 91 SLoC // 0.2% comments SQL 60 SLoC // 0.1% comments Forge Config 28 SLoC // 0.3% comments

Bandurria

Test and Build Build and Release dependency status Buy Me A Coffee

Bandurria is a self-hosted lightweight comment system for static websites and blogs. Built in Rust, it consumes only 4MB of RAM. Can be included with a single line of JavaScript (9KB!).


Bandurria Comic

This project has been started after I used another comment system on my personal blog, named Schnack, that requires users to authenticate through OAuth (eg. via Google) before they can send their comment. I have noticed that requiring to OAuth to a Google or GitHub account to send a comment on a public blog might discourage a lot of people from commenting, since user trust in the blog is possibly low.

Bandurria comes as a lighter and even simpler alternative to Schnack, and requires only a name and an email for users to post comments. Bandurria is built into a single tiny portable binary, and is published in an official Docker image.


πŸ‘‰ If you come from Schnack, migrating to Bandurria is very easy: the include script and CSS classes are very similar; you could also easily convert Schnack's SQLite database into Bandurria's MySQL database with a simple script. I'd like to thank Schnack's authors for their work, and the inspiration it's been for Bandurria.

Tested at Rust version: rustc 1.84.0 (9fc6b4312 2025-01-07)

πŸ‡¨πŸ‡± Crafted in Santiago, Chile.

Screenshots

What Bandurria comments look like:

Bandurria Screenshot 1

Posting a new comment with Bandurria:

Bandurria Screenshot 2

Waiting for the anti-bot Proof of Work to complete:

Bandurria Screenshot 3

Features

  • Built-in spam control with a Proof of Work anti-bot system (without annoying CAPTCHAs!)
  • Email-based notifications and Magic Link comment moderation (no complicated Web admin UI)
  • Zero-dependencies server runtime (4MB) and lightweight JavaScript script (9KB)
  • Compatible with any static website or blog system (as long as your can add 1 line of JavaScript)
  • Customize it in a few lines of CSS to match your website or blog style
  • Avatars of comment authors can be shown (this relies on Gravatar, although images are served from your domain name and cached there)

Bandurria provides no administration interface. It solely relies on email notifications for moderation and Magic Links for approving or rejecting comments. It also does not provide any built-in CSS styles, only CSS classes in its injected HTML that you can freely style to match your blog or website style. For convenience, it comes with an example CSS you can copy and paste to start with.

Spam is prevented by requiring user browsers to submit the result to a Proof of Work challenge, based on an improved variant of Hashcash in order to reduce the mint time variance. A solution to the server challenge is computed when the user submits their comment. This spam prevention method is CAPTCHA-free and hassle-free, as it should only take a few seconds to compute under normal circumstances.

Upon submission of their comment, the user will be informed that their comment has been submitted and is awaiting moderation. Then, you (the administrator) will receive the user comment over email for moderation. Bandurria also notifies people of new replies to their comments from administrators, over email (if they opted in to receive reply alerts).

Bandurria can optionally show user avatars next to their names, using the 3rd party Gravatar service. Avatars are fetched from Gravatar and cached in your MySQL database as BLOBs. They are then periodically refreshed when accessed. If Gravatar is down, then stale avatars are served. Avatars are limited to a maximum size of 64KB, so make sure not to configure Bandurria to request large avatar sizes (in pixels).

Oh and what about that name?! Well, the Bandurria name refers to the Bandurria Austral (Black-faced Ibis), which is a bird that can be found across Patagonia. It emits interesting metallic sounds.

How to use it?

Installation

Install from Docker Hub:

You might find it convenient to run Bandurria via Docker. You can find the pre-built Bandurria image on Docker Hub as valeriansaliou/bandurria.

First, pull the valeriansaliou/bandurria image:

docker pull valeriansaliou/bandurria:v1.2.1

Then, provide a configuration file and run it (replace /path/to/your/bandurria/config.cfg with the path to your configuration file):

docker run -p 8080:8080 -v /path/to/your/bandurria/config.cfg:/etc/bandurria.cfg valeriansaliou/bandurria:v1.2.1

In the configuration file, ensure that:

  • server.inet is set to 0.0.0.0:8080 (this lets Bandurria be reached from outside the container)
  • assets.path is set to ./res/assets/ (this refers to an internal path in the container, as the assets are contained there; you do not need to mount a volume for this!)

Bandurria will be reachable from http://localhost:8080.

Install from binary:

A pre-built binary of Bandurria is shared in the releases on GitHub. You can simply download the latest binary version from the releases page, and run it on your server.

You will still need to provide the binary with the configuration file, so make sure you have a Bandurria config.cfg file ready somewhere.

The binary provided is statically-linked, which means that it will be able to run on any Linux-based system. Still, it will not work on MacOS or Windows machines.

πŸ‘‰ Each release binary comes with an .asc signature file, which can be verified using @valeriansaliou GPG public key: πŸ”‘valeriansaliou.gpg.pub.asc.

Install from Cargo:

If you prefer managing bandurria via Rust's Cargo, install it directly via cargo install:

cargo install bandurria

Ensure that your $PATH is properly configured to source the Crates binaries, and then run Bandurria using the bandurria command.

Install from source:

The last option is to pull the source code from Git and compile Bandurria via cargo:

cargo build --release

You can find the built binaries in the ./target/release directory.

Configuration

Use the sample config.cfg configuration file and adjust it to your own environment.

You can also use environment variables with string interpolation in your configuration file, eg. server.inet = ${BANDURRIA_INET}.

Available configuration options are commented below, with allowed values:

[server]

  • log_level (type: string, allowed: debug, info, warn, error, default: error) β€” Verbosity of logging, set it to error in production
  • inet (type: string, allowed: IPv4 / IPv6 + port, default: [::1]:8080) β€” Host and TCP port the Bandurria server should listen on

[assets]

  • path (type: string, allowed: UNIX path, default: ./res/assets/) β€” Path to Bandurria assets directory

[database]

database.mysql

  • uri (type: string, allowed: MySQL connection URI, no default) β€” MySQL URI (format: mysql://user:password@server:port/database)

[email]

email.smtp

  • server_host (type: string, allowed: hostname, IPv4, IPv6, default: no default) β€” SMTP host to connect to
  • server_port (type: integer, allowed: TCP port, default: 587) β€” SMTP TCP port to connect to
  • server_starttls (type: boolean, allowed: true, false, default: true) β€” Whether to encrypt SMTP connection with STARTTLS or not
  • server_tls (type: boolean, allowed: true, false, default: false) β€” Whether to encrypt SMTP connection with TLS or not
  • auth_user (type: string, allowed: any string, default: no default) β€” SMTP username to use for authentication (if any)
  • auth_password (type: string, allowed: any string, default: no default) β€” SMTP password to use for authentication (if any)

email.identity

  • from_name (type: string, allowed: any string, default: Comments) β€” Name to send the emails from
  • from_email (type: string, allowed: email address, default: no default) β€” Email to send the emails from

[site]

  • name (type: string, allowed: any string, default: no default) β€” Name of the site
  • admin_emails (type: array[string], allowed: email addresses, default: no default) β€” Email addresses of site administrators
  • site_url (type: string, allowed: URL, default: no default) β€” URL of the site
  • comments_url (type: string, allowed: URL, default: no default) β€” URL of the comment system

[security]

  • secret_key (type: string, allowed: any hexadecimal string, default: auto-generated secret) β€” Secret key to use to sign all authenticated payloads (generate yours with openssl rand -hex 32)
  • check_pages_exist (type: boolean, allowed: true, false, default: false) β€” Whether to check over HTTP that a page a comment is left on actually exists (when sending the first comment for that page; it is safer to enable)

[antispam]

  • difficulty (type: integer, allowed: any number, default: 17) β€” Difficulty of the antispam PoW problem (not too low, not too high: 17 takes 3 seconds on a MacBook Pro M1 Pro, while 20 takes 21 seconds on the same device!)
  • problems_parallel (type: integer, allowed: any number, default: 10) β€” Number of antispam PoW problems to solve in parallel (this value should usually not be changed)
  • solutions_require (type: integer, allowed: any number, default: 6) β€” Number of antispam PoW problems to solve to pass the test (should be less or equal to problems_parallel, 60% of its value is a sweet spot, read why)

[avatar]

  • gravatar (type: boolean, allowed: true, false, default: false) β€” Whether to enable the Gravatar service to show comment author avatars (this uses a 3rd party service)
  • size_pixels (type: integer, allowed: any number, default: 20) β€” Size of avatars in pixels (as displayed in comments)
  • scale_factor (type: integer, allowed: any number, default: 3) β€” Scale factor of avatars, so that a high-resolution image is loaded for high DPI screens (a 20px sized avatar with a x3 scale factor results in a 60px sized image being loaded)

[i18n]

  • field_write_your_comment (type: string, allowed: any string, default: Write your comment...) β€” Translated string for the comment textarea
  • field_whats_your_name (type: string, allowed: any string, default: What's your name?) β€” Translated string for the name input
  • field_whats_your_email (type: string, allowed: any string, default: Enter your email) β€” Translated string for the email input
  • button_post_comment (type: string, allowed: any string, default: Post comment) β€” Translated string for the submit button
  • button_reply (type: string, allowed: any string, default: Reply) β€” Translated string for the reply button
  • label_leave_a_comment (type: string, allowed: any string, default: Leave a comment:) β€” Translated string for the main label
  • label_subscribe_replies (type: string, allowed: any string, default: I want to get notified over email when the site owner replies.) β€” Translated string for the replies subscribe label
  • banner_presubmit (type: string, allowed: any string, default: Your email is only stored if you opt-in to receive replies to your comment.) β€” Translated string for the pre-submit banner
  • banner_submitting (type: string, allowed: any string, default: Sending and proving you are not a bot. This might take a few seconds...) β€” Translated string for the submitting banner
  • banner_submitted_important (type: string, allowed: any string, default: Your comment has been submitted.) β€” Translated string for the submitted banner (important part)
  • banner_submitted_notice (type: string, allowed: any string, default: It will appear here after it gets accepted by moderation.) β€” Translated string for the submitted banner (notice part)
  • banner_submiterror (type: string, allowed: any string, default: Your comment could not be submitted. Mind try again?) β€” Translated string for the error banner

Run Bandurria

In order to run Bandurria on your server and add comments to your blog or website, follow those steps in order:

1. Create the SQL database

Before you can run Bandurria, you need to create its SQL database in MySQL:

  1. Create your MySQL database: CREATE DATABASE bandurria CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  2. Import the MySQL database schema
  3. Adjust the Bandurria configuration file so that the configuration value at database.mysql.uri points to your MySQL database

2. Start Bandurria

When you are ready, you can start Bandurria as such:

./bandurria -c /path/to/bandurria/config.cfg

If you use Docker, refer to the Docker instructions above ("Install from Docker Hub").

3. Create a proxy rule on NGINX

On your NGINX reverse proxy, add the following location block:

location /bandurria/ {
    proxy_pass http://localhost:8080;

    # Important: remove the '/bandurria' prefix from the URL upon proxying!
    rewrite ^/bandurria/(.*)$ /$1 break;

    proxy_http_version 1.1;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

If you are using a different HTTP server, the proxying configuration will be different. Please adapt it to your needs.

4. Include Bandurria on your site

Now that Bandurria is running, we can include the script on our website!

First, create the HTML container where your comments will be injected by Bandurria:

<!-- Your blog article is right before that -->

<aside id="comments" class="post-comments"></aside>

This container is usually placed at the end of blog articles (if you are including Bandurria on a blog).

Then, add the Bandurria loader script right before the </body> closing tag:

<html>
    <body>
        <!-- The rest of your body goes here -->

        <script
            src="/bandurria/assets/embed.js"
            data-bandurria-target=".post-comments"
        ></script>
    </body>
</html>

We are assuming here that Bandurria is running over a reverse proxy such as NGINX, proxying the /bandurria/ path to Bandurria's root.

Finally, in your <head> section, include Bandurria's style, which you may customize to fit your own design:

<html>
    <head>
        <!-- The rest of your head goes here -->

        <link rel="stylesheet" type="text/css" href="/path/to/your/own/bandurria.css" />
    </head>
</html>

You may copy and paste the example bandurria.css file that we provide, and start from there.

πŸ”₯ Report A Vulnerability

If you find a vulnerability in Bandurria, you are more than welcome to report it directly to @valeriansaliou by sending an encrypted email to valerian@valeriansaliou.name. Do not report vulnerabilities in public GitHub issues, as they may be exploited by malicious people to target production servers running an unpatched Bandurria instance.

⚠️ You must encrypt your email using @valeriansaliou GPG public key: πŸ”‘valeriansaliou.gpg.pub.asc.

Dependencies

~43–78MB
~1.5M SLoC