Calendar
The calendar service syncs events from your online accounts or local calendar directories (such as vdirsyncer) and shows them in the control center calendar. CalDAV providers (Nextcloud, iCloud, Fastmail, Radicale, mailbox.org, …) are talked to directly; Google Calendar is connected through a browser authorization flow; local vdir directories are watched with inotify. Events are read-only.
[calendar] covers account sync, optional duplicate merging, shared event date and time formats, and event reminders. Calendar-tab-only presentation options, such as the events card and ISO week number column, live under [control_center.calendar].
Clicking an event with an http or https link opens it in your default browser. Noctalia prefers a meeting link in the event location, then a provider-specific meeting link such as a Teams join link in the description, before a generic event URL. Google events without a meeting link open the event in Google Calendar instead. Reminder notifications only open meeting links.
Settings setup
Section titled “Settings setup”The simplest path is Settings → Calendar → Accounts → Add Account.
- iCloud: choose iCloud, enter a local account id such as
personal_icloud, your Apple Account email, and an Apple app-specific password. - CalDAV: choose CalDAV, enter a local account id such as
home_nextcloud, the provider’s CalDAV discovery root, your username, and a provider app password. - Google: choose Google, enter a local account id such as
work_google, then press Save and Connect. A browser opens for Google authorization. - Local (vdir): choose Local (vdir), enter a local account id such as
local_vdir, and the directory path (e.g.~/.local/share/calendars), then press Save. - ICS: choose ICS URL, enter a local account id such as
subscription, then press Save.
Saving an account enables [calendar] automatically. The GUI writes account config to settings.toml using named tables like [calendar.account.personal_icloud]. CalDAV passwords use the account’s explicit credential source: Secret Service by default, or one configured password file. Google refresh tokens are stored in Secret Service. Short-lived Google access tokens remain in memory only.
The last successfully synced events are cached in encrypted form. Their key is derived from the shared Noctalia storage master key configured by top-level [storage].
Account ids are local Noctalia identifiers. Use lowercase letters, digits, and underscores only. The GUI keeps an account id fixed after creation so existing config and credential keys are not removed or renamed.
Existing accounts can be edited from Settings → Calendar → Accounts. Google accounts expose the same name/color editor and include Save plus Save and Connect actions; both save the current editor values, and Save and Connect then opens browser authorization. The delete action is shown only for accounts created in settings.toml; accounts declared in ~/.config/noctalia/*.toml are never removed by the GUI.
Manual setup
Section titled “Manual setup”[calendar]enabled = truededupe_events = false # merge an event that appears in more than one calendar into a single entrydedupe_ignore_patterns = [] # regexes stripped from a title before events are comparedrefresh_minutes = 15event_date_format = "%A %e %B"event_time_format = "%H:%M"
# iCloud CalDAV. The table name is the local account id.[calendar.account.personal_icloud]type = "caldav"name = "Personal iCloud"provider = "icloud"username = "you@example.com"color = "primary" # optional color role or hex colorcalendars = [] # empty = all discovered calendars
# Generic CalDAV discovery root (Nextcloud, Fastmail, Radicale, mailbox.org, …)[calendar.account.home_nextcloud]type = "caldav"name = "Home"provider = "custom"server_url = "https://cloud.example.com/remote.php/dav/"username = "me"calendars = ["personal"] # optional discovered collection ids; empty = allcredential_source = "file"password_file = "/run/agenix/noctalia-caldav"
# Google Calendar (authorized via Settings → Calendar → Accounts → Connect)[calendar.account.work_google]type = "google"name = "Work"
# Local vdir directory (vdirsyncer / khal)[calendar.account.local_vdir]type = "vdir"name = "Local Calendars"path = "~/.local/share/calendars"calendars = []
# Example ICS configuration[calendar.account.holidays]name = "Canadian Holidays"server_url = "https://canada-holidays.ca/ics?cd=true"type = "ics"color = "#FF0000"Credentials are never stored in your config. The password_file setting contains only a path; the file contents hold the CalDAV password.
Sync status
Section titled “Sync status”Each account’s row in Settings → Calendar → Accounts shows its status: that it is syncing (connecting, or downloading events), when it last synced, or why its last sync failed and what fixes it. Most failures are fixed with Edit; a locked keyring with Retry once it is unlocked; a Google account whose authorization is missing or was revoked with Reconnect. Syncs keep being retried at the refresh interval, and the events last synced stay shown meanwhile.
Merging duplicate events
Section titled “Merging duplicate events”dedupe_events (also Settings → Calendar → General → Merge Duplicate Events) is off by default. When enabled, an event that shows up in more than one of your calendars, whether those calendars sit in a single account or in several, is shown once instead of once per calendar. Two events are treated as the same occurrence when their start, end, all-day flag, and title match. The copy kept is the one that shows the most: more filled-in fields (color, link, location), then the longer title.
dedupe_ignore_patterns is a list of ECMAScript regular expressions (matched case-insensitively). Every match is removed from a title before the titles are compared, so two calendars that word the same event slightly differently still merge, and the more detailed title is the one displayed. To ignore a trailing parenthetical, for example, use ['\s*\(.*\)$'] (TOML single quotes keep the backslashes literal). An invalid pattern is skipped.
Reminders
Section titled “Reminders”Noctalia can notify you before an event starts. Reminders are read from the calendar itself (VALARM triggers on CalDAV, ICS, and local vdir events, and reminder overrides or calendar defaults on Google), with a configurable fallback for events that carry none.
[calendar.reminders]enabled = trueuse_event_reminders = true # honor per-event reminders; off = always use the default lead belowdefault_lead_minutes = 10 # used for events with no reminder of their own; 0 = at event startall_day_digest_time = "09:00" # one notification listing today's all-day events; "" disables itThe same four options are in Settings → Calendar → Reminders. Reminders require [calendar] itself to be enabled.
A Google event whose notifications you removed stays silent. Google events that follow a calendar with no default notifications use default_lead_minutes. Set use_event_reminders = false to apply default_lead_minutes to every event regardless.
The notification body counts down while the event approaches and settles on “Starts now” when it begins. Clicking a reminder opens the event’s link when it has one.
All-day events do not notify individually. They are collected into one digest at all_day_digest_time local time (24-hour, zero-padded HH:MM), listing today’s all-day events. The digest fires once per day; starting the shell after that time still shows the day’s digest, and a calendar that syncs later in the morning is not skipped.
A reminder whose time passed while the shell was not running still notifies at startup, as long as the event has not started yet. Reminders for events that already began are dropped rather than shown late.
Reminders are kept in notification history, so one missed while you were away is still in the control center. They follow your Do Not Disturb setting.
Reminder times come from the encrypted event cache, so they work offline and across restarts without a sync.
Credential storage
Section titled “Credential storage”Each CalDAV account has exactly one credential source:
credential_source = "secret-service"is the default. Enter the password through Settings. It requires a provider in the login session, such as GNOME Keyring, KWallet, or KeePassXC.libsecretis the client library; installing it alone does not start or configure a provider.credential_source = "file"requires an absolutepassword_filepath. Noctalia reads that regular file for every sync and never reads or writes a Secret Service password for the account. One trailing LF or CRLF is ignored. Empty files, embedded NUL bytes, non-regular files, and files larger than 64 KiB are rejected.
File-backed credentials work with agenix, sops-nix, and systemd credentials. Configure the secret manager to make the decrypted file readable by the user running Noctalia; Noctalia never edits or deletes the file. Google refresh tokens remain writable credentials and always require Secret Service.
The two CalDAV modes are strict. password_file is rejected unless credential_source = "file", and file mode is rejected without an absolute password_file. Noctalia does not try one source and then fall back to the other.
CalDAV passwords and Google refresh tokens are written to the Secret Service default collection. That collection must be unlocked at login, which is the usual cause of credentials being locked after every reboot when multiple GNOME Keyring keyrings exist. See Secret Service & keyrings for how the default alias works, the multiple-keyrings fix, and automatic recovery.
If Secret Service contains a matching credential but it remains locked after an unlock attempt, Noctalia sends one notification and marks the affected Google account as locked. Unlock the keyring and use Retry; do not reconnect the Google account unless Settings specifically reports that authorization is missing. If a configured Google account has no refresh token, its account row shows Reconnect. The encrypted event-cache master key is separate from account credentials, so a cache-key warning does not by itself mean the Google authorization was lost.
Event cache encryption
Section titled “Event cache encryption”Calendar titles, locations, names, and times are written only to the encrypted $XDG_CACHE_HOME/noctalia/calendar/events.enc cache. Noctalia derives a calendar-specific key from the shared storage master key, separate from the derived clipboard-history key.
The default [storage].key_source = "secret-service" stores the master key in Secret Service. To use agenix, sops-nix, or another provisioner without a keyring, configure one authoritative file:
[storage]key_source = "file"key_file = "/run/agenix/noctalia-storage-key"The file format and generation instructions are documented under Encrypted storage master key. When no storage key is available, live calendar sync continues but events are not read from or written to disk. Missing or incorrect keys preserve the encrypted cache and surface a retry action in Settings. If the original key cannot be restored, or the cache is damaged, Recover Private Storage provides a confirmed reset. Because clipboard and calendar storage share one master key, this reset also removes saved clipboard history. A configured storage key file is never modified.
CalDAV setup
Section titled “CalDAV setup”Noctalia discovers CalDAV calendars from an account/server root, then syncs each selected calendar collection. You do not need to find the provider’s final calendar collection URL yourself.
The account id is the last segment of the named table. For [calendar.account.personal_icloud], the id is personal_icloud. Use short ids with lowercase letters, digits, and underscores only. The id is local to Noctalia; it does not need to match your email address or provider account name.
For manual CalDAV setup with Secret Service, declare the account table first, then open Settings → Calendar → Accounts, edit that account, enter its app password, and press Save. To use agenix or another file provisioner instead, set credential_source = "file" and password_file in the account table. There is no plaintext TOML credential key.
For providers with two-factor authentication, create an app-specific password in the provider’s account security settings and use that value here. Your normal account password will usually fail.
Provider rules:
provider = "icloud"uses Apple’s built-in CalDAV discovery URL. Do not setserver_url.provider = "custom"requiresserver_url, which should be the provider’s CalDAV discovery root, not a specific calendar collection.calendars = []syncs every discovered calendar that supports events.calendars = ["id"]syncs only discovered collection ids. Collection ids are the decoded final path segment of the discovered calendar URL. Leave this empty first, then narrow it manually after confirming discovery.
mTLS (client certificates)
Section titled “mTLS (client certificates)”Some CalDAV servers require mutual TLS: instead of a password alone, the client must present a certificate during the TLS handshake. Noctalia supports this for type = "caldav" accounts with two optional fields:
[calendar.account.secure_dav]type = "caldav"name = "mTLS Server"provider = "custom"server_url = "https://dav.example.com/"username = "me"credential_source = "file"password_file = "/run/agenix/noctalia-caldav"client_cert_file = "~/pki/client.pem"client_key_file = "~/pki/client.key"client_cert_fileandclient_key_filemust be set together and point at PEM-encoded files.~and$VARSexpand; the result must be an absolute path.- Noctalia presents the certificate during discovery and every calendar sync. Use an
https://server_url: libcurl also presents the certificate to any HTTPS host a redirect leads to. - The fields are only valid for caldav accounts;
google,ics, andvdiraccounts reject them.
CA-issued certificates commonly live in an NSS database (for example ~/.pki/nssdb, used by Firefox and NetworkManager). libcurl’s TLS backend cannot read NSS databases directly, so export the pair to PEM first:
certutil -d sql:$HOME/.pki/nssdb -L -n "your-cert-nickname" -a > ~/pki/client.pempk12util -o ~/pki/client.p12 -d sql:$HOME/.pki/nssdb -n "your-cert-nickname"openssl pkcs12 -in ~/pki/client.p12 -nocerts -nodes -out ~/pki/client.keyiCloud
Section titled “iCloud”iCloud needs an Apple app-specific password. Use your Apple Account email as username.
In Settings, choose iCloud and use your Apple app-specific password in the App Password field. For manual TOML, use provider = "icloud" and do not set server_url; Noctalia uses Apple’s CalDAV discovery URL automatically.
[calendar.account.icloud_personal]type = "caldav"name = "Personal iCloud"provider = "icloud"username = "me@example.com"calendars = []
[calendar.account.icloud_family]type = "caldav"name = "Family iCloud"provider = "icloud"username = "family@example.com"calendars = []Do not use a public .ics sharing URL or your normal iCloud password. Noctalia connects to your private iCloud CalDAV account with the app-specific password.
Common iCloud failures:
401or repeated auth failures: regenerate an Apple app-specific password, edit the account in Settings, and save the replacement password.- No events from one calendar: leave
calendars = []first to verify discovery, then select collection ids only after confirming what was discovered. - Shared calendars may have their own collection ids; leave
calendars = []to include them all.
Google setup
Section titled “Google setup”In Settings, choose Google, then press Save and Connect. For manual TOML, add a named account table with type = "google", enable the calendar service, then open Settings → Calendar → Accounts, edit the account, and press Save and Connect. A browser tab opens for Google’s consent screen; approve it and the tab confirms completion. Tokens are obtained through the noctalia OAuth broker (api.noctalia.dev) - no Google Cloud project or client secret is required on your side. Access is calendar.readonly.
If a Google account later shows as disconnected (revoked or expired), edit the account and press Save and Connect again to reauthorize.
If Settings reports that Secret Service is unavailable, confirm that a compatible provider is running in the same login session, then use the retry action. Noctalia also reopens calendar credentials and encrypted storage on its own once the provider appears or its keyring unlocks; see Secret Service & keyrings for how automatic recovery works and the multiple-keyrings caveat. If access is locked or denied, unlock the provider and retry. A cancelled prompt is not treated as a missing credential; start the operation again when you are ready.
[shell].offline_mode = true disables all calendar network traffic; the last successfully synced events remain available from the encrypted on-disk cache when the storage key is available.
ICS setup
Section titled “ICS setup”In Settings, choose ICS URL, paste in a URL that leads to a file in the iCalendar format. For manual TOML, add a named account table with type = ics and set server_url to the URL. You do not need to set any other fields.
For the URL, webcal:// and webcals:// are supported as aliases to https://; Noctalia normalizes all three schemes before fetching data, so using any of them in server_url produces identical behavior.
However you must make sure that it is a public URL, and does not lead to some login page, 404 error, etc. Noctalia should not need any credentials to fetch the data. If the URL does not return iCalendar data once, Noctalia will temporarily fall back to the event cache until it does.
Local directory (vdir / vdirsyncer)
Section titled “Local directory (vdir / vdirsyncer)”In Settings, choose Local (vdir), enter a local account id, and specify the path to your calendar folder. Both tilde (~/...) and absolute paths are supported.
For manual TOML, add a named account table with type = "vdir" and set path:
[calendar.account.local_vdir]type = "vdir"name = "Local Calendars"path = "~/.local/share/calendars"calendars = [] # empty = all discovered collectionsNoctalia automatically explores the directory tree (up to 5 levels deep) to discover calendar collections. For each collection, it reads standard metadata:
- Display name: extracted from the
displaynamefile, theX-WR-CALNAMEheader inside.icsfiles, or falls back to the directory name. - Color: extracted from the
colorfile (standard hex format). - Order: integer sorting priority extracted from the
orderfile.
Discovered collection directories are monitored via inotify. When external synchronization tools (like vdirsyncer) add, modify, or remove .ics event files, Noctalia automatically reloads the calendar without requiring a manual refresh.