Motivation
JSS already stores contacts as JSON-LD using the W3C vCard vocabulary (vcard:AddressBook, vcard:Individual, vcard:fn, vcard:hasEmail, etc.). Web apps can read and write this data natively. But right now, these contacts live only in the browser — they can't sync to your phone, desktop mail client, or any other app.
CardDAV is the missing bridge. It's the universal standard (RFC 6352) that every contact app already speaks. By adding a CardDAV plugin, every contact stored on a JSS pod becomes instantly accessible from:
- iOS/macOS Contacts (built-in, zero config)
- Android (via DAVx⁵ or built-in)
- Thunderbird (built-in CardDAV support)
- GNOME/KDE Contacts
- Nextcloud (can federate with external CardDAV)
- Outlook (via CalDav Synchronizer plugin)
This turns JSS from a web-only data store into a real personal data hub — your contacts live on your pod, and every device syncs to it.
What this enables
- Phone sync — Add a contact in a web app, it appears on your iPhone/Android. Edit on your phone, it syncs back to your pod.
- Desktop integration — Thunderbird, Apple Contacts, GNOME Contacts all become Solid-aware without knowing it.
- Migration path — Users can export contacts from Google/iCloud and import them into their pod via any CardDAV client.
- Interop with Nextcloud — Nextcloud can mount external CardDAV sources, making JSS pods federatable.
- Solid ecosystem value — Demonstrates that Solid pods aren't just for web apps; they integrate with the tools people already use.
Architecture
JSS already has the right patterns — this follows the same plugin approach as ActivityPub (src/ap/) and Nostr relay (src/nostr/).
What already works
- File-based storage with
GET/PUT/DELETE on any path
- Auth (WAC, NIP-98)
- Content negotiation (JSON-LD ↔ Turtle)
- The vCard RDF vocabulary is already the data model used by SolidOS contacts-pane
What needs to be added (~300-500 lines)
src/carddav/index.js ← Fastify plugin registration + route setup
src/carddav/xml.js ← WebDAV XML request/response helpers
src/carddav/vcf.js ← JSON-LD vcard ↔ .vcf text conversion
New HTTP methods
| Method |
Purpose |
Notes |
PROPFIND |
Discover address books and list contacts |
WebDAV XML envelope |
REPORT |
Query contacts (multiget, sync-collection) |
CardDAV-specific queries |
MKCOL |
Create new address book collection |
Simple container creation |
GET, PUT, DELETE for individual .vcf resources already work via existing handlers — CardDAV reuses standard HTTP for the actual contact CRUD.
Data flow
Phone/Desktop ←→ CardDAV (vCard .vcf) ←→ JSS Plugin ←→ JSON-LD (vcard vocab) ←→ Filesystem
↑
Web apps (contacts pane) read/write
the same JSON-LD files directly
The conversion between JSON-LD vcard and .vcf text is mechanical — it's the same fields:
vcard:fn → FN:Alice Wonderland
vcard:hasEmail → EMAIL:[email protected]
vcard:hasTelephone → TEL:+1-555-0101
vcard:hasPhoto → PHOTO;VALUE=URI:https://...
vcard:organization-name → ORG:Acme Corp
Server config
Following the existing pattern:
createServer({
carddav: true, // Enable CardDAV plugin
carddavPath: '/contacts/' // Mount point (default)
})
CLI: --carddav --carddav-path /contacts/
Prior art
- Radicale (Python) — minimal CardDAV in ~2000 lines, stores as flat .vcf files
- Baikal (PHP) — wraps SabreDAV
- SolidOS contacts-pane — already uses vcard vocabulary, which is the same data model
Size estimate
Small. The WebDAV XML wrapping is the most verbose part, but the actual logic is straightforward. The plugin should be comparable in size to the ActivityPub plugin.
Motivation
JSS already stores contacts as JSON-LD using the W3C vCard vocabulary (
vcard:AddressBook,vcard:Individual,vcard:fn,vcard:hasEmail, etc.). Web apps can read and write this data natively. But right now, these contacts live only in the browser — they can't sync to your phone, desktop mail client, or any other app.CardDAV is the missing bridge. It's the universal standard (RFC 6352) that every contact app already speaks. By adding a CardDAV plugin, every contact stored on a JSS pod becomes instantly accessible from:
This turns JSS from a web-only data store into a real personal data hub — your contacts live on your pod, and every device syncs to it.
What this enables
Architecture
JSS already has the right patterns — this follows the same plugin approach as ActivityPub (
src/ap/) and Nostr relay (src/nostr/).What already works
GET/PUT/DELETEon any pathWhat needs to be added (~300-500 lines)
New HTTP methods
PROPFINDREPORTMKCOLGET,PUT,DELETEfor individual.vcfresources already work via existing handlers — CardDAV reuses standard HTTP for the actual contact CRUD.Data flow
The conversion between JSON-LD vcard and
.vcftext is mechanical — it's the same fields:Server config
Following the existing pattern:
CLI:
--carddav --carddav-path /contacts/Prior art
Size estimate
Small. The WebDAV XML wrapping is the most verbose part, but the actual logic is straightforward. The plugin should be comparable in size to the ActivityPub plugin.