cosmic-pim
The shared substrate under the COSMIC personal-information suite: storage, sync, credentials, and the iCalendar/vCard layer.
This repository holds no user interface. It is the layer three applications sit on so that a sync bug is fixed once rather than three times.
The suite
| Repository | App | What it is | State |
|---|---|---|---|
| slate | Slate | Calendar and tasks | Working — CalDAV sync in-app and in a background daemon, reminders, panel applet, launcher plugin |
| circle | Circle | Contacts | Reads and searches a real address book; the lossless write path is done, the editing UI is not |
| envelope | Envelope | Reads, threads, and syncs a real mailbox over IMAP; no composer yet | |
| cosmic-pim | — | This substrate | 1067 tests |
Names: Slate holds what's on your slate; Circle is your circle of people; Envelope is the universal mail symbol as a word.
The crates
| Crate | What it is |
|---|---|
cosmic-pim-core |
The model (events, tasks, contacts), the one iCalendar/vCard parser the suite shares, vdir storage, a SQLite index for calendar range queries, filesystem watching, and a crash-safe writer |
cosmic-pim-caldav |
CalDAV and CardDAV — protocol, reconciliation, durable writeback queue, and a store trait implemented over the vdir |
cosmic-pim-accounts |
Accounts and credentials: the OS keychain with an encrypted local fallback, and the provider manifests that say where a named service lives |
cosmic-pim-auth |
OAuth 2.0 sign-in and token renewal — the only crate here that talks to a provider's login endpoint |
cosmic-pim-mail |
Mail — the message model over verbatim RFC 5322 bytes, a maildir store, JWZ threading, HTML-to-visible-text extraction, and five engines (IMAP, JMAP, POP3, the Gmail API, Microsoft Graph) with durable writeback |
cosmic-pim-sync |
The layer that joins core, caldav, and accounts — provisioning, one sync pass per account over calendars and address books, and the conflicts a pass could not resolve alone |
Dependencies point downward only. See ARCHITECTURE.md for the diagram, the invariants, and where new code belongs.
Accounts
Signing in works two ways, and an application does not have to care which.
Most servers — Fastmail, Nextcloud, Migadu, a university, a Synology box — take a URL and an app password. Google and Microsoft withdrew password authentication and take OAuth, so the suite runs the authorization-code flow itself: PKCE, a loopback redirect, and a refresh token renewed when it expires.
use ;
let registry = load;
let provider = registry.get.expect;
// Everything the manifest knows — CalDAV, CardDAV, mail — filled in.
let account = provider.account_for;
accounts.add?;
An OAuth provider is the same shape with the flow in between:
let oauth = provider.oauth.as_ref.expect;
let pending = begin?;
open_in_the_users_browser;
let credential = pending.exchange?;
accounts.add_oauth?;
From there nothing distinguishes the two. cosmic_pim_auth::resolve hands back
the secret of the moment — a password, or an access token it renewed and
re-stored on the way — and the CalDAV client sends Basic or Bearer, the
IMAP session LOGIN or AUTHENTICATE XOAUTH2, without knowing which.
Providers are data. Google, Microsoft, Fastmail and iCloud ship compiled
in; a manifest dropped in $XDG_CONFIG_HOME/cosmic-pim/providers/ adds one or
overrides a field of one. No OAuth client id is shipped — one identifies
the application asking, and there is none this project could publish that would
be right for a downstream package — so Google and Microsoft need a two-line
manifest before they can be used:
# $XDG_CONFIG_HOME/cosmic-pim/providers/google.toml
= "google"
[]
= "…apps.googleusercontent.com"
Using these
Not yet on crates.io — the licence gate is open (see Licensing); what is left is pushing this repository and tagging it. Until then, depend by git:
[]
= { = "https://github.com/Magnetar-OS/cosmic-pim", = "v0.1.0" }
# Uncomment to develop against a sibling checkout without retagging.
# [patch.'https://github.com/Magnetar-OS/cosmic-pim']
# cosmic-pim-core = { path = "../cosmic-pim/crates/cosmic-pim-core" }
A [patch] alone does not let you skip the git source: cargo still resolves it.
Until this repository is pushed, the applications use sibling path
dependencies, with the git form written in a comment ready to swap.
What you get for free
Reading a calendar, with sync, is about this much:
use Store;
let store = open_default?;
let today = now.date_naive;
let days = store.occurrences_by_day?;
let tasks = store.todos;
An address book:
use ContactStore;
let book = open_default?;
let matches = book.search;
One sync pass over every enabled account — calendars and address books together, failing per collection rather than per run:
let reports = sync_all;
Mail, in whichever protocol the account uses — IMAP, JMAP, POP3, the Gmail API or Microsoft Graph. Every one of them lands in the same maildir, because each provider API is used as a change feed while the message bytes still come from its raw endpoint:
let secret = resolve?;
let report = sync_account_mail?;
When the server and this device changed the same event, the pass first tries to
settle it without asking: an app that hands the pre-edit bytes to
queue_save_with_base gets non-overlapping changes three-way merged and
re-queued automatically. Only genuinely overlapping edits are recorded — local,
remote, and base intact — for the user to decide:
for in conflicts
An ICS subscription — a holiday calendar, a timetable, anything published as a
webcal:// URL — becomes an ordinary read-only collection, refreshed with
conditional requests and split into one file per event so every vdir reader
sees it:
use feed;
let meta = subscribe?;
refresh?;
And mail can be pushed rather than polled, where the server offers it — IMAP IDLE, or JMAP's event source — with one blocking primitive on each session:
match imap_session.watch?
Interoperability
The on-disk layout is vdir
— a directory per collection, one .ics or .vcf per item. That is not an
implementation detail we happened to land on; it is the point. khal, khard,
vdirsyncer, and Thunderbird read the same files, and a user can walk away with
their data as plain text at any moment.
Building
Licensing
MPL-2.0. The applications are GPL-3.0-only and link these crates without absorbing them. LICENSING.md explains why that split exists, why not MIT and not LGPL, and the one trap (MPL Exhibit B) that would break it.
The iCalendar, CalDAV, and mail layers derive from the Meltemi project, which now grants MPL-2.0 on the donor files explicitly — publishing here is no longer licence-blocked. Details in LICENSING.md.