-
-
Notifications
You must be signed in to change notification settings - Fork 0
Calendar
syn-cal — your calendars, on this machine, synced both ways.
CalDAV accounts (Nextcloud, Fastmail, iCloud, Radicale, Baikal, Posteo, mailbox.org), Google through OAuth, and Microsoft 365 through Graph. There is a window, a terminal view and a command line, and the desktop calendar you already have — the one the bar clock opens — marks the days that have something on them.
syn-cal account add work https://caldav.fastmail.com/
syn-cal login work --user you@fastmail.com
syn-cal discover work
syn-cal enable work "Personal"
syn-cal syncdiscover walks from whatever you typed to the list of your calendars —
/.well-known/caldav, then your principal, then your calendar home — so a bare
domain is enough, and a collection URL pasted from a provider's help page also
works and costs one request.
⚠ New calendars start switched off. Discovery on a work account turns up
every shared calendar in the building, and syncing them because they exist is
how a planner becomes unreadable on first use. Running discover again never
undoes a choice you have made.
⚠ Use an app password where the account has two-factor authentication. iCloud and Fastmail both require one; your ordinary password will be refused, and the message will not always say why.
Google's calendars are CalDAV behind OAuth, so you need a client id — which only you can create, because it identifies your copy of the application.
- console.cloud.google.com/apis/credentials
- Enable the Google Calendar API.
- Create credentials → OAuth client ID → application type Desktop app.
- Copy the client id. There is no secret to copy, and there should not be — see below.
syn-cal account add-google gmail --client-id 1234.apps.googleusercontent.com
syn-cal login gmail # opens your browser
syn-cal discover gmail
syn-cal enable gmail "you@gmail.com"Microsoft removed CalDAV; 365 calendars are reachable only through Graph.
- portal.azure.com → App registrations → New.
- Platform Mobile and desktop applications, redirect URI
http://localhost— the flow uses a loopback port the client picks. - API permissions → Microsoft Graph → Calendars.ReadWrite and offline_access.
syn-cal account add-microsoft work365 --client-id <the application id>
syn-cal login work365⛔ Repeating events are read from Microsoft but never written back. A recurrence rule flattened on the way out would replace a series on your work calendar with a single occurrence, and neither side would report anything wrong. Editing one locally is refused, the local file is left exactly as it is, and the message tells you to edit the series in Outlook. A refusal is visible and recoverable; a silent flattening is neither.
syn-cal agenda |
the next seven days, across every calendar |
syn-cal today / syn-cal week
|
just those |
syn-cal agenda --from 2026-10-01 --days 31 |
any range |
syn-cal gui |
the window |
syn-cal tui |
the month, in this terminal |
syn cal |
sync, from the top-level CLI |
The desktop calendar — the bar clock, or synctl dispatch calendar — marks
days that have something on them and lists the selected day underneath. It reads
what syn-cal has already synced, so it is instant and works offline; it does
not fetch anything itself.
~/.local/share/syn-cal/
accounts.conf the account list. NO PASSWORDS — see below.
<account>/<calendar>/*.ics one file per event. This is the data.
state/ the sync index. Rebuildable, never data.
⛔ The store is a vdir, which is what vdirsyncer and khal write. A
private database would have been less code and would have made syn-cal the only
program on the machine able to read your calendar. Anything that can read a
folder of .ics files can read this one — a backup, a grep, another client.
Delete state/ and you pay one full re-sync. Delete the vdir and you lose
everything, so back up the folder and not the index.
⛔ accounts.conf holds a URL and a username, and nothing else. Passwords
and OAuth tokens go to the keyring through libsecret. A config file carrying a
bearer token is one cat into a bug report, one backup to a shared drive, or
one screen-share away from handing somebody a calendar — a record of where you
are, when, and with whom.
⚠ Every store is read back before it is called a success. On a machine with
no keyring daemon the whole stack reports success and stores nothing; a
credential store that says "saved" when it saved nothing is worse than one that
refuses. Where there is genuinely no keyring, syn-cal falls back to a 0600 file
and tells you so — syn-cal accounts then reports the secret as living in a
file rather than claiming a keyring took it.
There is no client secret for Google or Microsoft, and there should not be: a desktop application is a public client, so whatever secret it were issued would ship inside the binary on every machine that installs it. Both providers say so, which is why the sign-in uses PKCE.
Sync decides from three facts, not two: the server's version, the local file's version, and what both were when they last agreed. With only the first two a difference is unattributable — it looks identical whether they changed it, you changed it, or one of you deleted it.
⛔ An edit is never lost without saying so. When both sides changed the same
event since the last sync, both are kept: the server's version keeps the
UID, and yours is re-filed under a UID derived from it, so in a fortnight you can
still tell which was which. Look for events whose UID ends -syncal-local-.
Deleted here but edited there brings the event back; deleted there but edited here puts it back up. In both directions the rule is the same: a deletion destroys and an edit does not, so the edit is the stronger claim.
syn-cal sync --dry-run # decide everything, change nothing
syn-cal sync --conflict=remote # or =local, if you would rather not keep both--dry-run is worth knowing about before the first sync of a real account.
syn-cal accounts # is it signed in, and where does the secret live
syn-cal calendars work # which are switched on
syn-cal sync --verbose --dry-run"not signed in" on an account that was working usually means an expired
OAuth grant — syn-cal login <name> again. syn-cal renews the token by itself
sixty seconds before it expires, so this is a revoked grant rather than a lapsed
one.
An event that never uploads and is reported as skipped has no UID in its
.ics. Such a file cannot be addressed on a CalDAV server at all; syn-cal says
which file rather than dropping it quietly.
Nothing in the desktop calendar but events in syn-cal agenda means the
compositor could not run syn-cal — it is spawned by name, so check it is on
PATH for the session.
See also: Settings · Commands · The Desktop
Using it
- Installation
- Welcome Guide
- Updating
- Software
- Files
- Settings
- Calendar
- Editor
- Studio
- Terminal
- Keybindings
- Speech and dictation
- Remote Desktop
- Commands
- Configuration
- Nix
- Gaming
- Big Screen
- DaVinci Resolve
- Secure Boot
- Troubleshooting
Customising it
Components
Apps
Hacking on it