AccessFlow

AccessFlow

Docker app from Pantanet96's Repository

Overview

Self-hosted portal to manage users, subscriptions and invites for a personal Plex server: roles, plans, renewals, payment reports, email/Telegram reminders. Requires APP_SECRET_KEY (>=32 random chars) and, to invite/manage Plex users, PLEX_TOKEN + PLEX_SERVER_NAME. Put the app behind a reverse proxy (NPM/SWAG/Traefik) and set PUBLIC_BASE_URL to your https:// URL.
AccessFlow logo

AccessFlow

Self-hosted portal to manage users, subscriptions, and invites for a personal Plex server.

FastAPI + Jinja2/HTMX + SQLite, shipped as a single Docker container that sits behind your reverse proxy.

Docker Image Python FastAPI License: MIT

Star history

Screenshots

Dashboard Users
Dashboard Users
Collect Reports
Collect Reports

Rame (copper) theme by default; Inchiostro and Muschio variants also available from Settings:

Dashboard — Inchiostro theme

Contents

Features

  • Role-based access — SuperAdmin, Admin, Moderator, User, each scoped to only what they need.
  • Multi-manager support — every user has a manager who collects their payments; multiple people can run their own client base under the same server.
  • Flexible plans — free/trial plans out of the box, custom paid plans created on demand.
  • Two-step renewals — a renewal stays pending until the payment is actually collected, then extends the expiration.
  • Automatic reminders — expiration notices via email and Telegram, deduplicated, configurable schedule.
  • Financial reports — revenue collected, pending, and projected, straight from recorded payments.
  • Telegram bot — users link their account for reminders; admins can broadcast.
  • 3 color themes — Rame, Inchiostro, Muschio — switchable per-user from Settings.
  • Audit log, soft-delete, nightly backups — out of the box.
  • i18n — English source strings, translatable via .po catalogs.

How it works

Manages the lifecycle of a Plex server's users: who has access, what plan they're on, when it expires, who collects the payment. The idea is to stop having to track "who owes me and when" by hand.

Roles

  • SuperAdmin — full access, local login (username/password). Configures the system.
  • Admin — manages users, plans, reports, settings.
  • Moderator — manages only the users assigned to them (their "clients") and collects their payments. Cannot touch free plans or global settings.
  • User — sees only their own subscription.

Every regular user has a manager (an Admin or Moderator): the person who brought them in and collects their payments. This way multiple people can manage their own users under the same server, each one seeing only their own.

Plans

By default only two special plans exist:

  • Family & Friends — free, never expires.
  • Trial — timed trial period (max 30 days), not renewable.

Paid plans (with custom price and duration) are created by the SuperAdmin as needed from the Plans page — there are no predefined ones.

Typical workflows

1. Adding a new paying user

  1. The admin/moderator invites the person on Plex from the portal (sends the Plex invite).
  2. The person accepts and logs in with their own Plex account (PIN flow, no password to manage).
  3. They're assigned a paid plan → the subscription starts and the initial payment is recorded right away (it goes into the reports as revenue).
  4. It's also possible to pay several months in advance: you set the number of periods and the expiration is calculated accordingly.

2. Renewal (two-step)

  1. At expiration the manager creates a renewal → it stays pending until collected.
  2. When the client pays, the manager marks it as paid, indicating the payment method (e.g. "PayPal", "cash"). Only then is the expiration extended and the revenue counted.
  • Multiple periods can be renewed at once.

