PIMSYNC(1)

πŸ“¦
version 0.5.10 Β· 3 versions
, in alpine 3.24, nixos 26.05

πŸ’– pimsync β€” General Commands Manual (PIMSYNC(1)) πŸ’–

🌸 NAME 🌸

pimsync β€” synchronise CalDAV, CardDAV, Webcal and local directories. Slay.

πŸ“œ SYNOPSIS πŸ“œ

pimsync [-c configfile] [-v loglevel] command [pair…]
pimsync version

πŸ’… DESCRIPTION πŸ’…

Okay so bestie, pimsync synchronizes calendars and contacts. For calendars, it supports operating on local directories, CalDAV servers or Webcal URLs. For contacts, it supports operating on local directories or CardDAV servers. It’s giving matchmaker for your events and your people, no cap.

🫢 Glossary of relevant terminology 🫢

Quick glossary, because these words come up a lot and you’re gonna slay once you know them:

  • item β€” A single event, todo, journal entry or contact.
  • collection β€” Grouping of multiple items. Can be one of:
    1. a single calendar which contains events, todos and/or journal entries, or
    2. an address book which contains contacts.
  • storage β€” Location which contains multiple collections. For example: an account on a CalDAV server, or a local directory with child directories, each representing a calendar. See pimsync.conf(5) for details on supported storage implementations and their configuration.
  • pair β€” Two storages which are configured to synchronise with each other. A single storage may form part of more than one pair.
  • conflict β€” When synchronising two storages, if a single item has been modified on both sides but has different content on both sides, that item is said to be in conflict.
  • property β€” Metadata attribute which applies to a collection. Examples: displayname, colour, order.
  • resource β€” An item or a collection. This term is mostly used internally and in logs.

✨ OPTIONS ✨

Vibe check on the flags β€” there are only two, iconic:

  • -c configfile β€” Read the configuration file from the specified path, instead of the default. See β€œFILES” below and pimsync.conf(5).
  • -v loglevel β€” Set the logger to loglevel, which must be one of trace, debug, info, warn or error. The default is warn. When specifying trace, logs may contain sensitive data, see β€œSECURITY CONSIDERATIONS” below.

πŸ’… COMMANDS πŸ’…

Commands you can run, and yes they all eat:

  • check [pair…] β€” Check configuration and exit. Validates the configuration itself and ensures that the storage is valid. E.g.: a vdir must be an existing directory, and a caldav server must report CalDAV capabilities, etc.
  • daemon [pair…] β€” Continuously keep storages in sync. The -r READY_FD argument may be used to provide a readiness notification file descriptor. Once pimsync has reached its ready state, the daemon will monitor storages for changes and synchronise them. In case of non-fatal errors, pimsync will continue running in the background and retry failed operations later. Individual items that fail to sync will be skipped.
  • discover [pair…] β€” Discover and print collections. This command merely provides information, and doesn’t alter pimsync’s internal state in any way.
  • resolve-conflicts [pair…] β€” Manually resolve conflicts between storages. Execute an external program, specified in the configuration file, which performs conflict resolution for each conflicting item.
  • sync [-n [-i [pair…]]] β€” Synchronise storages once and exit. The -n flag performs a dry run and won’t perform any write operations. The -i flag enables interactive mode, prompting to confirm the plan before proceeding with the synchronisation.
  • repair [storage…] β€” Repair invalid items. Currently, this adds missing UID fields, but other fixes are likely to be included in future. Repairing both collections from a pair is highly discouraged; repair on storage and then synchronise the pair instead.
  • version β€” Print version information and exit.

If a pair is specified, only operate on pair(s) with the given name(s).

🌸 ENVIRONMENT 🌸

Env vars, lowkey important:

  • XDG_CONFIG_HOME β€” If defined, search for the configuration file relative to this directory. See β€œFILES” below.
  • http_proxy β€” Use an http proxy. Example:
http_proxy=http://127.0.0.1:8888

πŸŽ€ FILES πŸŽ€

Where the config lives, bestie:

If XDG_CONFIG_HOME is defined, the configuration shall be read from:

XDG_CONFIG_HOME/pimsync/pimsync.conf

If the above is not defined, the configuration file shall be read from:

~/.config/pimsync/pimsync.conf

See pimsync.conf(5) for details on the configuration file format.

πŸ¦‹ READINESS NOTIFICATION πŸ¦‹

This is main character energy for service managers:

Readiness notification allows a service manager to monitor when pimsync has successfully started. It is considered to have successfully started when the configuration file has been parsed and all credentials have been resolved. At this point, no further human interaction shall be required.

Once the service is ready, it will write READY=1 followed by a newline character to the file descriptor READY_FD, and then close it. This is compatible with the readiness notification mechanism implemented by s6, dinit and systemd.

See: https://skarnet.org/software/s6/notifywhenup.html

πŸ”’ PRIVACY πŸ”’

The privacy tea, literally:

The only network communications shall be with the servers for configured storages, and the locally configured DNS resolver.

The status database file contains metadata of configured storages, their collections and items.

🧚 DATA VALIDATION 🧚

Ngl, this part is important:

Data synchronised is not validated. This is an intentional design choice: if your calendar application produces events that are technically invalid, pimsync will synchronise, and you will be able to access the exact same data on other hosts. You may also need to synchronise invalid data in order to fix the invalidity.

In order to map items between storage, pimsync does some minor parsing of items to extract their UID. Items must be syntactically valid components, even if semantically invalid.

