docs: Anhaenge, Hintergrund-Jobs und neue Variablen im README

Dokumentiert auch den ehrlichen Haken der ATTACH-Links: sie muessen ohne
Login funktionieren, weil externe Kalender-Clients keinen mitschicken
koennen, und sind damit unbefristet gueltige Capability-URLs. Mit dem
Abschalter, falls das fuer eine Installation nicht tragbar ist.

Nebenbei die seit dem Monorepo-Merge falsche Aussage korrigiert, die
mobilen Apps laegen in eigenen Repos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-14 10:28:08 +02:00
co-authored by Claude Opus 5
parent e2a7edf421
commit cd8aba74b6
+62 -4
View File
@@ -24,6 +24,7 @@ A clean, self-hosted calendar application with support for CalDAV, Google Calend
- Create, edit, and delete events with title, description, location, and time - Create, edit, and delete events with title, description, location, and time
- All-day events - All-day events
- Per-event color overrides - Per-event color overrides
- **Attachments** — PDFs, images and text files on local events (10 MB each, 10 per event)
- iCal event overrides (hide or edit individual events from subscriptions) - iCal event overrides (hide or edit individual events from subscriptions)
- Dim past events option - Dim past events option
@@ -102,11 +103,15 @@ Configuration is done via environment variables. The `install.sh` script generat
| `SECRET_KEY` | *(auto-generated)* | JWT signing key — use a strong random value in production | | `SECRET_KEY` | *(auto-generated)* | JWT signing key — use a strong random value in production |
| `PORT` | `8080` | Server port | | `PORT` | `8080` | Server port |
| `HOST` | `0.0.0.0` | Bind address | | `HOST` | `0.0.0.0` | Bind address |
| `DATA_DIR` | `./data` | Directory for SQLite database and avatars | | `DATA_DIR` | `./data` | Directory for the SQLite database, avatars, branding and attachments |
| `GOOGLE_CLIENT_ID` | — | Google OAuth2 Client ID *(optional)* | | `GOOGLE_CLIENT_ID` | — | Google OAuth2 Client ID *(optional)* |
| `GOOGLE_CLIENT_SECRET` | — | Google OAuth2 Client Secret *(optional)* | | `GOOGLE_CLIENT_SECRET` | — | Google OAuth2 Client Secret *(optional)* |
| `GOOGLE_REDIRECT_URI` | — | e.g. `https://yourdomain.com/api/google/callback` *(optional)* | | `GOOGLE_REDIRECT_URI` | — | e.g. `https://yourdomain.com/api/google/callback` *(optional)* |
| `PUBLIC_BASE_URL` | *(derived)* | Public origin behind a reverse proxy — **required for SSO** | | `PUBLIC_BASE_URL` | *(derived)* | Public origin behind a reverse proxy — **required for SSO** and for attachment links in CalDAV |
| `ATTACHMENT_PUBLIC_LINKS` | `1` | `0` disables the unauthenticated attachment URLs (see below) |
| `SCHEDULER_ENABLED` | `1` | `0` disables all background jobs |
| `ATTACHMENT_SWEEP_HOURS` | `24` | How often orphaned attachments are cleaned up |
| `ICAL_REFRESH_MINUTES` | `15` | How often iCal subscriptions are checked in the background |
| `OIDC_PROVIDERS` | — | Comma-separated provider keys, e.g. `authentik` *(optional)* | | `OIDC_PROVIDERS` | — | Comma-separated provider keys, e.g. `authentik` *(optional)* |
Per provider `<KEY>` listed in `OIDC_PROVIDERS` (variable names are uppercased): Per provider `<KEY>` listed in `OIDC_PROVIDERS` (variable names are uppercased):
@@ -138,8 +143,8 @@ Adds an SSO button to the login screen **alongside** password login. With
- iOS: `com.scarriffleservices.calendarr.ios:/oauth2redirect` - iOS: `com.scarriffleservices.calendarr.ios:/oauth2redirect`
- Android: `com.scarriffle.calendarr:/oauth2redirect` - Android: `com.scarriffle.calendarr:/oauth2redirect`
The mobile apps are separate repositories (`Calendarr-IOS`, `Calendarr-Android`) The mobile apps live in this repository under `ios/` and `android/`, and
and authenticate as **public** clients via AppAuth — PKCE, no client secret. authenticate as **public** clients via AppAuth — PKCE, no client secret.
Set `OIDC_<KEY>_MOBILE_CLIENT_ID` to the client id they use; without it the apps Set `OIDC_<KEY>_MOBILE_CLIENT_ID` to the client id they use; without it the apps
show no SSO button. show no SSO button.
@@ -194,6 +199,59 @@ same as 2FA users.
**Rollback:** remove `OIDC_PROVIDERS` and restart. Accounts created via SSO **Rollback:** remove `OIDC_PROVIDERS` and restart. Accounts created via SSO
remain and must be removed (or given a password) by an admin. remain and must be removed (or given a password) by an admin.
### Attachments
Events in **local** calendars can carry files: PDFs, images (PNG/JPEG/WebP/GIF)
and text files (TXT/Markdown/CSV), up to **10 MB per file and 10 per event**.
Events from CalDAV, Google, Home Assistant or iCal subscriptions cannot — they
are fetched live from their own server and have no record here to attach to.
Files are stored under `DATA_DIR/attachments`; **back that up together with the
database**, or a restored database will point at files that no longer exist.
Who may attach or remove a file is the same rule as who may edit the event:
the calendar's owner, group members, and anyone with a read-write share. A
read-only share can view and download. Private events of other users expose
neither their attachments nor their number.
**Attachments in external calendar clients.** The generated ICS carries an
`ATTACH` line per file, so Apple Calendar, Thunderbird and DAVx5 show the
attachment and can download it. Those clients cannot send a login, so the URL
has to work without one: each attachment gets its own unguessable 256-bit
token, and anyone who has that URL can fetch the file.
Weigh that honestly. The link only ever reaches clients that were already
allowed to read the calendar, and it dies when the attachment is deleted — but
it is long-lived, it ends up in plain text in the client's local calendar
store, and it would appear in a reverse-proxy access log. If that is not
acceptable for your deployment, set `ATTACHMENT_PUBLIC_LINKS=0`: no `ATTACH`
line is written and the public URL stops answering. Attachments then work in
the web UI and the mobile apps only.
`PUBLIC_BASE_URL` must be set for the links to be correct behind a reverse
proxy; without a usable base URL the `ATTACH` line is omitted entirely.
### Background jobs
The server runs two periodic jobs in-process:
| Job | Interval | What it does |
|---|---|---|
| `attachments.sweep` | 24 h | Removes attachment records whose event is gone, and files with no record |
| `ical.refresh` | 15 min | Re-fetches iCal subscriptions that are due |
Attachments are normally cleaned up the moment an event, calendar or user is
deleted; the sweep is the safety net for what gets past that — an external
CalDAV client deleting an event, or a request that died mid-upload. It leaves
files younger than an hour alone, so it cannot race an upload in progress.
The iCal job only drives the same refresh the app already did on demand, so
subscription intervals are unchanged. What is new is that subscriptions stay
current while nobody has the app open.
Both jobs assume a single server process, which is how `main.py` starts
uvicorn. Running multiple workers would run every job once per worker.
### Google Calendar Setup ### Google Calendar Setup
1. Create a project in [Google Cloud Console](https://console.cloud.google.com/) 1. Create a project in [Google Cloud Console](https://console.cloud.google.com/)