Documentation

Go ahead, make  your day.

How to use Sempa and how to host it. Features remain the same in browser, the Windows desktop app and Android, except where noted.

Active development & backups

Sempa is under active development. It's stable for everyday use — people run it as their daily task manager — but new features and fixes land often, and upgrading rebuilds the container. Before you come to rely on it, turn on automatic backups. They take about two minutes to set up and mean a bad upgrade, a disk failure, or a fat-fingered restore can never cost you your data.

A Docker volume is not a backup. Your data lives in the sempa_data volume and survives rebuilds — but that's one copy on one disk. Scheduled backups put a second copy somewhere your server isn't, which is what actually protects you.

Enable automatic backups

In the app, open Settings → Backup & Restore and:

  • Turn on automatic daily backups and pick an hour (an idle time, e.g. overnight) and a retention count — how many past backups to keep.
  • Choose a destination. Local is the default; for real safety add an off-server one — S3, WebDAV, or Google Drive — and hit Test to confirm the credentials work.
  • Optionally encrypt with a passphrase. Store the passphrase somewhere safe — without it an encrypted backup can't be restored.
  • Run one now to verify the whole path end to end, then Download the archive to keep an extra copy.

To recover, return to the same screen and Restore from a backup file. Database migrations run automatically on startup, so a backup taken on an older version restores cleanly after you upgrade. Full options are in Backup & restore below.

Before every upgrade (git pull && docker compose build && docker compose up -d), it's worth a quick Run now backup first — so if anything looks off afterwards, rolling back is trivial.

First run

After signing in, a short setup wizard helps you connect email and calendar — all optional, and addable later in Settings. You land on Today, ready to plan.

Getting around

On desktop and web, a left sidebar pairs a pinned Search with a sectioned nav rail: Today and This Week up top, then Rituals (Plan Day, Shutdown), Inbox (Email, Reminders) and Library (Backlog, Journal). Change the grouping (Spaces · Plan·Focus·Review · Flat) and section style (Labels or Dividers) in Settings → Appearance. The rail footer holds a utility icon row (Settings, light/dark, desktop Widget), an update indicator when a new version is available, the sync status, and an account chip (avatar · email · Sign out).

The day view's right-hand panel is a tabbed dock — Schedule · Inbox · Jira · Goals — beneath a mini-calendar.

On mobile, a bottom tab bar carries Today, Week, Journal, Lists and More. The More sheet is grouped: a quick row (Settings, light/dark, Widget, AI Import), a Plan group (Plan Day, Schedule, Backlog, Search), an Inbox group (Email, Reminders, Jira, Shutdown) and your account. A + button creates a task on list screens. To install the PWA, open your Sempa instance in a browser and choose “Install” / “Add to Home Screen”.

Command palette. Press Cmd/Ctrl+K anywhere to open a keyboard-first launcher — jump to any screen, run an action (including Import with AI), or fall through to search for any query you type.

Mobile gestures. Swipe a task left to reschedule it to tomorrow. Long-press the app icon for New task / Plan day / Shutdown quick actions. And Share-to-Sempa: share a link or text from any app and Sempa turns it into a task (optionally running it through AI Import).

Tasks

The core unit. Open any task to edit it in a side panel (desktop) or bottom sheet (mobile).

  • Create with the + button, the “Add task” box, or n on a day view; e edits the hovered task.
  • Notes support pasted URLs, which render as tidy link-preview chips (title, site, thumbnail).
  • Status flows backlog → planned → in progress → done (plus cancelled) — drag between columns on the week board, or toggle the checkbox.
  • Due date & estimate (15 min – 8 h), colour-coded tags, sub-tasks, file attachments, and time-blocking onto the schedule.
  • A soft “roughly at” hint orders a task in the day without a hard block.
  • Remind me (date + time) sets a hard alert — see Reminders.
  • Right-click menu (desktop): right-click a task for quick actions (Edit · Complete · Focus · Delete), an objective for its actions, or empty board space for New task / Go to today / Search.
  • Undo delete: deleting a task shows an Undo toast; non-Jira tasks are restored, and a deleted Jira task returns to the Jira list automatically.

Plan your day

Plan Day is a guided morning ritual: write your intention, see what carried over from yesterday, and pull tasks from your backlog into today. Your previous day's note is shown for continuity.

Week & objectives

