Files
btmx/README.md

156 lines
5.1 KiB
Markdown

# btmx
A self-contained hypermedia-driven blogging platform in a single portable binary. Written in Lua for the [RedBean](https://redbean.dev/) webserver, routed with [Fullmoon](https://github.com/pkulchenko/fullmoon), rendered as hypermedia with [HTMX](https://htmx.org/), and stored in SQLite. The build produces one executable (`blog.com`) that runs on Linux, BSD, macOS, and Windows; content lives in `blog.db` next to it.
## Features
- **Single binary:** no dependencies, no installation, no configuration files. Just run `./blog.com` and it just works™.
- **Markdown support:** posts are Markdown, rendered into HTML. [highlight.js](https://highlightjs.org/) is included for code blocks.
- **Radio Station:** web radio included as an optional feature for a given .m3u playlist URL.
- **RSS/Atom:** Atom feed is generated automatically at `/atom.xml`.
- **Themes:** attempts to respect user preferences for light or dark mode. Also includes a retro theme.
- **Admin dashboard:** `/edit` does almost everything needed to manage the blog.
## License
This project is licensed under the GPL-3.0 License - see the [LICENSE](LICENSE) file for details.
## Requirements
All requirements are pulled during maketime with `curl` using GNU `make` (if Justine has not updated her TLS certs, you may need to grab redbean by hand.)
Because it's awesome, the fatbin should work robustly on most OSes; daemons may need to use the `ape` loader, rather than running the binary directly.
## Building
```bash
git clone https://git.asperger.pro/hpcdisrespecter/btmx.git
cd btmx
make # fetch redbean/fullmoon/htmx/highlight.js/fonts and build blog.com
make setup # set the admin password (creates blog.db)
```
There is nothing to edit before building: all site identity (title, author, base URL, …) is configured after first login at `/edit/settings`.
## Running
### Configuration
Resetting the password and setting up the database:
```bash
./blog.com -i setup_admin.lua
```
### Development
```bash
./blog.com
```
Runs on `localhost:8080` and attempts to open your default browser to the blog (set `BLOG_NO_BROWSER=1` to suppress). Note that the Lua application is zipped **inside** `blog.com`; after editing anything under `.lua/` or `static/`, run `make` again.
### HTTPS
```
./blog.com -p 443 \
-K privkey.pem \
-C fullchain.pem \
-J
```
Runs on `localhost:443` with TLS and mandatory HTTPS using the provided certificates.
### Daemon
#### Systemd Service
First, produce a daemon user and copy the application files to its home directory:
```bash
sudo useradd -r -s /usr/sbin/nologin -d /var/lib/blog blog
cp blog.com /var/lib/blog/blog.com
cp blog.db /var/lib/blog/blog.db
sudo chown -R blog:blog /var/lib/blog
```
Then, deploy your TLS certificates to `/var/lib/blog/privkey.pem` and `/var/lib/blog/fullchain.pem` with similar permissions. Consider using a hook in your certificate management tool to automate this process.
Finally, make sure your system has a working [ape](https://justine.lol/ape.html) loader.
```
which ape
```
Create `/etc/systemd/system/blog.service`:
```ini
[Unit]
Description=Hypermedia Blog (Redbean)
After=network.target
[Service]
Type=simple
User=blog
WorkingDirectory=/var/lib/blog
Environment=BLOG_NO_BROWSER=1
ExecStart=ape \
/var/lib/blog/blog.com -p 443 -p 80 \
-K /path/to/privkey.pem \
-C /path/to/fullchain.pem \
-J
Restart=on-failure
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
```
Then enable and start the service:
```bash
sudo systemctl enable blog.service
sudo systemctl start blog.service
```
#### OpenRC Service
As above, you produce a daemon user, a copy of the application files, and deploy your TLS certificates.
Then, create `/etc/init.d/blog`:
```bash
#!/sbin/openrc-run
command="/usr/bin/ape"
command_args="/var/lib/blog/blog.com -p 443 -p 80 \
-K /path/to/privkey.pem \
-C /path/to/fullchain.pem \
-J"
command_user="blog:blog"
export BLOG_NO_BROWSER=1
depend() {
need net
}
```
Make the script executable and add it to the default runlevel:
```bash
sudo chmod +x /etc/init.d/blog
sudo rc-update add blog default
```
Finally, start the service:
```bash
sudo rc-service blog start
```
## Notes
- **Database**: `blog.db` is created automatically at first request in the working directory. Back it up and you have backed up the whole site (posts, uploads, settings, password hash).
- **Admin access**: Visit `/login`, enter your password. A session cookie (HttpOnly, SameSite=Strict, 7-day TTL) is set on success. Sessions are stored in `blog.db` and survive server restarts. `/edit` is the admin dashboard; `/edit/settings` configures the site.
- **Atom feed**: set the **Base URL** in Settings to your public origin (e.g. `https://example.com`) so feed IDs and links are absolute.
- **Rate limiting**: Login is limited to 5 attempts per IP per 15 minutes (in-memory; resets on restart).
- **CSRF tokens**: Every mutating form includes a `csrf_token` hidden field validated against an HttpOnly SameSite=Strict cookie.
- **Password changes**: Re-run `./blog.com -i setup_admin.lua` at any time. The new hash overwrites the old one immediately.