14 releases (4 stable)
new 1.2.1 | Jan 29, 2025 |
---|---|
0.6.0 | Jan 27, 2025 |
#24 in Web programming
834 downloads per month
140KB
3K
SLoC
Bandurria
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!).
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:
Posting a new comment with Bandurria:
Waiting for the anti-bot Proof of Work to complete:
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 to0.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 toerror
in productioninet
(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]
uri
(type: string, allowed: MySQL connection URI, no default) β MySQL URI (format:mysql://user:password@server:port/database
)
[email]
server_host
(type: string, allowed: hostname, IPv4, IPv6, default: no default) β SMTP host to connect toserver_port
(type: integer, allowed: TCP port, default:587
) β SMTP TCP port to connect toserver_starttls
(type: boolean, allowed:true
,false
, default:true
) β Whether to encrypt SMTP connection withSTARTTLS
or notserver_tls
(type: boolean, allowed:true
,false
, default:false
) β Whether to encrypt SMTP connection withTLS
or notauth_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)
from_name
(type: string, allowed: any string, default:Comments
) β Name to send the emails fromfrom_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 siteadmin_emails
(type: array[string], allowed: email addresses, default: no default) β Email addresses of site administratorssite_url
(type: string, allowed: URL, default: no default) β URL of the sitecomments_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 withopenssl 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, while20
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 toproblems_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 textareafield_whats_your_name
(type: string, allowed: any string, default:What's your name?
) β Translated string for the name inputfield_whats_your_email
(type: string, allowed: any string, default:Enter your email
) β Translated string for the email inputbutton_post_comment
(type: string, allowed: any string, default:Post comment
) β Translated string for the submit buttonbutton_reply
(type: string, allowed: any string, default:Reply
) β Translated string for the reply buttonlabel_leave_a_comment
(type: string, allowed: any string, default:Leave a comment:
) β Translated string for the main labellabel_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 labelbanner_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 bannerbanner_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 bannerbanner_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:
- Create your MySQL database:
CREATE DATABASE bandurria CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- Import the MySQL database schema
- 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