Verify content type, urlset structure, post URL presence, and draft exclusion.
5.6 KiB
5.6 KiB
Changelog
All notable changes to volumen are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[development]
Added
<pubDate>in RSS feed items, derived from the post date.<lastmod>in sitemap entries for SEO.- In-memory rate limiting on the login endpoint: 10 attempts per 60 seconds per IP.
<language>element in the RSS feed channel.- Periodic cleanup of stale
login_attemptsentries to prevent unbounded memory growth. - Slug format validation (
[a-z0-9._-]) preventing path traversal and XSS in templates. - Username validation in
change_usernamematching the create-user regex. - Escape-on-output for slug and username in admin template attributes.
- Memoization of
Store#allwith mtime-based invalidation, eliminating N×read+parse per request. - Atomic file writes (tmp + rename) for posts and
users.toml. - Post language directory routing: non-default-language posts now land in
content_dir/<lang>/instead of overwriting root-level files. - CLI test coverage (version, help, unknown command, option parsing, config bootstrap).
- Test coverage for RSS feed and sitemap endpoints.
Changed
- Split
lib/volumen/server.rbintoapi_routes.rbandadmin_routes.rbmodules, keeping the server class focused on configuration and shared helpers.
Fixed
admin.session_ttlwas defined but never read; it now controlsRack::Session::Cookieexpiration viaexpire_after.- Derived post excerpts now strip HTML tags before stripping markdown markup, avoiding raw HTML leaks in auto-generated excerpts.
- Draft posts no longer leak through the public detail endpoint (
/api/volumen/posts/:slug). Config::DEFAULTSinner hashes are now frozen anddeep_mergebuilds fresh copies, preventing mutation from silently poisoning laterConfig.loadcalls.
0.1.0 — 2026-06-20
First public release: a small, file-based Markdown blog engine in CRuby. Posts are plain files, the engine exposes them as a JSON API and ships a self-hosted admin — no database, no external services.
Added
Content & storage
- File-based store: each post is a
.mdfile with a TOML frontmatter block delimited by+++. No database. - Post fields:
title,slug,date,lang,author,tags,draft,excerpt,cover,all_langs, andtranslations(alang → slugmap). - Drafts (
draft = true) are hidden from the public API. - Excerpt falls back to the first paragraph (~200 chars) when none is set.
- Estimated reading time (~200 words/min) computed per post.
- Multilingual posts: per-language slugs via
translations, plusall_langsto surface a single post under every language.
Markdown rendering
- GFM rendering via kramdown: tables, fenced code blocks, task lists, and strikethrough.
- Automatic heading IDs for in-page anchors.
- Raw HTML in post bodies is passed through (authors are the trusted admin).
Public JSON API (/api/volumen)
- Read-only endpoints:
site,posts,posts/{slug},tags,feed.xml(RSS), andsitemap.xml. postssupports pagination (page,limit, returningtotalandpage_size) and filtering bylangandtag.- Permissive CORS so a separate front end can consume the API.
Admin (/admin)
- Server-rendered admin with a sidebar layout and full post CRUD.
- Dual-mode editor: Markdown source (primary) and a visual WYSIWYG, switchable per post, with a toggleable live preview persisted across sessions.
- Image upload from the editor toolbar into
content_dir/media; any image format is accepted (WebP and AVIF included) and served with the correctContent-Type. - Cover image field with upload, URL, clear, and preview.
Multi-user & roles
- Multiple admin users stored in
users.toml, managed from a settings page. - Two roles:
admin(full access incl. user management) andauthor(posts only). - Change password and username; last-admin and self-deletion safeguards.
Configuration & CLI
- TOML configuration, auto-generated with a commented template on first run.
- Configurable
host/port(defaults to9090),content_dir,users_file, andsitemetadata. - CLI:
serve,hash-password,version,help, with--config,--content,--host, and--portoverrides.
Deployment
install.shinstalls the Ruby toolchain from distro packages on Fedora and openEuler (and prints guidance on other distros), creates the service user and directories, and installs a systemd unit.- Deployment docs for systemd and FreeBSD (
rc.d), plus an nginx reverse-proxy setup that runs the admin same-origin, with brute-force rate-limiting and an optional IP allowlist on the login endpoint.
Project & tooling
- Documentation set: architecture, configuration, API reference, deployment, and security.
justtask set (install,run,dev,build,test,uninstall) and Forgejo Actions CI (RuboCop + tests on Ruby 3.3/3.4, gem build on release).CONTRIBUTING.mdand a minitest suite; clean RuboCop.
Security
- Passwords hashed with scrypt and verified in constant time.
- Per-form CSRF tokens on every state-changing admin request.
- Session cookies are
HttpOnlyandSameSite=Strict, and gain theSecureattribute automatically on HTTPS requests (detected viarequest.ssl?/X-Forwarded-Proto), so the cookie never travels over plain HTTP in production. - Configurable, stable
session_keyso sessions survive restarts. - Generic login error message to avoid username enumeration.