The Week board is a Kanban across seven days — drag tasks between days and statuses. Set Weekly Objectives (the handful of outcomes that matter) and link tasks to them so progress is visible. Drag an objective onto a day to create a task already linked to it; completing an objective's linked task(s) auto-completes the objective. Copy as Markdown exports a clean, single-heading bulleted list. Dedicated screens let you plan the week and run a weekly review — capturing wins, challenges and your next focus — and both surface as gentle in-app prompts.

Shutdown & journal

Shutdown is an end-of-day ritual: tick off what's done, reschedule what isn't, record a win and an optional reflection, and close the day cleanly. It all collects in the Journal — a searchable timeline of your daily intentions and reflections and your weekly wins, challenges and next focus. Optionally show reflections inline on day and week screens (Settings → Appearance → contextual reflections).

Daily encouragement

A quiet, rotating quote appears with the logo animation when you open Sempa, and flashes briefly when you complete a task — a small bit of encouragement, never fixed on screen. Turn it off or edit the list (add, remove, reset) in Settings → Account.

Backlog

A single list of everything not yet scheduled — your inbox of ideas. Pull items into days as you plan.

Focus & time tracking

Hit Focus on any task to start a Pomodoro. A persistent timer widget then follows you across the whole app, showing the task, the countdown, and how long you've actually spent vs. your estimate — so an overrun is obvious in the moment, not a surprise at the end. The timer survives navigation and reloads.

Sempa is built with time-blindness in mind — we routinely think a task will take 20 minutes when it takes an hour — and logs honest time, not the countdown: when you finish, it shows the measured time and asks you to confirm or adjust it (including “didn't really work on it”), so only the number you confirm is recorded. Complete a task with no timer running and a gentle “How long did that take?” prompt catches it (it skips very quick tasks and backs off when you're clearing a burst).

  • Planned-vs-actual profile — Sempa learns how your estimates compare to reality, overall and per tag (e.g. “email tasks usually run 1.8×”), and nudges you toward realistic estimates. Plan my day uses the same calibration.
  • Estimate with AI (with AI assist on) predicts a task's duration — general at first, then personalised from your own logged history.
  • Day capacity — set how many focused hours fit a realistic day and Sempa quietly flags overload, judged by calibrated time so optimistic guesses don't sneak a day over its limit.
  • Insights (in the nav) turns the logged data into a picture — a plain-language headline (“you take 1.8× longer than you plan”), planned-vs-actual totals, your multipliers by activity and tag, and recent over/under per task.

Tune all of it — the completion prompt, the day-capacity limit, per-activity default times, and your timer lengths — in Settings → Time tracking.

Search looks across tasks, objectives and journal entries. On list views, switch into tag filter mode to show only tasks with a given tag. When filtering by a tag, export the resulting list as CSV or clean Markdown from the Search page.

Lists

The Lists view holds standalone, undated checklists — an ongoing groceries list, a packing list, materials to buy. Add items, drag to reorder, and tick them off (checked items grey out and strike through in place; untick to restore). Lists live independently of any day, so a half-checked list just waits for you.

  • Link to a task — from a task's Lists section, attach an existing list or spin up a new one, with an optional “archive when the task is done” per list for automatic cleanup.
  • Organise with AI — groups items into natural categories (Produce, Dairy…) using your local model.
  • Export Markdown — downloads the list as a .md file (name as the title, items as bullets).
  • Archive / unarchive whole lists to keep the view tidy.

Lists are local-first like the rest of Sempa — add, reorder and check items with no connection; they sync when you're back online.

Recurring tasks

Create daily, weekly or monthly templates (Settings → Recurring Tasks). Instances generate automatically; editing one occurrence customises just that one while the series rolls forward. To change the template itself — title, notes, tags, estimate or schedule — use its edit button in Settings → Recurring Tasks; changes flow to upcoming occurrences while anything you've already customised stays put.

