OpenRSVP is a minimalist, accountless RSVP service built with FastAPI, SQLite, and magic-link authentication. This repository provides the v0.1 single container deployment requested in the specification.
- Magic-link authentication for root admin, event admins, and RSVP guests
- Device-side helpers: copy-to-clipboard toasts and optional local storage so browsers can reopen admin/RSVP magic links (clearable in the UI)
- Catppuccin themes (Latte, Mocha, Frappé, Macchiato) with per-browser preference and system fallback
- FastAPI + Jinja2 UI using Bootstrap 5 (CDN)
- SQLite storage with SQLAlchemy models
- Private RSVP toggle to hide a guest from public lists while keeping the organizer’s view intact
- Background decay + cleanup using APScheduler (runs hourly)
- Public/private channel system with dedicated listings at
/channel/<slug>and a/helpexplainer - Typer CLI (
openrsvp) for server management and token rotation - Optional end-time support for events with automatic timezone conversion
git clone <repo>
cd OpenRSVP
uv venv
source .venv/bin/activate
uv pip install -e .
openrsvp runserverFor auto-restart on Python file changes during development:
openrsvp runserver --devVisit http://localhost:8000 to access the UI. Use the CLI to retrieve the root
admin token:
openrsvp admin-tokenBuild and run the container:
docker build -t openrsvp .
docker run -p 8000:8000 openrsvpThe Docker image is based on ghcr.io/astral-sh/uv:python3.12-bookworm-slim
and syncs dependencies via uv sync (the .venv it creates is placed on
PATH). The container entrypoint starts openrsvp runserver, which launches
FastAPI and the decay scheduler.
- Channel names and visibility (public/private) are chosen when creating an event.
- Channel names are slugified (lowercased, ASCII-only, non-alphanumeric
collapsed to hyphens, with leading/trailing hyphens removed); reusing the
same slug + visibility reuses the existing channel and bumps its
last_used_at. - Public channels show up on the homepage filter and at
/channel/<slug>. - Private channels never appear in lists; you must know the slug/URL to view
them (
/channel/p/<slug>). - Events inherit none of the channel visibility rules—private events stay private regardless of channel type.
- Public events inside private channels hide the channel badge to avoid leaking the slug.
For guidance and safety tips, share http://localhost:8000/help (or the
deployed host) with event creators.
- Guests receive a one-time magic link after submitting an RSVP; it is also stored locally (when possible) so the browser can surface “Edit your RSVP” on the event page. Organizers’ admin links are treated the same way.
- Local storage is per-device only. Use the Preferences menu or “Forget this key” buttons on event/RSVP pages to clear saved links.
- Guests can mark an RSVP private so it hides from the public attendee list; organizers still see it. Organizers cannot edit RSVP contents but can delete entries on request.
- Event owners can delete an entire event from the admin page or via
DELETE /api/v1/events/{event_id}usingAuthorization: Bearer <admin-token>. Deletion wipes all RSVPs and cannot be undone. - The Preferences menu includes a Catppuccin theme picker. The chosen palette persists locally and falls back to your system light/dark setting if unset.
The Typer CLI exposes the decay cycle so you can trigger cleanup jobs outside of the scheduler:
openrsvp decay # decay without vacuum
openrsvp decay --vacuum # decay followed by SQLite VACUUM--vacuum is useful after large data churn or manual deletions, since VACUUM
reclaims disk space at the cost of briefly locking the database.
For local testing you can seed the SQLite database with synthetic channels, events, and RSVPs using the Typer CLI. This command relies on the Faker dev dependency.
openrsvp seed-data --channels 6 --max-events 4 --extra-events 2 --max-rsvps 5 --private-percent 15Each run creates new channels (public or private), fills them with events, and
attaches randomized RSVP records. The --extra-events flag optionally creates
events that are not attached to any channel, --max-rsvps caps RSVP counts per
event, and --private-percent controls what portion of events are marked
private.
Use uv so dev dependencies install on demand:
uv run pytestIf you want to keep cache files in the repo instead of the default user cache:
UV_CACHE_DIR=.uv-cache uv run pytestYou can also run the same defaults via the CLI:
uv run main.py test # or: openrsvp test- Keep
CHANGELOG.mdupdated ondevelopunder theUnreleasedsection. - When releasing, merge
developintomain, updatepyproject.tomlto the release version, and move theUnreleasedentries into a dated section like## [0.16.0] - 2024-12-19. - Tag the release commit on
main(for example,v0.16.0) and create the release from that tag.