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.
| Variable | What it does |
|---|---|
JWT_SECRET | Signs sign-in tokens. Changing it signs everyone out. |
ENCRYPTION_KEY | Encrypts 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
| Variable | Default | What it does |
|---|---|---|
JWT_EXPIRY_HOURS | 24 | How 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
| Variable | Default | What it does |
|---|---|---|
POSTGRES_PASSWORD | parcourse | Password for the bundled Postgres. |
DATABASE_URL | built from the above | Points at an external Postgres. postgresql:// and postgres:// both work. |
DB_SCHEMA | public | Which schema the tables live in. Created for you if it does not exist. |
Leave DATABASE_URL unset when using the bundled database. Setting it as well as POSTGRES_PASSWORD means the two can disagree.
Addresses
| Variable | Default | What it does |
|---|---|---|
BACKEND_PORT | 8000 | Port the API is served on. |
FRONTEND_PORT | 5173 | Port the app is served on. |
VITE_API_BASE_URL | http://localhost:8000 | The 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.
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.
| Variable | Default | What it does |
|---|---|---|
OIDC_ISSUER | empty | The provider's base address. Its discovery document supplies every endpoint. |
OIDC_CLIENT_ID | empty | The client your provider registered for Parcourse. |
OIDC_CLIENT_SECRET | empty | The secret that came with it. |
OIDC_REDIRECT_URL | empty | Where the provider sends the browser back. Must match what you registered, exactly. |
OIDC_NAME | SSO | Fills 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_URL | first CORS_ORIGINS entry | Where the browser lands once signed in. |
OIDC_AUTO_PROVISION | false | Whether an unknown address gets an account instead of being turned away. |
OIDC_ONLY | false | The provider is the only way in. No password form, and no password an admin can set. Needs OIDC_ISSUER. |
OIDC_SCOPES | openid email profile | What Parcourse asks your provider for. openid and email are both needed to match an account. |
Fetching Transcripts
| Variable | Default | What it does |
|---|---|---|
YTDLP_PROXY | empty | Sends only the YouTube fetches through a proxy. Leave empty at home. |
VPN_SERVICE_PROVIDER | OpenVPN provider name, for the bundled VPN sidecar. | |
OPENVPN_USER | The provider's OpenVPN username, not the website login. | |
OPENVPN_PASSWORD | The matching password. | |
FREE_ONLY | off | Set on when the VPN account is a free one. |
VPN_ROTATIONS | 2 | Reconnects to try when a fetch is refused. 0 disables rotation. |
VPN_CONTROL_URL | http://vpn:8000 | Where 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
| Variable | Default | What it does |
|---|---|---|
LOG_LEVEL | info | One 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.