[![Crates.io Version](https://img.shields.io/crates/v/guestbook)](https://crates.io/crates/guestbook) [![Crates.io License](https://img.shields.io/crates/l/guestbook)](./LICENSE) `guestbook` is a self-hosted guestbook web service with: - entries stored in plaintext, - a [drawing canvas](#drawing) for visitors to sketch alongside their message, - [voice notes](#voice-notes) for visitors to record a short audio clip, - notifications and moderation via [Telegram](#telegram), - spam prevention via honeypot and/or [captcha](#captcha), - fairly customisable [styling](#customisation), and more, written in Rust, and inspired by [t0.vc/g](https://t0.vc/g). `guestbook` is a single-page guestbook designed for personal sites. There's a form for visitors to submit a name, and optionally a message, a link to their own site, a drawing, or a voice note. Entries are written to plain text files with TOML frontmatter, and are initially marked as pending. The frontmatter can be manually edited to mark entries as approved or denied, or a Telegram bot can be hooked up for notifications and moderation (drawings and voice notes are fetched on demand via `/drawing_` and `/voice_note_` so the chat doesn't fill up with attachments). Running the Telegram bot just requires handing over a bot token. Everything is configured through environment variables (see [`.env.example`](#default-config) for the defaults). If you're hosting with Nix, there's a flake that can set up the `guestbook` service end-to-end, running on a systemd service with a Caddy reverse proxy. Optionally, just ignore the flake and set up all the extra stuff yourself. Aesthetically, essentially all of the HTML and CSS can be configured. There's a default template included for both, but you can take them and change both to your liking. Just point the template and/or style variables at your replacements. --- ### Installation

Build · NixOS

#### Build `guestbook` is written in [Rust](https://www.rust-lang.org). Clone the repo and build with `cargo`. ```bash git clone https://git.ily.rs/lew/guestbook cd guestbook cp .env.example .env # edit with your values cargo run --release ``` Alternatively, install directly from [crates.io](https://crates.io/crates/guestbook) with `cargo install guestbook`. The binary uses the current working directory for its `.env` and data, so run it from whichever directory you want it to operate out of. This will run the site on localhost on the port you've configured, or `8123` by default. I'll leave exposing it to the web to you, but personally I run [my guestbook](https://ily.rs/g) through a reverse proxy with [Caddy](https://caddyserver.com). #### NixOS [NixOS](https://nixos.org) users can use the included flake, which builds the binary via [crane](https://github.com/ipetkov/crane) and exports a module that sets up the systemd service, user, and optionally a [Caddy](https://caddyserver.com) reverse proxy. ```nix # flake.nix { inputs.guestbook.url = "git+https://git.ily.rs/lew/guestbook"; outputs = { self, nixpkgs, guestbook, ... }: { nixosConfigurations.myhost = nixpkgs.lib.nixosSystem { modules = [ guestbook.nixosModules.default { services.guestbook = { enable = true; package = guestbook.packages.x86_64-linux.default; siteTitle = "my guestbook"; telegram = { enable = true; botTokenFile = "/run/secrets/guestbook-bot-token"; chatId = 12345; }; caddy = { enable = true; domain = "guestbook.example.com"; }; }; } ]; }; }; } ``` --- ### Configuration `guestbook` is configured entirely through environment variables. For local development, copy `.env.example` to `.env`. For NixOS, the [module](#nixos-module) maps all options to environment variables for you. Running `guestbook` with no env vars will give you a working guestbook on `localhost:8123` with the default config below. Notably, no Telegram moderation. That requires a bot token, and is probably the most important thing to set up. #### Default Config ```bash # Port to listen on (binds to 127.0.0.1). # BOOK_PORT=8123 # Directory for guestbook entry files. # BOOK_DATA_DIR=./data # Site title shown in nav and page title. # BOOK_SITE_TITLE=guestbook # Telegram bot token. Optional — if unset, telegram moderation is disabled. # BOOK_TELEGRAM_BOT_TOKEN=your-bot-token-here # Telegram chat ID for moderation messages. Required if bot token is set. # BOOK_TELEGRAM_CHAT_ID=0 # Seconds between retry attempts for failed Telegram notifications. # BOOK_TELEGRAM_RETRY_INTERVAL=20 # Maximum number of retry attempts for failed Telegram notifications. # BOOK_TELEGRAM_RETRY_LIMIT=3 # Seconds between pending entry reminders. Set to 0 to disable. # BOOK_TELEGRAM_REMINDER_INTERVAL=86400 # Enable honeypot field for spam prevention. # BOOK_ENABLE_HONEYPOT=true # Allow new guestbook submissions. When false, the form is hidden and submissions are rejected. # BOOK_ENABLE_SUBMISSIONS=true # Show website field in form and render website links in entries. # When false, the input is hidden, submitted values are ignored, and existing links are not displayed. # BOOK_ENABLE_WEBSITE_LINKS=true # Allow raw HTML/JS in entry names and message bodies. When false, HTML is escaped. # Website URLs are always escaped regardless of this setting. # BOOK_ENABLE_HTML_INJECTION=false # Enable captcha on submission form. # BOOK_ENABLE_CAPTCHA=false # Captcha question displayed as a label. # BOOK_CAPTCHA_QUESTION=What is my name? # Captcha answer to validate against. # BOOK_CAPTCHA_ANSWER=lew # Require exact match (true) or just "contains" (false). # BOOK_CAPTCHA_EXACT=false # Require case-sensitive match. # BOOK_CAPTCHA_CASESENSITIVE=false # Maximum length for names. 0 for unlimited. # BOOK_MAX_NAME_LENGTH=0 # Maximum length for messages. 0 for unlimited. # BOOK_MAX_MESSAGE_LENGTH=0 # Maximum length for website URLs. 0 for unlimited. # BOOK_MAX_WEBSITE_LENGTH=0 # Path to a CSS file. Takes precedence over BOOK_STYLE. Uses built-in default if unset. # BOOK_STYLE_FILE=./templates/default.css # Custom CSS injected into a style tag. # Classes: .guestbook-form, .guestbook-label, .guestbook-input, .guestbook-textarea, # .guestbook-button, .guestbook-canvas, .guestbook-drawing-wrap, # .guestbook-drawing-tools, .guestbook-drawing-content, .guestbook-swatch, # .guestbook-size-slider, .guestbook-voice-wrap, .guestbook-voice-controls, # .guestbook-voice-record, .guestbook-voice-timer, .guestbook-voice-playback, # .entries, .entry-header, .entry-date, .entry-name, .entry-website, # .entry-body, .entry-drawing-wrap, .entry-drawing, .entry-voice-note-wrap # BOOK_STYLE= # Submit button text. # BOOK_BUTTON_TEXT=Submit Entry # Label for the name field. # BOOK_LABEL_NAME=Your name # Label for the website field. # BOOK_LABEL_WEBSITE=Link a website (optional) # Label for the message field. # BOOK_LABEL_MESSAGE=Leave a message (optional) # Label for the drawing field (when BOOK_ENABLE_DRAWINGS=true). # BOOK_LABEL_DRAWING=Leave a drawing (optional) # Label for the voice note field (when BOOK_ENABLE_VOICE_NOTES=true). # BOOK_LABEL_VOICE_NOTE=Leave a voice note (optional) # Initial text on the voice note record button. # BOOK_VOICE_NOTE_RECORD_TEXT=Start recording # Message textarea width in pixels. # BOOK_TEXTAREA_WIDTH=320 # Message textarea height in pixels. # BOOK_TEXTAREA_HEIGHT=150 # Custom HTML template file with {{title}}, {{form}}, {{entries}}, {{style}}, and {{base}} placeholders. # Uses built-in default if unset. # BOOK_TEMPLATE=./templates/default.html # Custom success page template shown after a successful submission. # Supports {{title}}, {{style}}, and {{base}} placeholders. Use