Skip to content

nicolasacchi/railsdav

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Railsdav

A self-hosted CardDAV server as a Rails Engine. Mount it in any Rails 8+ app for contact sync with DAVx5, Apple Contacts, Thunderbird, and other CardDAV clients.

Implements RFC 6352 (CardDAV), RFC 4918 (WebDAV), and RFC 6578 (WebDAV Sync).

Features

  • Full CardDAV protocol support (sync, multiget, addressbook-query)
  • Web UI for managing contacts and addressbooks
  • Import/export in vCard (.vcf), CSV, and JSON formats
  • Addressbook sharing between users (read/write permissions)
  • Public share links
  • Session-based auth for web UI, HTTP Basic Auth for CardDAV clients
  • vCard data stored as-is — never parsed into columns

Quick Start

rails new my-contacts --skip-active-storage
cd my-contacts

Add to your Gemfile:

gem "railsdav"
bundle install
bin/rails db:migrate

Add a root route in config/routes.rb:

Rails.application.routes.draw do
  root to: redirect("/login")
end

Enable registration in config/initializers/railsdav.rb:

Railsdav.configure do |config|
  config.allow_registration = true
end
bin/rails server

Open http://localhost:3000, register an account, and point your CardDAV client at:

http://localhost:3000/.well-known/carddav

Configuration

Create config/initializers/railsdav.rb:

Railsdav.configure do |config|
  # Display name shown in the web UI header
  config.site_name = "Contacts"

  # Base URL used in DAV sync-token URLs (required for CardDAV sync)
  config.site_url = "https://contacts.example.com"

  # Layout template to use (default: "railsdav/application")
  config.layout = "railsdav/application"

  # Allow new user registration (default: false)
  config.allow_registration = true

  # Optional callback to gate DAV access (e.g. require a subscription)
  # Return true to allow, or a Rack response array [status, headers, body] to deny.
  config.dav_auth_callback = ->(user, env) {
    if user.paid?
      true
    else
      [402, { "Content-Type" => "text/plain" }, ["Payment Required"]]
    end
  }
end

Host App Requirements

Railsdav is a full (non-isolated) Rails engine. It owns models, migrations, controllers, views, and middleware. Your host app must satisfy these contracts.

Required Railties

Your config/application.rb must load:

require "active_record/railtie"
require "active_job/railtie"      # for deliver_later (even :async adapter works)
require "action_mailer/railtie"   # engine sends invitation emails

Session middleware must be enabled (Rails default cookie sessions are fine).

Database

The engine creates 5 tables via migrations 001008: users, addressbooks, contacts, sync_changes, addressbook_shares. Running bin/rails db:migrate picks them up automatically — the engine appends its migration path to the host's.

You can add columns to users (e.g. admin), but must not rename or remove columns the engine created. SQLite, PostgreSQL, and MySQL all work.

Action Mailer

The engine sends invitation emails when users share addressbooks. Your host must configure a delivery method in production:

# config/environments/production.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { address: "smtp.example.com", ... }
config.action_mailer.default_url_options = { host: "contacts.example.com", protocol: "https" }

Set the sender address via config.mailer_from (see Configuration above). The engine uses deliver_later, so Active Job must be available — even the default :async adapter is sufficient.

Routes

The engine injects routes for: /login, /logout, /register, /addressbooks, /invitations/:token/accept, /profile, /s/:token (public shares), and /up (health check). The CardDAV middleware handles /.well-known/carddav and /dav/ at the Rack level.

Your host must:

  • Define a root route (the engine redirects to addressbooks_path after login but does not define root)
  • Avoid conflicting routes at the paths listed above

Extending the User Model

The engine owns User with username, email, password_digest, has_secure_password, and several callbacks. Extend it from an initializer:

# config/initializers/user_extensions.rb
Rails.application.config.to_prepare do
  User.class_eval do
    has_one :subscription, dependent: :destroy

    def paid?
      subscription&.active?
    end
  end
end

Do not remove has_secure_password, override create_default_addressbook or claim_pending_invitations callbacks, or change how email is normalized.

Layout

The engine provides a default layout at layouts/railsdav/application. To use your own:

Railsdav.configure do |config|
  config.layout = "application" # your app/views/layouts/application.html.erb
end

If you override the layout, it must include: CSRF meta tags (<%= csrf_meta_tags %>), <%= yield %>, and flash message display. Link the engine stylesheet for correct icon/component sizing:

<%= stylesheet_link_tag "railsdav/application", "data-turbo-track": "reload" %>

Middleware

The engine inserts CardDav::Middleware before Rails::Rack::Logger. It intercepts all requests to /dav/ and /.well-known/carddav. Do not remove, reorder, or add middleware that rewrites these paths.

Testing

If your test suite uses FactoryBot, the engine auto-prepends its factory paths so engine factories load first. Extend them with FactoryBot.modify:

# spec/factories/users.rb (host app)
FactoryBot.modify do
  factory :user do
    trait :admin do
      admin { true }
    end
  end
end

URL Structure

Path Description
/.well-known/carddav Discovery endpoint (redirects to /dav/)
/dav/ DAV context root
/dav/{username}/ User principal
/dav/{username}/contacts/ Addressbook home set
/dav/{username}/contacts/{uri}/ Addressbook collection
/dav/{username}/contacts/{uri}/{card}.vcf Individual vCard
/login Web UI login
/register Web UI registration
/addressbooks Web UI dashboard

Running Tests

bundle exec rspec

RFC References

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors