409 lines
12 KiB
Groff
409 lines
12 KiB
Groff
.\" 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 .
|