Skip to main content

Configuration

Everything is set in .env, copied from .env.example at the root of the repository. Nothing here is read from anywhere else.

Required​

Neither has a default, and both belong in .env before the first run.

VariableWhat it does
JWT_SECRETSigns sign-in tokens. Changing it signs everyone out.
ENCRYPTION_KEYEncrypts stored provider keys. Changing it makes every stored key unreadable.

Generate the encryption key with:

python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

Sessions​

VariableDefaultWhat it does
JWT_EXPIRY_HOURS24How long a sign-in lasts before the person signs in again.

A session already handed out keeps its original lifetime, so shortening this only applies from the next sign-in onwards.

Database​

VariableDefaultWhat it does
POSTGRES_PASSWORDparcoursePassword for the bundled Postgres.
DATABASE_URLbuilt from the abovePoints at an external Postgres. postgresql:// and postgres:// both work.
DB_SCHEMApublicWhich schema the tables live in. Created for you if it does not exist.
note

Leave DATABASE_URL unset when using the bundled database. Setting it as well as POSTGRES_PASSWORD means the two can disagree.

Addresses​

VariableDefaultWhat it does
BACKEND_PORT8000Port the API is served on.
FRONTEND_PORT5173Port the app is served on.
VITE_API_BASE_URLhttp://localhost:8000The API address the browser calls. Behind a proxy this must be the public one.
CORS_ORIGINS["http://localhost:5173"]JSON array of origins allowed to call the API.

VITE_API_BASE_URL and CORS_ORIGINS have to agree with wherever the app is actually reached from. They are the two that catch people out behind a reverse proxy.

VITE_API_BASE_URL is read when the frontend container starts, so changing it takes a docker compose up -d and nothing more.

note

Till v1.6.0, VITE_API_BASE_URL used to be compiled into the app. This was changed to avoid rebuilding the app again and again.

Single Sign-On​

Leave OIDC_ISSUER empty and none of this applies. Set it and the next three are required; the backend refuses to start otherwise. Single Sign-On covers the setup.

VariableDefaultWhat it does
OIDC_ISSUERemptyThe provider's base address. Its discovery document supplies every endpoint.
OIDC_CLIENT_IDemptyThe client your provider registered for Parcourse.
OIDC_CLIENT_SECRETemptyThe secret that came with it.
OIDC_REDIRECT_URLemptyWhere the provider sends the browser back. Must match what you registered, exactly.
OIDC_NAMESSOFills the blank in "Continue with ___" on the login screen.
OIDC_LABEL(empty)The whole login button, when "Continue with ..." is not the wording you want. Wins over OIDC_NAME.
OIDC_POST_LOGIN_URLfirst CORS_ORIGINS entryWhere the browser lands once signed in.
OIDC_AUTO_PROVISIONfalseWhether an unknown address gets an account instead of being turned away.
OIDC_ONLYfalseThe provider is the only way in. No password form, and no password an admin can set. Needs OIDC_ISSUER.
OIDC_SCOPESopenid email profileWhat Parcourse asks your provider for. openid and email are both needed to match an account.

Fetching Transcripts​

VariableDefaultWhat it does
YTDLP_PROXYemptySends only the YouTube fetches through a proxy. Leave empty at home.
VPN_SERVICE_PROVIDEROpenVPN provider name, for the bundled VPN sidecar.
OPENVPN_USERThe provider's OpenVPN username, not the website login.
OPENVPN_PASSWORDThe matching password.
FREE_ONLYoffSet on when the VPN account is a free one.
VPN_ROTATIONS2Reconnects to try when a fetch is refused. 0 disables rotation.
VPN_CONTROL_URLhttp://vpn:8000Where a reconnect is requested.

The VPN variables are only read by docker-compose.ghcr-vpn.yml. See Troubleshooting for when any of this is needed.

Logging​

VariableDefaultWhat it does
LOG_LEVELinfoOne of debug, info, warning, error, critical.

The API's Own Reference​

The backend is FastAPI, so every instance serves an interactive schema of its own routes at /docs on the API port. That is the reference, and it is always correct for the version you are running.