-
v0.4.0 Stable
released this
2026-07-26 16:19:36 +00:00 | 5 commits to main since this releaseAdded
volumen init— a newinitsubcommand is the operational
bootstrap (config, data dirs, admin password, optional systemd unit). Run
as root for a system install (default--systemd); pass--localfor a
per-user install (~/.config/volumen/,~/.local/share/volumen/). The
command renders the production-hardened config from the same commented
TOML template (host = "::1",env = "production",trust_proxy = true,
cookie_secure = true), prompts for an admin password (getpass), hashes
it via the existing scrypt helper, generates a 64-byte hex session key,
creates the system user (useradd --system) and the/etc/volumen//
/var/lib/volumen/layout, and (with--systemd) installs a hardened
/etc/systemd/system/volumen.serviceand runs
systemctl daemon-reload && systemctl enable --now.- PyPI publish metadata —
pyproject.tomlgainsreadme,keywords,
a completeclassifiersarray (Development Status, Framework :: FastAPI,
supported Python versions, OS, Topic, Typing), and a[project.urls]
block (Homepage, Repository, Issues, Changelog, Documentation). PyPI now
renders the README on the project page and links back to sourcedock.dev. volumen status— read-only inspection of an installation. Prints
human output by default or--jsonfor tooling; reportsconfig_exists,
data_dir_exists,users_file_exists,password_hash_set,
session_key_ok,systemd_unit_installed,service_active,
admin_user_present, and any issues found. Exit code is1when any
issue is reported.volumen check-update— compares the installed version (read via
importlib.metadata) with the latest release on PyPI. Uses only
urllib.request(stdlib, 5 s timeout); exit code is0up-to-date,
1update available (suggestsuv tool upgrade volumen),2PyPI
unreachable / offline / malformed response.--jsonreturns the same
fields pluschecked_atfor cron-friendly alerting.src/volumen/installer.py— a new module exposingsystem_install,
user_install,inspect, plus the private_mutate_config_texthelper
used to apply line-anchored substitutions to the rendered TOML template
(preserves comment blocks byte-for-byte).- Tests —
tests/test_installer.pyadds 15 tests covering both install
flows, the config-template mutator, the systemd-unit rendering,
idempotency / backup behaviour, theinspectround-trip, and the
--systemd+--localCLI guard.
Changed
- First-run setup is now
volumen init— replaces the legacy
install.sh. The CLI subcommand renders config, creates data dirs,
prompts for an admin password, generates a session key, and (with
--systemd) installs and enables a hardened systemd unit. No bash
required. - Publishing now targets PyPI —
release.ymlruns
uv publish --token "$PYPI_TOKEN"afterpython -m buildforv*
tags. Wheel + sdist continue to be uploaded to sourcedock.dev Releases as a
secondary mirror. End users getuv tool install volumenand
pipx install volumenas the one-line install path; no checkout
required. - Distribution path is
uv tool install(orpipx install) — the
CLI binary is installed globally; the operational bootstrap is delegated
tovolumen init. The legacyuv sync+ project-local venv at
/opt/volumen/.venvis gone. Config.loadround-trips config-filecontent_dir/users_file—
the rendered template places these two keys after the[server]header,
so TOML table folding puts them inside[server].Config.loadnow
hoists them to the root so files produced byvolumen init(and any
hand-edited config) round-trip through the canonical accessors without
relying on CLI overrides.- Migrated from Ruby to Python: the runtime has been rewritten on top of
FastAPI + Uvicorn, replacing Sinatra with FastAPI, kramdown with
python-markdown + pymdown-extensions, Puma with Uvicorn, Bundler with uv,
minitest with pytest, RuboCop with Ruff, and the TOML parser
(toml-rb) with the standard librarytomllib(plustomli-wfor
round-tripping edits). The public API (/api/volumen/*) and the
server-rendered admin (/admin/*) keep the same shape and HTTP semantics. - Installer —
install.shnow bootstrapsuvinstead of Bundler, runs
uv sync --frozento provision/opt/volumen/.venv, and points the
systemdExecStartat the venv-residentvolumenconsole script. The
Python runtime is auto-installed on Fedora/openEuler; other distros get a
heredoc with the equivalentapt/pacman/zypper/apklines. - CI — Forgejo Actions moved to
setup-uv+ Ruff + pytest on a single
Python 3.12 job (was: Ruby 3.3/3.4 matrix with Bundler, RuboCop, and
bundle exec rake test). The release workflow builds wheel + sdist via
python -m buildand uploads them with the Forgejo Releases API. - Migration guard — new
.forgejo/workflows/migration-guard.ymlgreps
the tracked tree for Ruby-only terms (bundle exec,RuboCop,Sinatra,
Puma,kramdown,gem build,Volumen::,Rack::Session::Cookie,
OpenSSL::KDF,lib/volumen/) and fails the build if any of them leak
back into production paths. - Configuration —
config.tomlgains six new keys surfaced in
config.toml.example:[server].env("production"enables strict
startup),[server].trust_proxy(honourX-Forwarded-Protofrom
nginx),[admin].min_password_length(default10),
[admin].max_password_length(default1024, caps scrypt work),
[admin].max_upload_bytes(default10485760, 10 MB), and
[admin].cookie_secure(defaultfalsein dev, settruebehind
HTTPS). Seedocs/configuration.mdfor the full schema. - Default bind address — the installer and
config.toml.example
default tohost = "::"(IPv6 with automatic IPv4 fallback) so the
service is reachable on both stacks out of the box. Bind to"::1"
when running behind a reverse proxy. - Test client now uses
httpx2— dev dependencies replace
httpx>=0.28withhttpx2>=2.7(the actively maintained fork by
Pydantic). Starlette 1.3.x prefershttpx2inTestClientand emits
StarletteDeprecationWarningwhen only legacyhttpxis installed;
shippinghttpx2keeps the test suite warning-free. The legacy
httpxpackage is still pulled in viafastapi[standard]for the
fastapiCLI and remains API-compatible. The unused
ignore::DeprecationWarning:starlette.testclientfilter is removed
from[tool.pytest.ini_options](it targeted the wrong warning
class and never matched).
Removed
install.sh— the bash installer is deleted. Useuv tool install volumen(orpipx install volumen) followed byvolumen init.pkg/build output andvolumen.gem(Ruby gem packaging is gone; the
project now ships as a wheel + sdist built withpython -m build).Gemfile,volumen.gemspec,Rakefile,.rubocop.ymland the
Sinatra-eralib/tree..editorconfig— Ruff (pyproject.toml's[tool.ruff]) is the single
source of truth for Python formatting; modern editors default to UTF-8,
LF, and a trailing newline. Markdown files in this repo use blank lines
between paragraphs (no\nline-break idiom), so trimming trailing
whitespace is safe.
Security
- scrypt password hashing now uses
hashlib.scryptand constant-time
verification viasecrets.compare_digest(was: Ruby'sOpenSSL::KDF). - Session cookies are signed by Starlette's
SessionMiddleware
(itsdangerous) withHttpOnlyandSameSite=Strict;Secureis added
bySecureCookieMiddlewarewhen the request was forwarded over HTTPS
(only when[server].trust_proxy = true). - CSRF tokens are generated with
secrets.token_hex(32)and verified
withsecrets.compare_digeston every state-changing admin POST. - Markdown sanitisation policy — the renderer still passes inline HTML
through (authors are the trusted admin); document the trust model
explicitly indocs/security.mdso deployments that ingest untrusted
content know to add a sanitiser.
Note: refine the Security bullets above once the Python agent confirms the
final cryptographic controls (e.g. whetherimage/*upload signatures get
re-validated server-side and whether the CSRF token is rotated on login).Downloads