pimalaya/calendula
pimalaya/calendula has 58 GitHub stars and 7 forks. Star changes are not measured yet. Last checked 2026-11-09 09:09 UTC. Last push 2026-11-08.
So werden diese Zahlen gemessen
CLI to manage calendars
What you can do
- Shared API: calendar, event, todo, journal and item work the same whichever backend serves the account.
- One family per component kind: event (VEVENT), todo (VTODO) and journal (VJOURNAL) each render the columns their kind is read by; item keeps the raw, unfiltered view.
- Microsoft Graph: Outlook and Microsoft 365 calendars through the Graph API, the iCalendar document synthesized both ways, Windows time zones read as their IANA names.
- Protocol-specific APIs: caldav, gcal, pimdir and vdir each expose what only that backend has.
Gemessener Sternzuwachs
- in 24h
- Verlauf wird gesammelt
- in 7 days
- Verlauf wird gesammelt
- in 15 days
- Verlauf wird gesammelt
- in 1 month
- Verlauf wird gesammelt
Nettoänderungen in überlappenden Zeiträumen, keine Tagessummen. Fehlende Messwerte bleiben leer. Sterne zeigen Interesse, nicht Qualität.
README.md
Original📅 Calendula

CLI to manage calendars.
[!CAUTION] Calendula is
v0.x: expect breaking changes between releases until it stabilises.
Table of contents
- Features
- RFC coverage
- Installation
- Configuration
- Usage
- Migration
- AI policy
- License
- Social
- Contributing
- Sponsoring
Features
- Shared API:
calendar,event,todo,journalanditemwork the same whichever backend serves the account. - One family per component kind:
event(VEVENT),todo(VTODO) andjournal(VJOURNAL) each render the columns their kind is read by;itemkeeps the raw, unfiltered view. - Microsoft Graph: Outlook and Microsoft 365 calendars through the Graph API, the iCalendar document synthesized both ways, Windows time zones read as their IANA names.
- Protocol-specific APIs:
caldav,gcal,pimdirandvdireach expose what only that backend has. - CalDAV: talk to any standard calendar server, with basic or bearer authentication.
- Google Calendar: the native API v3, where Google's CalDAV bridge is crippled, with the iCalendar document synthesized both ways.
gcaladds sharing, free/busy, recurrence expansion and quick add. - vdir: read and write a local vdir home, one directory per calendar.
- pimdir: read and stage writes against a local pimdir store, the offline cache a sync engine fills.
- Agenda view:
event agendadraws a cal(1)-style grid marking the days that carry an event. - Composer:
-i/--interactiveonbuild,createandupdateopens the item in the commanditem.composernames, your editor included, and what it leaves in the file is the decision. - Build without an account:
<family> buildapplies a source and the composer and prints the iCalendar rather than sending it, on a machine holding no configuration at all. - Discovery: an email address is enough to find a provider's server, through SRV records,
.well-knownand the provider configuration documents. - Interactive wizard:
calendula configurediscovers an account, picks the best way to your calendars, signs it in and tests it, stores your password, and saves the account; a first run with no configuration is offered it. - Multi-account: one TOML file, one block per account, several files deep-merged when you want secrets apart.
- JSON output: every command switches to JSON with
--json, for scripts and other tools, andcalendula json-schemadescribes the shape each one returns. - Full standard, blocking client with TLS support:
- Rustls with ring crypto (requires
rustls-ringfeature, enabled by default) - Rustls with aws crypto (requires
rustls-awsfeature) - Native TLS (requires
native-tlsfeature)
- Rustls with ring crypto (requires
[!TIP] Each backend sits behind its own cargo feature (
caldav,gcal,msgraph,vdir,pimdir), all enabled by default. Build with--no-default-featuresand pick the ones you need.The default
wizardfeature brings the interactiveconfigure; leave it out of a scripted or embedded build that never prompts.The default
vendoredfeature builds SQLite from source; leave it out to link the system one.
RFC coverage
| RFC | What is covered |
|---|---|
| 4791 | CalDAV: calendar collections, calendar object resources, and the calendar-query REPORT with its time-range filter |
| 4918 | WebDAV: the PROPFIND, PROPPATCH, MKCOL, GET, PUT and DELETE methods CalDAV builds on |
| 5397 | Current-user-principal, the first step of the CalDAV discovery walk |
| 5545 | iCalendar: parsing and editing the event, to-do and journal components a calendar holds |
| 6764 | CalDAV service discovery: the _caldav and _caldavs SRV records, and .well-known/caldav |
| 6578 | Collection synchronization, whose sync token a CalDAV calendar listing reports |
| 7617 | HTTP Basic authentication |
| 6750 | HTTP Bearer authentication, for a provider-issued or broker-refreshed API token |
Installation
Pre-built binary
As root:
curl -sSL https://raw.githubusercontent.com/pimalaya/calendula/master/install.sh | sudo sh
As a regular user:
curl -sSL https://raw.githubusercontent.com/pimalaya/calendula/master/install.sh | PREFIX=~/.local sh
These commands install the latest binary from the GitHub releases section.
For a more up-to-date version than the latest release, check out the releases GitHub workflow and look for the Artifacts section. These pre-built binaries are built from the master branch.
[!NOTE] Such binaries are built with the default cargo features. If you need specific features, please use another installation method.
Cargo
cargo install --locked --git https://github.com/pimalaya/calendula.git
With only the local backends, and no network code at all:
cargo install --locked --git https://github.com/pimalaya/calendula.git \
--no-default-features \
--features vdir,pimdir,rustls-ring
Nix
If you have the Flakes feature enabled:
nix profile install github:pimalaya/calendula
Or run without installing:
nix run github:pimalaya/calendula
Sources
git clone https://github.com/pimalaya/calendula
cd calendula
nix run
Configuration
The configuration is loaded from the first existing path among:
$XDG_CONFIG_HOME/calendula/config.toml$HOME/.config/calendula/config.toml$HOME/.calendularc
Override the path with calendula -c <PATH> or CALENDULA_CONFIG=<PATH>; multiple paths can be passed at once, separated by :. The first is the base and the rest are deep-merged on top. The full field reference lives in config.sample.toml.
Run calendula configure to launch the wizard. It asks one question, taking an email address, a server URL or a local folder path, and the shape of what you type decides the rest.
An address is discovered, and the best way to your calendars is picked for you: the provider's own calendar API where one is known (Google Calendar, Microsoft Graph), else CalDAV, signing in through your browser when Ortie is installed, else with your password. A review shows the pick in plain words, any other way found or a server URL of your own being one change away. When nothing is discovered, the wizard asks for your server URL.
A URL is taken as the CalDAV context root, which is how a self-hosted server publishing no SRV record gets configured. Only encrypted servers are accepted: https://, or caldav:// and caldavs://, both read as https. A folder is detected as a vdir home or a pimdir store.
Your password, or API token, is typed once, after the wizard names the server it goes to, and tested right away; a refused one can be typed again, or its login changed. Once it works, the wizard offers to store it in a keyring it finds installed, reading it back to check, or to keep a command of your own, or the raw value. An OAuth sign-in is handed off to Ortie, whose token command the account reads.
The wizard then offers to save the account: to a new configuration file, or appended to the one you already have, leaving its comments and formatting untouched.
A bare calendula finding no configuration offers the wizard, as does any command needing an account; once you have one, bare calendula prints the help.
Redirect it instead to keep the document yourself:
calendula configure > ~/.config/calendula/config.toml
Apple
Apple exposes calendars via CalDAV, but you cannot use your regular password. You need to generate an app-specific password (required once two-factor authentication is on):
[accounts.example]
caldav.discover = "icloud.com"
caldav.server = "https://caldav.icloud.com/"
# The home URL is usually of this shape:
#caldav.home = "https://caldav.icloud.com/<id>/calendars/"
caldav.auth.basic.username = "[email protected]"
caldav.auth.basic.password.raw = "***"
calendar.default = "home"
Use the gcal backend, which speaks the Calendar API v3 directly. Both routes need OAuth 2.0, and an access token expires within the hour, so point the token at a broker such as Ortie rather than pasting one in. calendula configure with a Google address picks this backend and signs in through Ortie, writing ["ortie", "token", "show", "-a", "calendula-<account>"].
[accounts.example]
gcal.auth.token.command = ["ortie", "token", "show"]
# Your email address for the primary calendar, or a
# "[email protected]" value for a secondary one. Run
# `calendula calendar list` to read them.
calendar.default = "[email protected]"
Google stores events as JSON and exposes no iCalendar representation of one, so calendula synthesizes the document you read and re-projects what you write.
Properties with a well-defined iCalendar slot are authoritative in both directions, and provider-scoped ones surface as read-only X-GOOGLE-* properties.
Google-only fields (the event colour, the guest permissions, working-location and out-of-office blocks) survive an update untouched, and everything else is stashed verbatim on the server so it round-trips instead of being dropped on the next write.
Only events project: a VTODO or VJOURNAL is refused by name, since Google models neither.
Google is also reachable over CalDAV, but only in a crippled form: bearer tokens are the sole authentication, MKCALENDAR is refused outright, and the discovery entry point is off-spec, so each calendar has to be addressed by hand and none can be created.
If you want it anyway, set caldav.home to the base URL and make the calendar id the <CALENDAR-ID>/events segment:
[accounts.example]
caldav.home = "https://apidata.googleusercontent.com/caldav/v2"
caldav.auth.bearer.token.command = ["ortie", "token", "show"]
# Primary calendar: "<your-email>/events".
calendar.default = "[email protected]/events"
Microsoft
Use the msgraph backend, which speaks the Graph API: Microsoft offers no CalDAV for calendars. Graph takes an OAuth 2.0 token with the Calendars.ReadWrite scope, so point the token at a broker such as Ortie. calendula configure with a Microsoft address picks this backend and signs in through Ortie.
[accounts.example]
msgraph.auth.token.command = ["ortie", "token", "show", "-a", "calendula-example"]
Proton
Not supported: Proton exposes no calendar API, neither CalDAV nor through Proton Bridge (which proxies mail only). Calendars are reachable only from Proton's own web and mobile apps.
Fastmail
Standard CalDAV with the mailbox address and its app password. If caldav.discover / caldav.server return a 404, point caldav.home straight at the calendar home-set to skip the discovery walk:
[accounts.example]
caldav.home = "https://caldav.fastmail.com/dav/calendars/user/[email protected]/"
caldav.auth.basic.username = "[email protected]"
caldav.auth.basic.password.raw = "***"
Run calendula calendar list once connected to read the calendar ids (the ID column), then set calendar.default to the one you want.
Posteo
Standard CalDAV with the mailbox address and its password.
[accounts.posteo]
caldav.discover = "posteo.de"
caldav.server = "https://posteo.de:8443/"
# The home URL is usually of this shape:
#caldav.home = "https://posteo.de:8443/calendars/<username>/"
caldav.auth.basic.username = "[email protected]"
caldav.auth.basic.password.raw = "***"
calendar.default = "default"
Local calendars
No server is involved, so nothing needs discovering. Point calendula at a directory and it works offline.
A vdir home is one directory per calendar, holding one .ics file per item. This is what vdirsyncer writes, and what most local tools read:
[accounts.local]
vdir.home-dir = "~/.local/share/vdirsyncer/calendars"
calendar.default = "personal"
A pimdir store is the offline cache a sync engine fills: a SQLite index plus content-addressed bodies, shared with the other Pimalaya clients reading the same store.
It is a cache, not a server, so calendars come from the sync and the collection verbs refuse here. Writes are staged for the next sync to push:
[accounts.cached]
pimdir.root = "~/.local/state/neverest/example"
# Usually left unset: a store synced by one account is read as that one.
#pimdir.account = "personal"
Run calendula pimdir status to see which account you are reading, how much of each calendar is downloaded, and how many creations are still queued. An item that is listed but not downloaded reads as "body not fetched" until a sync hydrates it.
Run calendula pimdir search <query> to search the account's events (--kind todo or journal for the others) through the search index the sync engine keeps, in the pimdir SEARCH query language: budget location:lisbon, attendee:[email protected] date:2026-11, --kind todo status:in-process priority:high. Days, today and yesterday are read in the local zone (TZ, then the system's); a line under the table names the index's zone when the sync engine reads floating times and dates in another. A write still queued shows once a sync indexed it. calendula pimdir count <query> counts the same.
Usage
Run calendula --help for the full command tree, and calendula <command> --help for any subcommand's arguments and its JSON output shape (printed when the global --json flag is set). calendula json-schema --dir ./schemas writes the JSON Schema of every --json payload.
A few real command lines:
calendula calendar list
calendula event list --calendar personal --from 2026-08-01 --to 2026-08-31
calendula event agenda -3
calendula todo list --calendar tasks
calendula journal list --calendar notes
calendula item read --calendar personal event-1.ics
calendula event build -i -o event.ics
calendula event create --calendar personal -i
calendula todo update --calendar tasks -i todo-1.ics
calendula pimdir status
calendula pimdir search budget date:2026-11
calendula gcal free-busy --from 2026-08-10 --to 2026-08-14
calendula gcal quick-add "Lunch with Ada tomorrow at noon"
License
This project is licensed under either of:
Social
- Chat on Matrix
- News on Mastodon or RSS
- Mail at [email protected]
Sponsoring
Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:
- 2022 → 2023: NGI Assure
- 2023 → 2024: NGI Zero Entrust
- 2024 → 2026: NGI Zero Core
- 2026 → 2027: NGI Zero Commons Fund
This program is part of Pimalaya, free software funded entirely by grants and donations. If you find it useful, consider sponsoring its development:
Ähnliche Projekte entdecken
codecrafters-io/build-your-own-x
Meistere das Programmieren, indem du deine Lieblingstechnologien von Grund auf neu erschaffst.
561,250 Sternepublic-apis/public-apis
Eine gemeinschaftliche Liste kostenloser APIs
497,507 Sternemorluto/rea
Mit Agenten alles per Reverse Engineering untersuchen, vom Verhalten der Anwendung bis hin zu nativen Binärdateien.
445,844 Sterneopenclaw/openclaw
Die KI, die wirklich Dinge erledigt. Jedes Betriebssystem, jede Plattform. Auf die Hummer-Art. 🦞
394,231 Sterne