Initial commit
Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s

Assisted-by: GLM 5.3
This commit is contained in:
2026-09-29 10:03:32 +02:00
commit f8ed33df83
206 changed files with 44165 additions and 0 deletions
+408
View File
@@ -0,0 +1,408 @@
.\" Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
.\" SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
.TH VOLUMEN 1 "2026-09-27" "volumen 1.0.0" "Volumen Manual"
.SH NAME
volumen \- lightweight publishing platform for scientists
.SH SYNOPSIS
.B volumen
.RI [ command "] [" options ]
.SH DESCRIPTION
Volumen is a lightweight publishing platform for scientists: a headless JSON
API plus a server-rendered admin, over posts that are files. Every post is a
Markdown file with a TOML frontmatter block inside a content directory. The
engine serves that directory as a JSON API under
.BR /api/volumen ,
serves the admin under
.BR /admin ,
and stores uploaded media under
.BR /media .
It has no database and no runtime dependency besides the binary itself; a
public site is a separate front end that consumes the API.
.PP
Run
.B volumen
with a command named below. Each command accepts
.B \-h
for its own usage. An unknown command and an unknown flag exit with status 2,
and every command rejects a stray positional argument.
.SH COMMANDS
.TP
.B serve
Start the publishing server. With no accounts yet, the first visit to
.B /admin
shows the first-run wizard, which creates the administrator account.
.TP
.B status
Show installation status.
.TP
.B doctor
Check installation health.
.TP
.B check-update
Compare with the latest Gitea release.
.TP
.B export
Export posts, media, and users to a tar.gz.
.TP
.B import
Import a backup archive.
.TP
.B publish-due
Publish scheduled posts whose date arrived.
.TP
.B validate
Validate the content directory.
.TP
.B version
Show version.
.SH OPTIONS
.SS serve
.TP
.BI "\-\-config" " path"
Path to
.IR config.toml .
Without the flag the server reads
.I /etc/volumen/config.toml
if it exists, otherwise
.IR ~/.config/volumen/config.toml ;
when neither exists it runs on the built-in defaults, whose state paths lie
under
.I ~/.local/share/volumen
(honouring
.IR XDG_DATA_HOME ).
.TP
.BI "\-\-content" " dir"
Override
.IR content_dir .
.TP
.BI "\-\-host" " addr"
Override
.BR [server].host ,
an IP address such as
.B ::
or
.BR ::1 .
.TP
.BI "\-\-port" " n"
Override
.BR [server].port ,
1 to 65535. A value outside the range is rejected.
.PP
The server bounds every connection: a 10 second read-header timeout, a 60
second read timeout, a 120 second write timeout and a 120 second idle
timeout, with request lines and headers capped at 1 MiB.
.B SIGINT
and
.B SIGTERM
drain in-flight requests for up to 15 seconds and then exit 0. When
.B [scheduler].enabled
is set, the due posts are published at start-up and then every
.B [scheduler].interval
seconds. Logging goes to standard error, as text or as JSON lines when
.B [server].log_format
is
.BR json .
Every request carries a 16-character id, answered in
.B X-Request-Id
and attached to the request's access line.
.PP
A fresh installation needs no bootstrap command: start the server, open
.BR /admin ,
and the first-run wizard creates the administrator account, the interface
language and the colour scheme, and signs the operator in. The wizard is
reachable until that first account exists; on a machine reachable from the
network, start the service and claim the installation right away.
.SS status
.TP
.BI "\-\-config" " path"
Config path to inspect (default as under
.BR serve ).
.TP
.BI "\-\-data" " dir"
Data directory to inspect.
.TP
.BI "\-\-users-file" " path"
Users file to inspect.
.TP
.B \-\-json
Machine-readable output:
.BR {"ok":\ true,\ "checks":\ {\ ...} } ,
with a
.B config_validate
entry naming the first invalid value, a
.B posts_unreadable
entry when a post file cannot be parsed, and
.B "users:\ \(dqunreadable\(dq"
when the accounts file cannot be read.
.PP
Reports whether the config exists, parses and validates, the number of posts
with the draft count, and the number of users; with no accounts yet the user
count names the first-run wizard as the next step. Read-only: a mistyped
.I content_dir
is reported, never created. Exit 1 when the config is missing, unparseable
or invalid, when content cannot be parsed, or when the users file cannot be
read.
.SS doctor
.TP
.BI "\-\-config" " path"
Config path to check (default as under
.BR serve ).
.TP
.B \-\-json
Machine-readable output:
.BR {"ok":\ bool,\ "checks":\ [{name,\ status,\ detail}]} .
.PP
Checks that the config exists, parses and validates, that every post file
can be parsed, that every post renders, that the users file can be read,
that the installation has its first account, and
that no stored scrypt parameters sit below the policy floor. A missing
account is a warning pointing at the wizard, not a failure. Exit 1 when a
check fails.
.SS check-update
.TP
.B \-\-json
Machine-readable output:
.BR {"current":\ ...,\ "latest":\ ...,\ "available":\ bool} ,
keeping the same exit-code contract.
.PP
Compares the running version with the latest release at
.IR https://sourcedock.dev/petrbalvin/volumen/releases .
Exit 0 when up to date (or the local build is newer); exit 1 when a newer
release exists or the release check failed (offline, timeout, malformed
response); exit 2 on a usage error.
.SS export
.TP
.BI "\-\-config" " path"
Config path (default as under
.BR serve ).
.TP
.BI "\-\-out" " archive"
Output archive path (default
.IR volumen-backup.tar.gz ,
written mode 0600).
.PP
Writes a tar.gz containing
.B posts/
(including
.B .revisions/
and
.BR media/ ),
.BR users.toml ,
.BR templates.toml ,
and
.BR tokens.toml .
The config file is deliberately not included: it may hold the session key
override. A file that exists but cannot be read aborts the export rather than
writing an archive that looks complete. Exit 1 on I/O errors.
.SS import
.TP
.BI "\-\-config" " path"
Config path (default as under
.BR serve ).
.PP
Restores an archive made by
.B volumen export
or the admin Backup panel. The named archive is the one required positional
argument. Entries are written to the paths the deployment configures for
them; extraction is confined to the content directory, only post files,
revision archives and allowed image extensions are written, and the
decompressed total is bounded at 512 MiB. Exit 1 on a malformed archive or
one that holds no file the layout recognises.
.SS publish-due
.TP
.BI "\-\-config" " path"
Config path (default as under
.BR serve ).
.TP
.B \-\-dry-run
List the due slugs without touching files.
.TP
.B \-\-json
Machine-readable output:
.BR {"published":\ [...],\ "failed":\ n} ,
or
.B {"due":\ [...],\ "dry_run":\ true}
with
.BR \-\-dry-run .
.PP
Publishes every post whose
.B publish_at
date is today or earlier: the key is removed and
.B date
defaults to it when the post has none. The configured webhooks receive
.B post.published
for every post this publishes. Exit 1 when a post could not be written, so a
timer notices.
.SS validate
.TP
.BI "\-\-config" " path"
Config path (default as under
.BR serve ).
.TP
.B \-\-json
Machine-readable output:
.BR {"problems":\ [{slug,\ path,\ error}]} .
.PP
Scans the content directory and reports files that cannot be parsed, posts
that fail to render, duplicate slugs, slugs that do not match the slug
format, missing titles, a
.B publish_at
that is not a date, and alias conflicts. Prints
.B content OK
when clean, exit 1 otherwise.
.SS version
Prints
.BI "volumen " version ,
the release the toolchain recorded in the binary's build information. A
release binary reports its tag, a checkout reports a pseudo-version, a build
outside version control reports
.BR "(devel)" ,
and a dirty tree appends
.BR +dirty .
.SS Global flags
.TP
.BR \-h , " \-\-help" ", " help
Prints the usage block and exits 0.
.B \-h
on a subcommand prints that subcommand's flags; on
.B version
it prints the version.
.TP
.BR \-v , " \-\-version"
Prints
.BI "volumen " version
and exits 0.
.SH EXIT STATUS
.TP
.B 0
Success.
.TP
.B 1
A failure the program detected: an unreadable config, a failed write, a
content directory with a problem, a release check that could not run.
.B check-update
also exits 1 when a newer release simply exists.
.TP
.B 2
The arguments were wrong: an unknown command, an unknown flag, or a stray
positional argument.
.SH CONFIGURATION
One TOML file, \fIconfig.toml\fP, copied from the commented
.I config.toml.example
shipped with the release.
Every key, its type, its default and its validation rules are documented in
.I docs/CONFIGURATION.md
of the repository. Sources, strongest first: the flags of
.BR serve ,
then the file, then the built-in defaults. A key with the wrong type or a root key swallowed by a table
header stops the start with a message naming the key; an unknown key is
ignored.
.PP
The five state files
.BR users.toml ,
.BR secret.key ,
.BR templates.toml ,
.BR tokens.toml
and
.B webhooks.toml
live beside the users file, are written atomically at mode 0600 by the
server, and are not read from the configuration file.
.B secret.key
holds the session secret the server generated on first start;
.B [admin].session_key
overrides it.
.SH ENVIRONMENT
The configuration itself comes from the file and the flags of
.B serve
only. The XDG variables
.I XDG_CONFIG_HOME
and
.I XDG_DATA_HOME
locate the per-user config and state paths used by the defaults.
.SH FILES
.TP
.I /etc/volumen/config.toml
System configuration, tried first when
.B \-\-config
is absent.
.TP
.I ~/.config/volumen/config.toml
Per-user configuration, tried when the system file does not exist.
.TP
.I /var/lib/volumen/posts
Content directory: posts,
.BR media/ ,
and
.BR .revisions/ .
.TP
.I /var/lib/volumen/users.toml
Admin accounts, created by the first-run wizard or the admin Settings page.
.TP
.IR /var/lib/volumen/secret.key ,
.IR /var/lib/volumen/templates.toml ,
.IR /var/lib/volumen/tokens.toml ,
.IR /var/lib/volumen/webhooks.toml
Session secret generated by the server, post templates, API tokens,
admin-managed webhooks.
.TP
.I ~/.local/share/volumen
Per-user state directory: the default content and users paths when no
configuration file exists.
.TP
.I /etc/systemd/system/volumen.service
Unit written by the operator;
.I docs/DEPLOYMENT.md
carries the text to copy.
.TP
.I <content_dir>/media/<uuid>.webp, <uuid>.avif, <uuid>.svg
Uploaded images; WebP, AVIF and SVG are the only accepted formats, and the
name is assigned by the engine. An SVG is served sandboxed, so opened at its
own URL it runs as no one.
.SH EXAMPLES
Serve a checkout against its own content:
.PP
.B volumen serve \-\-config ./config.toml \-\-content ./posts
.PP
Start a fresh per-user installation and open the wizard in the browser at
.IR http://localhost:9091/admin/ :
.PP
.B volumen serve
.PP
Publish what a timer missed, then check the content:
.PP
.B volumen publish-due \-\-dry-run
.br
.B volumen publish-due
.br
.B volumen validate
.PP
Back up one deployment and restore it into a second:
.PP
.B volumen export \-\-config /etc/volumen/config.toml \-\-out /backup/volumen.tar.gz
.br
.B volumen import \-\-config /srv/staging/config.toml /backup/volumen.tar.gz
.PP
Find out why a fresh install will not start:
.PP
.B volumen doctor \-\-config /etc/volumen/config.toml
.br
.B volumen status \-\-config /etc/volumen/config.toml \-\-json
.SH AUTHOR
Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
.SH LICENCE
PolyForm Noncommercial 1.0.0, see
.I LICENSE
in the repository: every non-commercial use is permitted; a commercial use
needs a licence from the author. The artwork in
.I internal/web/static/
comes from the shared icon set and stays under Creative Commons BY-NC 4.0
alone. The licence texts of the modules the binary is compiled from are in
.IR NOTICE.md .
.SH "SEE ALSO"
.I docs/CLI.md
and
.I docs/DEPLOYMENT.md
in the repository;
.IR docs/API.md
for the JSON API;
.IR https://sourcedock.dev/petrbalvin/volumen .