An open-source event management, ticketing and membership platform for communities, clubs and independent venues.
This repository contains the backend API and business logic for Revel. The complete platform consists of:
Revel is an event management and ticketing platform designed with community at its heart. Initially created to serve the specific needs of queer, LGBTQ+, and sex-positive communities, it is built to be event-agnostic, scalable, and a powerful tool for any group that values privacy, control, and transparency. It works just as well for a gym, a yoga studio, a dance school or a choir: sell monthly or annual memberships, run members-only classes with a plain RSVP, sell a pass for a whole series, and put the membership card in your members' wallets.
Unlike monolithic, corporate platforms that treat events as transactions, Revel treats them as part of a larger community ecosystem.
π€ AI disclosure: Revel makes use of AI-assisted coding, but stays firmly away from vibe coding. Every line that lands in
mainis understood, reviewed, and defended by a human. If you contribute with AI, follow the workflow in AI_USAGE.md.
You can try out an early version of Revel yourself at https://demo.letsrevel.io
The api lives at https://demo-api.letsrevel.io/api/docs
NOTE: Emails are dry, and the data is reset every day at 00:00 CET.
More info on what's available here.
Revel is being built to address the shortcomings of existing event platforms, especially for communities that prioritize safety, autonomy, and trust.
Revel combines the ticketing power of platforms like Eventbrite with the community-building tools of Meetup, all under a privacy-minded, open-source framework.
Event detail page β cover art, live availability, and one-click ticketing
Event discovery with filters, tags & calendar view |
Ticket tiers β free, fixed, PWYC, at-the-door & offline |
Interactive seat selection with accessible-seat markers |
QR tickets with PDF download & Apple Wallet |
Questionnaire-based attendee screening |
Organizer review workflow with scoring & approval stats |
Potluck coordination with item claiming |
Revenue & VAT reporting, per event and org-wide |
Organization admin β events, tickets, members, questionnaires, venues, billing & more
Revel is built with a modern and robust backend, designed for performance and scalability.
We intentionally stay on Django 5.2 LTS rather than upgrading to Django 6.x. Our policy:
Get a local development environment running in minutes. You'll need make, Docker, Python 3.14+, and UV.
Clone the repository:
git clone https://github.com/letsrevel/revel-backend.git
cd revel-backend
Make sure you have the necessary geo data:
src/geo/data/src/geo/data/ (or for dev purposes just copy worldcities.mini.csv into worldcities.csv)Run the setup command: This command fully automates the setup process.
make setup
macOS note: if startup crashes with
Could not find the GDAL libraryor alibgobject-2.0dlopenerror (often after a macOS update), expose Homebrew's libs on dyld's default fallback path:ln -s "$(brew --prefix)/lib" ~/lib. See Troubleshooting.
You're ready!
http://localhost:8000http://localhost:8000/api/docsadmin@letsrevel.io / password).http://localhost:8025The project uses multiple Docker Compose files for different purposes:
| File | Purpose | Usage |
|---|---|---|
compose.yaml | Local development β PostgreSQL, Redis, ClamAV + Mailpit (email testing) | docker compose up -d |
docker-compose-ci.yml | CI β minimal services for tests (PostgreSQL, Redis, ClamAV, no Mailpit) | docker compose -f docker-compose-ci.yml up -d |
docker-compose-base.yml | Service definitions β every service the other files extend (core + observability stack); not run directly | β |
docker-compose-observability.yml | Standalone β core services + the full observability stack (Grafana, Prometheus, Loki, Tempo, β¦). Replaces compose.yaml; does not include Mailpit | docker compose -f docker-compose-observability.yml up -d |
The application itself (Django + Celery) runs on the host via make run β Docker only provides the backing services. For production (app, frontend, reverse proxy, TLS) use the infra repo.
For local development, simply run:
docker compose up -d
This starts PostgreSQL, Redis, ClamAV, and Mailpit. All emails sent by the application are captured by Mailpit and viewable at http://localhost:8025.
The entire stack β frontend, API, workers, database, and (optionally) the full observability suite β is self-hostable on a single box with Docker Compose. The infra repository ships the Compose files, a parameterized Caddyfile, and an interactive setup.sh wizard that writes your .env, picks the right Caddy config, fetches geo data, and brings the stack up. You don't need to clone the backend or frontend repos β the application images are pulled from the registry.
Two reference tiers let Revel scale down a long way:
The difference is mostly which Compose profiles you enable (COMPOSE_PROFILES) plus a few feature flags (FEATURE_MALWARE_SCAN, FEATURE_TELEGRAM, FEATURE_OBSERVABILITY, FEATURE_ORGANIZATION_CREATION). Clients read the active flags from GET /version, so gated features are hidden rather than 403'd. The published frontend image (ghcr.io/letsrevel/revel-frontend) is environment-agnostic β it reads its backend API URL from PUBLIC_API_URL at runtime, so one prebuilt image can target any backend (no rebuild required).
π Full guide: docs.letsrevel.io/self-hosting.
Revel includes a comprehensive observability stack built on the LGTM (Loki, Grafana, Tempo, Mimir) framework.
The observability stack lives in a separate Docker Compose file. After make setup, only the core services (PostgreSQL, Redis, ClamAV, Mailpit) are running. To enable full observability:
docker compose down # stop compose.yaml first
docker compose -f docker-compose-observability.yml up -d
!!! note
docker-compose-observability.yml is standalone: it bundles the core services and the observability stack, so it replaces compose.yaml (same container names β don't run both). Note it does not include Mailpit, so email testing is unavailable while it's running.
| Service | Purpose | URL | Credentials |
|---|---|---|---|
| Grafana | Unified dashboard for logs, traces, and metrics | http://localhost:3000 | admin / admin |
| Prometheus | Metrics collection and querying | http://localhost:9090 | - |
| Loki | Log aggregation | http://localhost:3100 | - |
| Tempo | Distributed tracing | http://localhost:3200 | - |
| Django Metrics | Application metrics endpoint | http://localhost:8000/metrics | - |
!!! warning "Pyroscope SDK Disabled"
The Pyroscope Python SDK (pyroscope-io) is currently disabled due to incompatibility with Grafana Pyroscope 1.6+. Profiling can be provided externally (e.g., via a Grafana Alloy eBPF agent at the infrastructure level). This may change when the SDK is updated.
View logs in Grafana: Go to http://localhost:3000 β Explore β Select "Loki" datasource
{service="revel"} | json | level="error"
View traces in Grafana: Explore β Select "Tempo" datasource β Search by service or endpoint
View metrics in Prometheus: Go to http://localhost:9090 β Graph
rate(django_http_requests_total[5m])
Set up alerts: Configure Grafana alert rules for production monitoring
Observability can be configured via environment variables in .env:
FEATURE_OBSERVABILITY=True # Enable/disable all observability features (legacy alias: ENABLE_OBSERVABILITY)
TRACING_SAMPLE_RATE=1.0 # 100% in dev (auto-switches to 0.1 in production)
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
!!! note
make setup runs with FEATURE_OBSERVABILITY=False to avoid connection errors to non-existent services. If you start the observability stack later, set FEATURE_OBSERVABILITY=True in your .env. The old ENABLE_OBSERVABILITY name is still honoured as a deprecated alias for one release.
After starting the observability stack and Django with make run, verify the setup:
Check startup logs: Look for initialization messages:
OpenTelemetry tracing initialized: service=revel, sample_rate=1.0, endpoint=http://localhost:4318
Check metrics endpoint: Visit http://localhost:8000/metrics - should show Prometheus metrics
Generate some traffic: Make API requests to create traces and logs
curl http://localhost:8000/api/docs
Check Grafana: Go to http://localhost:3000 β Explore β Select datasource
{service="revel"} | jsonrate(django_http_requests_total[5m])For detailed documentation, see:
The project uses a Makefile to streamline common development tasks.
| Command | Description |
|---|---|
make setup | Runs the complete one-time setup for the dev environment. |
make run | Starts the Django development server. |
make check | Runs all checks: formatting, linting, type checking, migration check, i18n check, and file length. |
make test | Runs the full pytest test suite and generates a coverage report. |
make run-celery | Starts the Celery worker for processing background tasks. |
make run-celery-beat | Starts the Celery beat scheduler for periodic tasks. |
make migrations | Creates new database migrations based on model changes. |
make migrate | Applies pending database migrations. |
make shell | Opens the Django shell. |
make restart | Destructive: Deletes all migrations, regenerates them, restarts Docker, and bootstraps. |
make nuke-db | Destructive: Resets database and regenerates migrations (preserves special data migrations). |
Revel implements HMAC-signed URLs for protected file access, allowing certain media files to require authorization while being served efficiently by Caddy.
Client β Caddy β forward_auth β Django /api/media/validate/*
β
Validates HMAC signature + expiry
β
Returns 200 (serve file) or 401
We evaluated MinIO but chose HMAC signing for these reasons:
protected/ requires signed URL access/media/protected/* through forward_authUse ProtectedFileField or ProtectedImageField for files requiring signed access:
from common.fields import ProtectedFileField, ProtectedImageField
class MyModel(models.Model):
# Stored in protected/attachments/ - requires signed URL
attachment = ProtectedFileField(upload_to="attachments")
# Stored in protected/profile-pics/ - requires signed URL
profile_pic = ProtectedImageField(upload_to="profile-pics")
Use get_file_url() with a static resolver to generate signed URLs in your schemas:
from ninja import ModelSchema
from common.signing import get_file_url
class MyResourceSchema(ModelSchema):
file_url: str | None = None
@staticmethod
def resolve_file_url(obj: MyModel) -> str | None:
"""Return signed URL for protected files, direct URL for public files."""
return get_file_url(obj.file)
class Meta:
model = MyModel
fields = ["id", "name"]
The get_file_url() function automatically:
exp and sig params) for protected pathsNone if the file field is emptySECRET_KEY with domain separationSee the Protected Files architecture docs for details. The Caddy configuration lives in the infra repository.
The codebase is organized into a src directory with a clear separation of concerns, following modern Django best practices.
src/revel/: The core Django project settings.src/accounts/: User authentication, registration, and profile management.src/events/: The core logic for organizations, events, tickets, and memberships.src/questionnaires/: The questionnaire building, submission, and evaluation system. π Read moresrc/notifications/: Multi-channel notification system (in-app, email, Telegram) with user preferences, digest support, and event-driven delivery.src/wallet/: Apple Wallet pass generation for event tickets (.pkpass files).src/geo/: Geolocation features (cities, IP lookups).src/telegram/: Telegram Bot integration with FSM-based conversation flows, inline keyboards, and organizer notifications.src/api/: Main API configuration, exception handlers, and global endpoints.src/common/: Shared utilities, authentication backends, base models, and admin customizations.Most apps contain controllers and service modules for API endpoints and business logic respectively, either as directories or single files depending on complexity.
We welcome contributions! Please read our CONTRIBUTING.md to learn how you can get involved, from reporting bugs to submitting code. If you contribute with AI assistance, also read AI_USAGE.md β it is not optional.
Revel aims to support multiple languages (currently English, German, Italian, and French). See i18n.md for details on how the translation system works and how to add new languages.
This is currently heavily WIP.
We run layered, mostly-automated security controls β SAST (bandit), dependency
CVE scanning (pip-audit) and license checks, strict typing, a 90%
branch-coverage gate, a nightly dependency audit, and periodic OWASP ZAP scans.
See SECURITY.md for the full posture.
Found a vulnerability? Please report it privately via Report a vulnerability β do not open a public issue.
This project is licensed under the MIT license. See LICENSE.
581 commits
6 commits
3 commits
1 commits
Python
98.5%
HTML
1.3%
An open-source event management, ticketing and membership platform for communities, clubs and independent venues.
This repository contains the backend API and business logic for Revel. The complete platform consists of:
Revel is an event management and ticketing platform designed with community at its heart. Initially created to serve the specific needs of queer, LGBTQ+, and sex-positive communities, it is built to be event-agnostic, scalable, and a powerful tool for any group that values privacy, control, and transparency. It works just as well for a gym, a yoga studio, a dance school or a choir: sell monthly or annual memberships, run members-only classes with a plain RSVP, sell a pass for a whole series, and put the membership card in your members' wallets.
Unlike monolithic, corporate platforms that treat events as transactions, Revel treats them as part of a larger community ecosystem.
π€ AI disclosure: Revel makes use of AI-assisted coding, but stays firmly away from vibe coding. Every line that lands in
mainis understood, reviewed, and defended by a human. If you contribute with AI, follow the workflow in AI_USAGE.md.
You can try out an early version of Revel yourself at https://demo.letsrevel.io
The api lives at https://demo-api.letsrevel.io/api/docs
NOTE: Emails are dry, and the data is reset every day at 00:00 CET.
More info on what's available here.
Revel is being built to address the shortcomings of existing event platforms, especially for communities that prioritize safety, autonomy, and trust.
Revel combines the ticketing power of platforms like Eventbrite with the community-building tools of Meetup, all under a privacy-minded, open-source framework.
Event detail page β cover art, live availability, and one-click ticketing
Event discovery with filters, tags & calendar view |
Ticket tiers β free, fixed, PWYC, at-the-door & offline |
Interactive seat selection with accessible-seat markers |
QR tickets with PDF download & Apple Wallet |
Questionnaire-based attendee screening |
Organizer review workflow with scoring & approval stats |
Potluck coordination with item claiming |
Revenue & VAT reporting, per event and org-wide |
Organization admin β events, tickets, members, questionnaires, venues, billing & more
Revel is built with a modern and robust backend, designed for performance and scalability.
We intentionally stay on Django 5.2 LTS rather than upgrading to Django 6.x. Our policy:
Get a local development environment running in minutes. You'll need make, Docker, Python 3.14+, and UV.
Clone the repository:
git clone https://github.com/letsrevel/revel-backend.git
cd revel-backend
Make sure you have the necessary geo data:
src/geo/data/src/geo/data/ (or for dev purposes just copy worldcities.mini.csv into worldcities.csv)Run the setup command: This command fully automates the setup process.
make setup
macOS note: if startup crashes with
Could not find the GDAL libraryor alibgobject-2.0dlopenerror (often after a macOS update), expose Homebrew's libs on dyld's default fallback path:ln -s "$(brew --prefix)/lib" ~/lib. See Troubleshooting.
You're ready!
http://localhost:8000http://localhost:8000/api/docsadmin@letsrevel.io / password).http://localhost:8025The project uses multiple Docker Compose files for different purposes:
| File | Purpose | Usage |
|---|---|---|
compose.yaml | Local development β PostgreSQL, Redis, ClamAV + Mailpit (email testing) | docker compose up -d |
docker-compose-ci.yml | CI β minimal services for tests (PostgreSQL, Redis, ClamAV, no Mailpit) | docker compose -f docker-compose-ci.yml up -d |
docker-compose-base.yml | Service definitions β every service the other files extend (core + observability stack); not run directly | β |
docker-compose-observability.yml | Standalone β core services + the full observability stack (Grafana, Prometheus, Loki, Tempo, β¦). Replaces compose.yaml; does not include Mailpit | docker compose -f docker-compose-observability.yml up -d |
The application itself (Django + Celery) runs on the host via make run β Docker only provides the backing services. For production (app, frontend, reverse proxy, TLS) use the infra repo.
For local development, simply run:
docker compose up -d
This starts PostgreSQL, Redis, ClamAV, and Mailpit. All emails sent by the application are captured by Mailpit and viewable at http://localhost:8025.
The entire stack β frontend, API, workers, database, and (optionally) the full observability suite β is self-hostable on a single box with Docker Compose. The infra repository ships the Compose files, a parameterized Caddyfile, and an interactive setup.sh wizard that writes your .env, picks the right Caddy config, fetches geo data, and brings the stack up. You don't need to clone the backend or frontend repos β the application images are pulled from the registry.
Two reference tiers let Revel scale down a long way:
The difference is mostly which Compose profiles you enable (COMPOSE_PROFILES) plus a few feature flags (FEATURE_MALWARE_SCAN, FEATURE_TELEGRAM, FEATURE_OBSERVABILITY, FEATURE_ORGANIZATION_CREATION). Clients read the active flags from GET /version, so gated features are hidden rather than 403'd. The published frontend image (ghcr.io/letsrevel/revel-frontend) is environment-agnostic β it reads its backend API URL from PUBLIC_API_URL at runtime, so one prebuilt image can target any backend (no rebuild required).
π Full guide: docs.letsrevel.io/self-hosting.
Revel includes a comprehensive observability stack built on the LGTM (Loki, Grafana, Tempo, Mimir) framework.
The observability stack lives in a separate Docker Compose file. After make setup, only the core services (PostgreSQL, Redis, ClamAV, Mailpit) are running. To enable full observability:
docker compose down # stop compose.yaml first
docker compose -f docker-compose-observability.yml up -d
!!! note
docker-compose-observability.yml is standalone: it bundles the core services and the observability stack, so it replaces compose.yaml (same container names β don't run both). Note it does not include Mailpit, so email testing is unavailable while it's running.
| Service | Purpose | URL | Credentials |
|---|---|---|---|
| Grafana | Unified dashboard for logs, traces, and metrics | http://localhost:3000 | admin / admin |
| Prometheus | Metrics collection and querying | http://localhost:9090 | - |
| Loki | Log aggregation | http://localhost:3100 | - |
| Tempo | Distributed tracing | http://localhost:3200 | - |
| Django Metrics | Application metrics endpoint | http://localhost:8000/metrics | - |
!!! warning "Pyroscope SDK Disabled"
The Pyroscope Python SDK (pyroscope-io) is currently disabled due to incompatibility with Grafana Pyroscope 1.6+. Profiling can be provided externally (e.g., via a Grafana Alloy eBPF agent at the infrastructure level). This may change when the SDK is updated.
View logs in Grafana: Go to http://localhost:3000 β Explore β Select "Loki" datasource
{service="revel"} | json | level="error"
View traces in Grafana: Explore β Select "Tempo" datasource β Search by service or endpoint
View metrics in Prometheus: Go to http://localhost:9090 β Graph
rate(django_http_requests_total[5m])
Set up alerts: Configure Grafana alert rules for production monitoring
Observability can be configured via environment variables in .env:
FEATURE_OBSERVABILITY=True # Enable/disable all observability features (legacy alias: ENABLE_OBSERVABILITY)
TRACING_SAMPLE_RATE=1.0 # 100% in dev (auto-switches to 0.1 in production)
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
!!! note
make setup runs with FEATURE_OBSERVABILITY=False to avoid connection errors to non-existent services. If you start the observability stack later, set FEATURE_OBSERVABILITY=True in your .env. The old ENABLE_OBSERVABILITY name is still honoured as a deprecated alias for one release.
After starting the observability stack and Django with make run, verify the setup:
Check startup logs: Look for initialization messages:
OpenTelemetry tracing initialized: service=revel, sample_rate=1.0, endpoint=http://localhost:4318
Check metrics endpoint: Visit http://localhost:8000/metrics - should show Prometheus metrics
Generate some traffic: Make API requests to create traces and logs
curl http://localhost:8000/api/docs
Check Grafana: Go to http://localhost:3000 β Explore β Select datasource
{service="revel"} | jsonrate(django_http_requests_total[5m])For detailed documentation, see:
The project uses a Makefile to streamline common development tasks.
| Command | Description |
|---|---|
make setup | Runs the complete one-time setup for the dev environment. |
make run | Starts the Django development server. |
make check | Runs all checks: formatting, linting, type checking, migration check, i18n check, and file length. |
make test | Runs the full pytest test suite and generates a coverage report. |
make run-celery | Starts the Celery worker for processing background tasks. |
make run-celery-beat | Starts the Celery beat scheduler for periodic tasks. |
make migrations | Creates new database migrations based on model changes. |
make migrate | Applies pending database migrations. |
make shell | Opens the Django shell. |
make restart | Destructive: Deletes all migrations, regenerates them, restarts Docker, and bootstraps. |
make nuke-db | Destructive: Resets database and regenerates migrations (preserves special data migrations). |
Revel implements HMAC-signed URLs for protected file access, allowing certain media files to require authorization while being served efficiently by Caddy.
Client β Caddy β forward_auth β Django /api/media/validate/*
β
Validates HMAC signature + expiry
β
Returns 200 (serve file) or 401
We evaluated MinIO but chose HMAC signing for these reasons:
protected/ requires signed URL access/media/protected/* through forward_authUse ProtectedFileField or ProtectedImageField for files requiring signed access:
from common.fields import ProtectedFileField, ProtectedImageField
class MyModel(models.Model):
# Stored in protected/attachments/ - requires signed URL
attachment = ProtectedFileField(upload_to="attachments")
# Stored in protected/profile-pics/ - requires signed URL
profile_pic = ProtectedImageField(upload_to="profile-pics")
Use get_file_url() with a static resolver to generate signed URLs in your schemas:
from ninja import ModelSchema
from common.signing import get_file_url
class MyResourceSchema(ModelSchema):
file_url: str | None = None
@staticmethod
def resolve_file_url(obj: MyModel) -> str | None:
"""Return signed URL for protected files, direct URL for public files."""
return get_file_url(obj.file)
class Meta:
model = MyModel
fields = ["id", "name"]
The get_file_url() function automatically:
exp and sig params) for protected pathsNone if the file field is emptySECRET_KEY with domain separationSee the Protected Files architecture docs for details. The Caddy configuration lives in the infra repository.
The codebase is organized into a src directory with a clear separation of concerns, following modern Django best practices.
src/revel/: The core Django project settings.src/accounts/: User authentication, registration, and profile management.src/events/: The core logic for organizations, events, tickets, and memberships.src/questionnaires/: The questionnaire building, submission, and evaluation system. π Read moresrc/notifications/: Multi-channel notification system (in-app, email, Telegram) with user preferences, digest support, and event-driven delivery.src/wallet/: Apple Wallet pass generation for event tickets (.pkpass files).src/geo/: Geolocation features (cities, IP lookups).src/telegram/: Telegram Bot integration with FSM-based conversation flows, inline keyboards, and organizer notifications.src/api/: Main API configuration, exception handlers, and global endpoints.src/common/: Shared utilities, authentication backends, base models, and admin customizations.Most apps contain controllers and service modules for API endpoints and business logic respectively, either as directories or single files depending on complexity.
We welcome contributions! Please read our CONTRIBUTING.md to learn how you can get involved, from reporting bugs to submitting code. If you contribute with AI assistance, also read AI_USAGE.md β it is not optional.
Revel aims to support multiple languages (currently English, German, Italian, and French). See i18n.md for details on how the translation system works and how to add new languages.
This is currently heavily WIP.
We run layered, mostly-automated security controls β SAST (bandit), dependency
CVE scanning (pip-audit) and license checks, strict typing, a 90%
branch-coverage gate, a nightly dependency audit, and periodic OWASP ZAP scans.
See SECURITY.md for the full posture.
Found a vulnerability? Please report it privately via Report a vulnerability β do not open a public issue.
This project is licensed under the MIT license. See LICENSE.
581 commits
6 commits
3 commits
1 commits
Python
98.5%
HTML
1.3%