3. Expiration reminders (automatic)

  • A daily job checks who's about to expire and sends reminders via email and Telegram (default: 7/3/1 days before, and 0/3 days after for overdue follow-ups).
  • Reminders are deduplicated (they don't repeat on the same day).
  • The manager receives a "to be collected" notice.

4. Reports

  • User count per plan + revenue summary: previous month / collected this month / to be collected this month / next month's projection.
  • Every euro in the reports comes from a recorded payment (initial setup or a paid renewal).

5. Telegram

  • Users link their own Telegram account to the portal to receive reminders.
  • Admin can send manual broadcasts.

Other automations: audit log of every action, soft-delete with orphan protection, nightly backup of the SQLite database.


Deploy

The image is published on Docker Hub (public repo): pantanet96/accessflow.

cp .env.example .env   # fill in the secrets, never commit the real .env
docker compose up -d

App at http://localhost:8000, behind your reverse proxy (NPM / Traefik / Caddy). Health check: GET /healthz.

docker-compose.yml:

services:
  app:
    image: pantanet96/accessflow:latest
    env_file: .env
    ports:
      - "8000:8000"
    volumes:
      - appdata:/data
    restart: unless-stopped
volumes:
  appdata:

Upgrading: docker compose pull && docker compose up -d.

The image honors X-Forwarded-Proto (--proxy-headers), so behind an HTTPS proxy URLs come out as https.

Unraid

Ships a Community Applications template → docs/UNRAID.md.

Security

  • APP_SECRET_KEY is mandatory (random, ≥32 chars) — the app won't start without one.
  • SuperAdmin password auto-generates on first boot if left blank; change it from /profile.
  • Set FORWARDED_ALLOW_IPS to your reverse proxy's subnet — never * on a directly exposed app.
  • Runs as an unprivileged container user; secrets are encrypted at rest.

Full details → docs/SECURITY.md.


Local development

python -m venv .venv
. .venv/Scripts/activate          # Windows: .venv\Scripts\activate
pip install -r requirements-dev.txt   # prod deps + pytest (prod uses requirements.txt)
export DATABASE_PATH=./data/app.db   # avoids the container's /data path
uvicorn app.main:app --reload
pytest

Configuration

Everything via environment / .env — see .env.example.

Identity shown to Plex

Every request the app makes to Plex carries AccessFlow as product, device and device name, so plex.tv → Settings → Authorized Devices and Tautulli's logs name the app instead of Linux + the container hostname. The platform version is left alone and reports the host kernel release (a container shares the host kernel), so you end up with e.g. AccessFlow / 6.6.78-Unraid.

Running more than one instance against the same Plex account? Give each a distinct device name so you can tell their tokens apart when revoking one:

services:
  app:
    environment:
      PLEXAPI_HEADER_DEVICE_NAME: "AccessFlow-staging"

Leave PLEXAPI_HEADER_PRODUCT as is — Plex and Tautulli group activity by product, and changing it splits your own history in their dashboards.

i18n

Source strings are in English. Translations live in app/translations/<locale>/LC_MESSAGES/messages.po. After changing templates/strings:

pybabel extract -F babel.cfg -o messages.pot .
pybabel update -i messages.pot -d app/translations
# edit the .po files, then:
pybabel compile -d app/translations

The Docker build compiles the catalogs automatically.

Background workers

The web container also runs, in-process: a daily APScheduler job (expiration scan at the NOTIFY_HOUR hour, nightly DB backup) and the Telegram bot in polling mode. They're toggled with ENABLE_SCHEDULER / ENABLE_BOT.

Install AccessFlow on Unraid in a few clicks.

Find AccessFlow in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for AccessFlow Review the template variables and paths Click Install

Download Statistics

181
Total Downloads

Related apps

Details

Repository
pantanet96/accessflow
Last Updated2026-08-06
First Seen2026-08-07

Runtime arguments

Web UI
http://[IP]:[PORT:8000]/
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI PortPorttcp

Container Port: 8000

Target
8000
Default
8000
Value
8000
AppdataPathrw

SQLite DB + backups + secrets. Container Path: /data

Target
/data
Default
/mnt/user/appdata/accessflow
Value
/mnt/user/appdata/accessflow
App Secret KeyVariable

REQUIRED. Random string >=32 chars, the app refuses to start without it. Generate: python -c &quot;import secrets;print(secrets.token_urlsafe(48))&quot;

Target
APP_SECRET_KEY
Public Base URLVariable

Your public https:// URL (through the reverse proxy). Needed for Secure session cookies.

Target
PUBLIC_BASE_URL
Default
http://localhost:8000
Value
http://localhost:8000
Forwarded Allow IPsVariable

Reverse proxy source IP/subnet uvicorn trusts for X-Forwarded-*. Never &quot;*&quot; if exposed directly. Unraid docker bridge is typically 172.17.0.0/16.

Target
FORWARDED_ALLOW_IPS
Default
127.0.0.1
Value
127.0.0.1
TimezoneVariable

Timezone for schedules/reminders/backups.

Target
TZ
Default
Europe/Rome
Value
Europe/Rome
Default LocaleVariable

UI language (it/en).

Target
DEFAULT_LOCALE
Default
it
Value
it
SuperAdmin UsernameVariable

Local login username, seeded on first boot.

Target
SUPERADMIN_USERNAME
Default
admin
Value
admin
SuperAdmin PasswordVariable

Leave blank: a random password is generated and logged once at first boot (change it later from /profile).

Target
SUPERADMIN_PASSWORD
Plex TokenVariable

Admin Plex token, used for invites + OAuth. https://support.plex.tv/articles/204059436

Target
PLEX_TOKEN
Plex Server NameVariable

Name of the Plex server to manage.

Target
PLEX_SERVER_NAME
Plex Direct URLVariable

Optional: connect straight to this Plex baseurl (plex.direct dashed-host form), skipping plex.tv discovery. Leave blank for normal discovery.

Target
PLEX_DIRECT_URL
Plex Client IDVariable

Optional: stable Plex client identifier. Derived from the secret key if left blank.

Target
PLEX_CLIENT_ID
Plex Revoke On DeleteVariable

Revoke the user's Plex server access when their AccessFlow account is deleted.

Target
PLEX_REVOKE_ON_DELETE
Default
false
Value
false
Telegram Bot TokenVariable

Optional: enables the Telegram reminder bot. From @BotFather.

Target
TELEGRAM_BOT_TOKEN
Telegram Bot UsernameVariable

Bot username (without @), used for deep links.

Target
TELEGRAM_BOT_USERNAME
SMTP HostVariable

Optional: enables email reminders.

Target
SMTP_HOST
SMTP PortVariable
Target
SMTP_PORT
Default
587
Value
587
SMTP UserVariable
Target
SMTP_USER
SMTP PasswordVariable
Target
SMTP_PASS
SMTP FromVariable

From address for reminder emails.

Target
SMTP_FROM
SMTP From NameVariable

Display name in the From header.

Target
SMTP_FROM_NAME
SMTP TLSVariable
Target
SMTP_TLS
Default
true
Value
true
Notify HourVariable

Hour (0-23, server TZ) the daily expiration scan + reminders run.

Target
NOTIFY_HOUR
Default
9
Value
9
Reminder Days BeforeVariable

Comma-separated days-before-expiry to send reminders. Admin-editable in Settings too.

Target
REMINDER_DAYS_BEFORE
Default
7,3,1
Value
7,3,1
Reminder Days AfterVariable

Comma-separated overdue days for dunning follow-ups. Admin-editable in Settings too.

Target
REMINDER_DAYS_AFTER
Default
0,3
Value
0,3
Digest Lookahead DaysVariable

Manager weekly-digest lookahead window in days. Admin-editable in Settings too.

Target
DIGEST_LOOKAHEAD_DAYS
Default
14
Value
14
Backup KeepVariable

How many nightly SQLite backups to keep.

Target
BACKUP_KEEP
Default
14
Value
14
Enable SchedulerVariable

Run the daily reminder/backup scheduler in-process.

Target
ENABLE_SCHEDULER
Default
true
Value
true
Enable Telegram BotVariable

Run the Telegram bot (polling) in-process.

Target
ENABLE_BOT
Default
true
Value
true