ex_dav

⚠️ Beta / heavy WIP. APIs are unstable and may change between releases. Not yet recommended for production use.

CalDAV server library for Elixir. Mounts as a Plug under any host application. Ships with a Postgres-backed reference adapter and an in-memory adapter; bring your own for arbitrary backends.

plug ExDav.CalDav.Plug,
  storage: ExDav.Storage.Postgres,
  authenticator: {ExDav.Authenticator.Basic,
                  verify: {ExDav.Storage.Postgres, :authenticate}}

What you get

Usage in a Phoenix app

# config/config.exs
config :ex_dav, repo: MyApp.Repo

# lib/my_app_web/endpoint.ex
plug ExDav.CalDav.Plug,
  storage: ExDav.Storage.Postgres,
  authenticator: {ExDav.Authenticator.Basic,
                  verify: {ExDav.Storage.Postgres, :authenticate}}

Run ExDav.Storage.Postgres's migrations into your repo (copy from apps/ex_dav/priv/repo/migrations — these will move into the library itself in a future release).

Custom storage adapter

defmodule MyApp.CalDavStorage do
  @behaviour ExDav.Storage

  @impl true
  def list_calendars(username) do
    # return [%{name:, displayname:, description:, components:, ctag:, objects:}]
  end

  @impl true
  def put_object(username, cal_name, obj_name, ical), do: ...

  # ...all 11 callbacks
end

Then mount:

plug ExDav.CalDav.Plug,
  storage: MyApp.CalDavStorage,
  authenticator: {MyApp.CalDavAuth, []}

Custom authenticator

defmodule MyApp.CalDavAuth do
  @behaviour ExDav.Authenticator

  @impl true
  def authenticate(conn, _opts) do
    case extract_token(conn) do
      {:ok, principal} -> {:ok, conn, principal}
      :error -> :unauth
    end
  end
end

Standalone

For a runnable reference deployment, see apps/ex_dav_server in this repository — a minimal Phoenix app that wires the library to Postgres and ships an admin LiveView for managing CalDAV principals.