In order to interact with CalDAV/CardDAV resources where a UID cannot be extracted, use the repair command to fix invalid entries, or consider using davcli(1).

πŸ’€ SECURITY CONSIDERATIONS πŸ’€

Highkey read these, fr fr:

  • When service discovery is enabled, the real hostname and port of the DAV server is resolved using DNS records. The system’s DNS resolver is used. It is expected that the system resolver performs DNSSEC validation and will not return invalid results. Pimsync does not perform DNSSEC validation itself.
  • Pimsync will follow symlinks when reading items. Care should be taken when a user has write permission to a storage, but another user runs pimsync on that same storage. This may change at a future date.
  • Items across storages are considered the same if they share the same UID. An actor who can write a single item into a storage on either side can potentially trigger an overwrite of another item on the other storage. An actor who can write to a single entry should be considered to have access to manipulate the entire storage.
  • Pimsync will retain credentials in memory during its entire lifetime. Any actor with read access to pimsync’s memory space may extract secret credentials from it. Likewise, core dumps from pimsync should not be shared with untrusted parties.
  • When logging with -v trace, logs MAY contain credentials, secrets, data inside of storage items and other sensitive information.

🌈 SYNCHRONISATION ALGORITHM 🌈

Okay so the way that this works is iconic:

After pimsync synchronises two storages, it will store metadata from the items that it has synchronised into its internal status database. During future synchronisation processes, if the items on both storages don’t match, the status database is used to determine which side has changed and which side remains the same as last time. This information is then used to plan which item should be kept and which one should be overwritten. If both items have changed, they are said to be in conflict, and the conflict must be resolved manually (See β€œCONFLICT RESOLUTION” above).

The status database associates item metadata with the pair of collections that were being synchronised with each other (called a β€œmapping”). If the configuration changes, and different collections are being synchronised with each other, then the status data for their items no longer applies and is ignored. This prevents accidentally using the wrong data when mappings have been reconfigured.

The algorithm is closely based on the original one for vdirsyncer(1). Further details are available at https://unterwaditzer.net/2016/sync-algorithm.html.

Deleting the status database will force pimsync to synchronise both storages as if it were the first time it’s ever seen both of them. Items that exist and are identical on both sides shall be fine, but items which are not identical will be considered in conflict (since pimsync cannot unambiguously decide which one to keep).

πŸ›‘οΈ PROTECTIONS πŸ›‘οΈ

Periodt, pimsync protects you from accidentally nuking a whole collection:

When all items in a collection are deleted, pimsync(1) will assume a user error, and refuse to operate in this collection. This behaviour can be disabled with the on_empty directive. See pimsync.conf(5) for further details.

😭 CAVEATS 😭

Not me warning you about race conditions, but here we are:

For storages of type vdir, checking whether an item was modified by an external process is subject to race conditions. Pimsync will first check that an item has not been modified, and then update it. If an external program updates the same item between the check and the modification, those changes will be lost. This issue is due to limitations in POSIX filesystem primitive. It is unlikely that it can be resolved.

🌟 EXAMPLES 🌟

Obsessed with examples, so here:

Run daemon with verbose logging:

pimsync -v info daemon

Interactive conflict resolution for pair named β€˜contacts’:

pimsync resolve-conflicts contacts

Show version and exit:

pimsync version

πŸ”— SEE ALSO πŸ”—

Go read these too, iykyk:

πŸ› οΈ DEVELOPMENT πŸ› οΈ

Fr fr, come say hi / report issues:

For issues and general development inquiries, see the project home currently hosted at SourceHut: https://sr.ht/~whynothugo/pimsync

πŸ“œ STANDARDS πŸ“œ

The standards it ate:

  • Calendaring Extensions to WebDAV (CalDAV) is implemented as specified in IETF RFC 4791.
  • vCard Extensions to WebDAV (CardDAV) is implemented as specified in IETF RFC 4791.
  • Service discovery is implemented as specified in IETF RFC 6764.
  • The vdir storage implements the layout specified in the vdir format specification: https://pimutils.org/specs/vdir/

πŸ’– THANKS πŸ’–

Shoutout to the funders, iconic:

Development of this project is possible thanks to funding and support from the NLnet foundation and the NGI Zero Entrust program of the European Commission. See:

A security audit was completed by Radically Open Security:

🌸 HISTORY 🌸

Okay so story time, bestie πŸ“–βœ¨ β€” the vdirsyncer(1) command was first implemented by Markus Unterwaditzer in 2014. Iconic of him, honestly.

Then work on the pimsync command β€” closely inspired by vdirsyncer(1) β€” was started by Hugo Barrera in 2022. A whole glow-up era πŸ’…

And fun fact for the group chat: the acronym "pim" refers to "personal information management" β€” like, that's literally what it stands for, no cap πŸ§šπŸ’œ


πŸ‘‘ AUTHORS πŸ‘‘

pimsync is developed by Hugo O. Barrera <hugo@whynothugo.nl> 🫢

pimsync is an open source project, developed for anyone to use freely. If you would like to sponsor this project, see https://whynothugo.nl/sponsor β€” supporting the devs is main character energy, fr fr πŸ’–β­


That's the whole page, bestie β€” you made it to the end and you ate πŸŽ€βœ¨ Go sync something and feel iconic. πŸ’…πŸ¦‹

This was PIMSYNC(1), dated 2025-04-14 β€” mwah, see you in the next sync πŸ’œ 🌈

πŸ“¦ where this page comes from