CardDAV (Contacts Sync)

bext can serve as a CardDAV server (RFC 6352), enabling contacts synchronization with standards-compliant clients — Apple Contacts, Thunderbird, GNOME Contacts, DAVx5, and others. It shares the same WebDAV foundation and storage backend as CalDAV.

Enabling CardDAV

CardDAV is enabled through the same [caldav] config section, since both protocols share the WebDAV stack and storage:

[caldav]
enabled = true
storage = "memory"   # "memory" | "pg"

# For PostgreSQL storage
# storage = "pg"
# pg_url = "postgresql://user:pass@localhost/bext_dav"

Compile with the caldav feature flag:

bext build --features caldav

When [caldav] is enabled, both CalDAV and CardDAV endpoints are active.

WebDAV Foundation

CardDAV uses the same WebDAV methods as CalDAV:

Method Purpose
PROPFIND Read properties of address books and contacts.
PROPPATCH Modify address book properties.
REPORT Run queries (text search, multiget, sync).
MKCOL Create a new address book collection.
PUT Create or update a contact.
GET Retrieve a single contact (.vcf).
DELETE Remove a contact or address book.

URL Structure

Address book collections follow a predictable hierarchy:

/addressbooks/{user}/                       # User's address book home
/addressbooks/{user}/{book}/                # A specific address book
/addressbooks/{user}/{book}/{uid}.vcf       # A single contact

Example:

/addressbooks/alice/contacts/
/addressbooks/alice/contacts/bob-smith.vcf
/addressbooks/alice/team/

vCard Format

Contacts are stored as vCard 4.0 (RFC 6350) .vcf files. bext parses and validates them on PUT:

BEGIN:VCARD
VERSION:4.0
FN:Bob Smith
N:Smith;Bob;;;
EMAIL;TYPE=work:bob@example.com
TEL;TYPE=cell:+1-555-0123
ADR;TYPE=home:;;123 Main St;Springfield;IL;62701;US
ORG:Acme Corp
END:VCARD

Supported vCard properties include:

Property Description
FN Formatted (display) name.
N Structured name components.
EMAIL Email addresses with type labels.
TEL Phone numbers with type labels.
ADR Postal addresses with type labels.
ORG Organization name.
PHOTO Contact photo (URI or inline base64).
BDAY Birthday.
NOTE Free-text notes.

CardDAV Reports

Clients query address books using REPORT requests. bext supports three report types:

addressbook-query

Search contacts by property value:

<card:addressbook-query xmlns:card="urn:ietf:params:xml:ns:carddav">
  <d:prop xmlns:d="DAV:">
    <d:getetag/>
    <card:address-data/>
  </d:prop>
  <card:filter>
    <card:prop-filter name="EMAIL">
      <card:text-match collation="i;unicode-casemap"
                       match-type="contains">example.com</card:text-match>
    </card:prop-filter>
  </card:filter>
</card:addressbook-query>

addressbook-multiget

Fetch specific contacts by URL in a single request:

<card:addressbook-multiget xmlns:card="urn:ietf:params:xml:ns:carddav">
  <d:prop xmlns:d="DAV:">
    <d:getetag/>
    <card:address-data/>
  </d:prop>
  <d:href>/addressbooks/alice/contacts/bob-smith.vcf</d:href>
  <d:href>/addressbooks/alice/contacts/carol-jones.vcf</d:href>
</card:addressbook-multiget>

sync-collection

Efficient incremental sync (RFC 6578), identical to CalDAV's sync mechanism. The client sends a sync token and receives only changes:

<d:sync-collection xmlns:d="DAV:">
  <d:sync-token>data:,67890</d:sync-token>
  <d:prop>
    <d:getetag/>
  </d:prop>
</d:sync-collection>

Discovery

Clients discover CardDAV using the .well-known redirect (RFC 6764):

GET /.well-known/carddav
→ 301 /addressbooks/

bext registers this redirect automatically when CardDAV is enabled. Client apps only need the server hostname to find the address book home.

Shared Storage Backend

CardDAV shares its storage backend with CalDAV — both use the [caldav] config section and the same database tables. See the CalDAV docs for storage backend details.

Authentication

CardDAV endpoints require authentication, using the same middleware as all other bext routes:

[[routes]]
path = "/addressbooks/*"
auth = "required"

The {user} in the URL path is matched against the authenticated identity. Users can only access their own address books unless granted explicit sharing permissions.

Configuration

CardDAV uses the [caldav] configuration section. Contact-specific limits:

[caldav]
enabled = true
storage = "pg"
pg_url = "postgresql://user:pass@localhost/bext_dav"
max_contact_size_bytes = 65536
max_contacts_per_book = 100000
Field Default Description
max_contact_size_bytes 65536 Max size of a single .vcf upload.
max_contacts_per_book 100000 Safety limit per address book.

Feature Flag

CardDAV requires the caldav feature flag (shared with CalDAV):

bext build --features caldav
Tip

CardDAV is enabled automatically whenever [caldav] is enabled — there is no separate feature flag or config toggle. Disabling contacts while keeping calendars is not currently supported; both share the same WebDAV stack and are always activated together.

Warning

The memory storage backend loses all contact data on restart. Use storage = "pg" in any production deployment. The database schema is shared with CalDAV — a single PostgreSQL instance and pg_url covers both protocols.

Related

- CalDAV (Calendar Sync) — companion protocol sharing the same WebDAV stack and storage backend

- Auth capability — authentication middleware applied to /addressbooks/* routes

- Build flags — compile-time feature flags; caldav enables both CalDAV and CardDAV

- Configuration reference — full bext.config.toml field reference

Links

- RFC 6352 — CardDAV: vCard Extensions to WebDAV — the CardDAV protocol specification

- RFC 6350 — vCard Format Specification — the vCard 4.0 format used for .vcf contact files