Timezone-aware. Recurring instances, the daily-board rollover and the morning digest all key off your home timezone — set during install (auto-detected) or any time in Settings → Notifications → Timezone. Set it to your real zone, or the day turns over at the wrong hour. Task times are floating (they don't shift when you travel); a travelling device follows its own zone, and Sempa offers a banner when it notices you're away from home.

Today board

The Today view is a rolling board of day columns with today anchored at the left edge. Scroll left into the past and right into the future continuously — no week boundaries to page through. The ‹ › header buttons and the j / k keys jump a week at a time; Today re-anchors on the current day.

Desktop widget

The Windows desktop app includes a floating Widget — a compact, always-on-top panel showing what's up next, with quick checkboxes and a quick-add box. Open it from the sidebar icon rail or a single click on the system-tray icon (double-clicking the tray icon opens the main window).

Calendars & schedule

See events beside your tasks in the Schedule tab of the right-hand dock. Connect Google and Fastmail calendars, a CalDAV server (with optional push of your time-blocks back to it), or subscribe to any ICS / webcal feed.

  • Time-block by dragging a task onto the timeline — it moves into that day and snaps to the task's planned time (its estimate, or a 30-minute default); list edits reflect live on the block.
  • Resize a block's top or bottom edge to change its length, which updates the task's planned time.
  • Per-calendar colours are assigned automatically — including each individual Fastmail calendar (Family, personal, shared…) — and blocks show the source calendar name so feeds stay distinguishable.
  • In the schedule's Calendars panel (and Settings → Calendars for feeds) show/hide each calendar and tap its colour dot to recolour it. All calendars are listed, even ones with no events that day.

Email → tasks

  • Gmail / Fastmail: star an email and it imports as a task.
  • Task inbox: forward (or auto-forward) mail to a dedicated address to create tasks; allow-list senders.
  • AI title cleanup: imported subjects are tidied into clean task titles by your local model — no API key, and nothing leaves your server. It's part of the broader AI assist suite; opt in during install.sh or in Settings → Integrations → AI.

The Email screen lets you triage incoming mail and convert messages to tasks, with the original linked.

AI assist — local & private

Sempa can use a small language model to take the friction out of capture and review. Everything runs on a model you host yourself (Ollama), on your own server — no data is ever sent to any third party, there's no API key, and there are no usage costs or rate limits. If the model is unavailable, every feature quietly falls back to the manual path; nothing breaks.

You're in complete control. AI is off by default and does nothing until you enable it — during install.sh (which installs Ollama, pulls the model and tests the connection) or in Settings → Integrations → AI. The model only ever suggests; nothing is automatic or destructive — you approve.
  • Per-feature toggles — each feature below has its own switch in Settings → Integrations → AI → “AI features”. Turn off any you don't want and it disappears from the app. Features only appear when the model is reachable.
  • Model management — see your downloaded models and their on-disk sizes, switch the active model, download a new model with a live progress bar, or remove one to reclaim disk — all from Settings. The default is qwen2.5:1.5b (~1 GB, CPU-friendly); browse alternatives on ollama.com/library. Use any chat/instruct model. If one errors with “does not support generate/chat”, its local copy is stale from an Ollama upgrade — re-pull it (ollama pull <model>) or remove and re-download in Settings.
FeatureWhereWhat it does
AI ImportLists button · Ctrl/⌘+Shift+I · command palette · mobile More menuTurn a URL (a recipe, an article) or pasted text into a task with ordered steps as sub-tasks plus a companion checklist of items (ingredients, supplies). Structured recipes (schema.org) are read directly without the model; everything else goes through it — always into an editable preview (place it today or in the backlog) before saving. The source link is stamped onto the task. Each entry point is individually toggleable.
Natural-language quick add✦ on a task titleType “lunch with Sam thu 1pm 30m #personal” and it fills in the title, date, time, estimate and tags.
Suggest tags✦ on the tag editorRecommends tags only from your existing tag set, so your taxonomy stays consistent.
Break into subtasks✦ on a taskSplits a task into a few concrete, ordered subtasks you can keep or edit.
Title cleanup & summaryEmail / Jira importTidies imported subjects and issues into concise, action-oriented titles with a rough estimate.
Plan my dayPlan DaySuggests a focused order for today's tasks around your calendar events — applied only when you click.
Draft weekly reviewWeek ReviewDrafts your wins / challenges / next focus from the week's completed tasks and objectives; you edit before saving.
Reflection promptsShutdownOffers a couple of context-aware end-of-day questions based on what you did and didn't finish.

Jira

Connect Jira to import your assigned issues as tasks — full guide in docs/JIRA.md. In brief:

  • Rich import: issues sync with a link to the ticket and the description copied into the task notes (offline-readable); re-sync keeps title, status and metadata current.
  • Manage from the task: a live Jira section shows status, type, priority, assignee, labels, an “Open in Jira” link, and the issue's real workflow transitions as buttons. Marking a Jira task done also transitions the ticket.
  • Multi-select filters + default view: filter the Jira panel by any combination of Priority / Type / Status / Epic / Sprint; your chosen filters and scope save as your default, with a Reset.
  • Delete returns it to Jira: deleting a Jira-linked task re-imports it, so it goes back to the Jira list rather than disappearing.

Reminders, notifications & routines

Configured in Settings → Notifications. Set Remind me (date + time) on any task for a hard alert with Mark done and Snooze 1h actions; tapping it opens the task. Reminders are deduplicated, so re-arming or snoozing won't double-fire. Choose delivery channels independently:

  • Web Push — native notifications on Windows/Android browsers and installed PWAs.
  • Native Android — push to the installed app, with on-device alarms that fire even fully offline.
  • Custom webhook — POST to ntfy, Gotify or your own service (endpoint, optional topic, HTTP method, auth header), with a Send test notification button.

Pick from 10 calm alert tones (each with a ▶ preview), and set the times for the in-app weekly planning and daily shutdown routine prompts — calm in-app banners, not OS alarms.

Will reminders fire if I'm offline? Within reason — Android schedules on-device OS alarms locally, and a briefly-down server fires the reminder late rather than losing it. The Windows desktop app needs to be running to fire; settings changed offline sync on reconnect. A background web-push on a plain browser uses the OS default sound (a browser limitation).

Offline & sync

The desktop and Android apps are local-first: they keep a local copy of your data, so the app stays fully usable with no connection. Changes queue and sync automatically when the server is reachable; a sync indicator shows status. Plain web (in a browser, not installed) talks directly to the server.

Sharing (household)

One Sempa server can hold more than one person. Manage people in Settings → Users — an admin can add users and reset passwords, and each person can change their own password. Google sign-in and the bootstrap env-login both map to a user account.

Everything is private by default. Tasks, lists and weekly objectives each have a Private / Shared toggle: flip one to Shared and everyone on the server can see and edit it (sharing a task cascades to its sub-tasks; sharing a list cascades to its items). Un-sharing removes it from everyone else again while keeping it yours. Your daily plan, shutdown journal and focus sessions stay owner-only, always. It's built for a household sharing what it wants and keeping the rest to itself — not for large teams.

The Shared toggle is an online action, so it's hidden when the app is running fully offline.

Backup & restore

In Settings → Backup & Restore: automatic daily backups at an hour you choose with a retention count, optional encryption with a passphrase, and one or more destinations (each with a Test button). Run now for an on-demand backup, Download the latest archive, or Restore from a file. Migrations run automatically on startup, so a backup taken on an older version restores cleanly after you upgrade; your data lives in the sempa_data Docker volume.

DestinationWhat you need
Local folderNothing — kept on the server (the default). That's one copy on one disk, so pair it with an off-server destination.
S3-compatibleBucket, region, access key ID + secret (AWS S3, Backblaze B2, MinIO…), optional key prefix.
WebDAV / NextcloudThe WebDAV URL plus a username + password (or app password).
Google DriveA connected Google account (see below). Backups land in a dedicated Sempa Backups folder.
Encryption: turn on a passphrase and archives are encrypted (AES-GCM) before they leave the server. Store the passphrase somewhere safe — without it, an encrypted backup cannot be restored.

Connecting Google Drive

Google Drive backup reuses your server's Google OAuth client — the same one that powers Google sign-in and Gmail import (see Configuration). Once that's configured, add a Google Drive destination and click Connect Google Drive; you'll consent in Google and come back to Sempa. Sempa requests only the drive.file scope, so it can see only the files it creates — never the rest of your Drive.

  • Authorise both redirect URIs on your OAuth client (Google Cloud Console → Credentials), or the connect step fails with a redirect-mismatch error:
    • {APP_URL}/api/v1/auth/google/callback — sign-in
    • {APP_URL}/api/v1/backup/drive/callback — Drive backup
  • On Android, consent opens in a secure in-app browser tab and returns you straight to the app when it finishes — no detour through the website.
Stop Google backups from breaking every 7 days. If your OAuth app's publishing status is “Testing”, Google expires its tokens after 7 days — so Drive backups quietly start failing a week after you connect them and Sempa shows a “Google access expired — reconnect” banner. The permanent fix is to publish the app to Production: in Google Cloud Console → OAuth consent screen → Audience, set the status to In production. Because Sempa is a single-user app requesting only its own files, you can safely click past the “unverified app” warning (Advanced → Go to Sempa) — verification only removes that warning, it isn't needed to stop the expiry. If access has already lapsed, click Reconnect Google Drive on the backup screen after publishing.

Restoring

On the same screen, Restore from a downloaded archive (enter the passphrase if it was encrypted). A restore replaces your current data, so it asks you to confirm first. Because migrations run on startup, an archive from an older Sempa restores cleanly onto a newer one.

Appearance & themes

In Settings → Appearance, choose from six full-interface themesTerracotta (default), Forest, Plum, Slate, OLED Black and Ocean — each with a live preview. Every theme has a light and dark mode, except OLED Black, which is dark-only. The same page has a text-size slider, the sidebar grouping and section style controls for the desktop nav rail, and the contextual reflections toggle. Your choice is remembered per device and applied before first paint — no flash on load.

  • Use system UI font — render the interface in your device's own font instead of Sempa's Plus Jakarta Sans.
  • Match system accent — on platforms that expose an accent colour (Windows, GNOME 47+/KDE), use it in place of the theme's; off keeps the Sempa accent.
  • Use system title bar (desktop) — hand the window frame to your OS; off keeps Sempa's in-app title bar, which mirrors your window-button layout.

Updates

Sempa checks GitHub for newer releases and surfaces them in-app: a subtle indicator in the nav rail, an update toast (Download · What's new · Later), and Settings → About — current version, update channel (Stable / Beta), an automatic-checks toggle, when it last checked, and a manual Check for updates. On web and desktop this points you to the installer. Optional silent background self-update for the desktop app is opt-in.

Security

Sempa is built to be safe to run yourself. It's free and open source under the AGPL-3.0, so anyone can read exactly what it does, and it follows the OpenSSF Best Practices standard (passing) — a recognised checklist for how securely an open-source project is run.

Every change is checked automatically before it ships: CodeQL scans the code, govulncheck and Trivy watch for known vulnerabilities in dependencies (the build fails on serious ones), and gitleaks guards against committed secrets. The parsers that read untrusted input — calendar feeds, imported email, recurrence rules and AI output — are continuously fuzzed, with tests run under Go's race detector. Dependencies are kept current by Dependabot, the full test suite runs on every change, and the main branch is protected so code only lands through reviewed pull requests. Release downloads are signed with cosign, Docker base images are digest-pinned, and the server runs as a non-root container.

For your own setup, two steps do most of the work: reach Sempa over Tailscale so nothing is exposed to the public internet, and turn on encrypted backups. The plain-language Security overview covers the rest, and the full OpenSSF assessment and SECURITY.md document every criterion and scan.

Found a vulnerability? Please report it privately via GitHub's security advisories rather than a public issue, so it can be fixed before details are made public.

Settings overview

SectionWhat you configure
AccountSign-in, profile
IntegrationsGmail, Fastmail, Jira, CalDAV, task inbox, and AI (local model: connection, model download/remove, per-feature toggles)
CalendarsConnected calendars, ICS/webcal feeds, show/hide, colours
TagsCreate, rename, recolour tags
Recurring TasksDaily / weekly / monthly templates
NotificationsReminders, delivery channels, sounds, routines, home timezone
Time trackingCompletion prompt, day capacity, per-activity default times, focus/break lengths
Backup & RestoreSchedule, encryption, destinations (Local · S3 · WebDAV · Google Drive)
AppearanceTheme, light/dark, text size, sidebar grouping, contextual reflections, system font / accent / title bar
AboutVersion, update channel, automatic checks, “Check for updates”

Keyboard shortcuts

KeyAction
Cmd/Ctrl+KOpen the command palette (navigate, run actions, search)
Ctrl/⌘+Shift+IAI Import (URL / text → task)
nNew task (day view)
eEdit the hovered task
tGo to today
j / kPrevious / next week
?Show shortcuts help
EscClose the open dialog

Self-hosting · quick start

You need Docker and Docker Compose (v2). The installer asks a few questions (URL, auth method, and optional extras like Tailscale, email-to-task and local AI), writes your config, builds the image and starts the container — about two minutes.

# Prerequisites: Docker + Docker Compose v2 git clone https://github.com/moorew/sempa.git cd sempa bash install.sh

Open the URL it prints and follow the setup wizard. Everything else — email, calendar and Jira accounts — is connected later in Settings. To upgrade: git pull && docker compose build && docker compose up -d. Your data lives in the sempa_data volume and is preserved across rebuilds.

Run from the prebuilt image

Don't want to build from source? Every release also publishes a multi-arch (amd64/arm64) server image to the GitHub Container Registry — install.sh offers a “pull prebuilt image” option, or pull it directly:

docker pull ghcr.io/moorew/sempa:latest # or a pinned tag, e.g. :1.0.149

The server container runs as a non-root user (uid 10001) and is built on digest-pinned base images.

Upgrading from an older (root) image? The data volume used to be root-owned. install.sh and deploy/update.sh re-own it automatically; if you manage the stack by hand, run once before starting: docker compose run --rm --no-deps --user root --entrypoint sh sempa -c 'chown -R 10001:10001 /data'

Self-hosting with Tailscale

Tailscale is the easiest way to reach Sempa securely from all your devices without exposing it to the public internet — no port forwarding, automatic HTTPS, and only your devices can connect.

Your URL is the sempa node. The bundled ts-sempa sidecar joins your tailnet as its own dedicated node named sempa — not the host's hostname. So your address is https://sempa.<tailnet>.ts.net, where <tailnet> is your MagicDNS name (e.g. tail1234.ts.net).
  • One-time tailnet setup: enable MagicDNS and HTTPS Certificates in DNS settings, and add an ACL tag tag:container under tagOwners so the node never expires.
  • Install Tailscale on every device you want to reach Sempa from. The server host doesn't need it — the sidecar container provides Tailscale.
  • Generate a tagged auth key (with tag:container) and paste it when the installer asks for TS_AUTHKEY.
  • Run install.sh and enter https://sempa.<tailnet>.ts.net as the App URL.
  • HTTPS is automatic — the sidecar runs Tailscale Serve and provisions the cert; there's no manual tailscale cert step. Then enter that URL in each app's Server field and sign in.
Any reverse proxy works too. Caddy, nginx or Traefik — set APP_URL and forward to port 9001. If you expose it publicly, enable authentication (Google OAuth or username/password).

Configuration

Configuration lives in two local files: .env (URL, port — for Docker Compose substitution) and .env.local (secrets, loaded into the container). Authentication supports Google Sign-In (OAuth — the same client also powers Gmail import) and username + password; leaving the password unset disables auth entirely (fine for a trusted Tailscale-only install).

VariablePurpose
APP_URLWhere Sempa is accessible (no trailing slash)
HOST_PORTPort to expose on the host (default 9001)
GMAIL_CLIENT_ID · GMAIL_CLIENT_SECRETGoogle OAuth client — one client powers sign-in, Gmail import and Google Drive backup. Authorise both the /api/v1/auth/google/callback and /api/v1/backup/drive/callback redirect URIs, and publish the app to Production to avoid 7-day token expiry (see Backup)
SEMPA_TIMEZONEHome IANA timezone (e.g. America/Toronto); sets when the day rolls over for recurring tasks, the board and the digest. Auto-detected by install.sh; also editable in Settings → Notifications
SEMPA_ALLOWED_EMAILSComma-separated allow-list for Google sign-in
SEMPA_USERNAME / _PASSWORDUsername + password auth (unset disables auth)
TS_AUTHKEYAuth key for the Tailscale sidecar
OLLAMA_BASE_URLLocal model endpoint for AI title cleanup (empty = off)
OLLAMA_MODELModel for title cleanup (default qwen2.5:1.5b)
EMAIL_FORWARD_TOKEN · SMTP_ALLOWED_SENDERSEmail-to-task webhook token; sender allow-list
VAPID_SUBJECT · FCM_KEY_PATHWeb Push contact; Android push key
SMTP_PORTPort for the built-in inbound SMTP server (default 2525)
COMPOSE_PROFILESSet to ai to start the optional ollama service (written automatically when you choose local AI)
INBOX_POLL_INTERVALHow often to poll the email inbox (default 1m)
CALENDAR_POLL_INTERVALHow often to refresh ICS feeds + the Fastmail calendar (default 15m; empty disables)

Full details, integration setup and all optional variables are in the repository README.

Note: this guide mirrors the project README. The canonical version lives in the repository — see the README User Guide.

Ready to set it up?