Files
bike_admin/README.md
T
Jake 4576e6fae3 Docs: production deployment recipe + Arch gotchas
- README: expand the production section with a full systemd recipe
  (useradd, chown, uv sync --project, install -d for ReadWritePaths,
  .env perms 640), a Caddy block, and backup note.
- AGENTS.md: add the Arch python-uv package-name note and the root-rooted
  venv caveat, plus a new "Deploy / production" section documenting the
  volundr unit, data location, .env perms, and forward-proxy expectations.
2026-09-10 13:25:19 +01:00

3.6 KiB

volundr

A small, secure, single-user web app for logging bike service and repair "events" (type, date, mileage, cost, notes) and getting a clear service-due hint per bike, plus MOT / insurance renewal tracking.

Built with FastAPI + SQLite + Jinja2 + HTMX. No build step. Server-rendered pages.

Features

  • Log events for a bike: type (service, fuel, tyre, chain, brake, repair, mot, insurance, other), date, optional mileage + cost, free-text notes.
  • Per-bike service status derived from your newest mileage and the most recent service event: ok (still under the interval), due-soon (within the lead time — book it soon), or overdue (past it — book a service now).
  • MOT / insurance renewals: the most recent mot / insurance event is tracked and flagged ok / due-soon / overdue by date (default: valid for 12 months, "due soon" within the final 60).
  • Set a service interval in miles per bike (e.g. 5000).
  • Dashboard "action needed" strip surfaces any overdue bike.
  • Cost and event totals, newest-first history.
  • Secure login (Argon2id hashing, server-side session, CSRF, login throttling, hardened security headers, strict CSP). Designed to run behind a reverse proxy on the open web.

Quick start

# 1. Install dependencies
uv sync --all-extras

# 2. Configure credentials (required)
export BIKE_ADMIN_USERNAME=admin
export BIKE_ADMIN_PASSWORD=<change-me>
export BIKE_ADMIN_SECRET_KEY=$(python -c "import secrets;print(secrets.token_urlsafe(32))")

# 3. Run
uv run uvicorn app.main:app --host 127.0.0.1 --port 8000
# open http://127.0.0.1:8000

Or copy .env.example to .env and fill in the values (env vars and the local .env file are both read automatically).

Running behind a reverse proxy (production)

TLS terminated at the proxy (Caddy/nginx), app on a private host behind it. deploy/bike_admin.service is the systemd unit used in production; deploy/env.production.example is the production .env template.

  • Set BIKE_ADMIN_IS_PRODUCTION=true so the session cookie is Secure.
  • Bind as needed for the proxy to reach you:
    • proxy on the same host → --host 127.0.0.1
    • proxy on another host (e.g. a single fronting Caddy) → --host 0.0.0.0 or bind to a specific LAN interface; never the public interface directly, since there is no app-level TLS.
  • Proxy forwards to the app and sets X-Forwarded-For / X-Forwarded-Proto; the app trusts these by default (BIKE_ADMIN_TRUST_PROXY). uvicorn's --forwarded-allow-ips 192.168.0.0/16 --proxy-headers accepts them (adjust to your LAN range).

systemd (production recipe)

# one-time: deps + service account   [on Arch: uv ships as python-uv]
sudo pacman -S --needed git python-uv
sudo useradd --system bike

# one-time: app
sudo chown -R bike:bike /opt/bike_admin
sudo -u bike uv sync --all-extras --project /opt/bike_admin
sudo install -d -o bike -g bike /opt/bike_admin/data   # systemd needs it to exist

# one-time: .env (see deploy/env.production.example)
sudo chown bike:bike /opt/bike_admin/.env
sudo chmod 640 /opt/bike_admin/.env                     # owner rw, group r, others none

sudo cp deploy/bike_admin.service /etc/systemd/system/bike-admin.service
sudo systemctl daemon-reload
sudo systemctl enable --now bike-admin

Caddy block for the fronting proxy:

volundr.local.jakes-asgard.uk {
    reverse_proxy 192.168.0.52:8000
}

Back up your data by copying the single file in the data dir — <data_dir>/bike_admin.db (SQLite, WAL; data/bike_admin.db by default).

Tests / lint

uv run pytest -q
uv run ruff check app tests
uv run ruff format --check app tests