texxasrulez / rounddav
RoundDAV - a SabreDAV-based CalDAV/CardDAV server tailored for Roundcube.
Suggests
- texxasrulez/rounddav_files: Required to have a Files page and attach RoundDAV Files from within Roundcube
- texxasrulez/rounddav_provision: Required to use Roundcube for self Integration
README
A lightweight CalDAV, CardDAV, and WebDAV storage engine designed for self-hosters who want full control without dragging in a monster stack. RoundDAV powers calendars, contacts, and file storage behind Roundcube - cleanly and predictably.
While this may work like any other sabre based server, this one is tailored for Roundcube specifically.
There are 3 Roundcube plugins to use with this and extend the bridge between the two.
Required for use with Roundcube:
RoundDAV Provision Roundcube Plugin
Recommended Plugins:
RoundDAV Files Roundcube Plugin
RoundDAV Bookmarks Roundcube Plugin
Features
- CalDAV & CardDAV: Standards-compliant calendars and addressbooks
- WebDAV Filesystem: Per-user storage rooted at a filesystem path you control
- Provisioning API: Roundcube can create DAV users automatically
- SSO-Ready: Includes
/public/sso_login.phpand/public/sso_logout.php - Admin UI: Simple administration panel for principals, calendars, addressbooks
- Per-User Extras: Extra calendars and addressbooks can be created on first login
- Clean PHP: No frameworks, minimal dependencies, easy to debug
Directory Layout
Typical layout on disk:
rounddav/
config/
config.dist.php
config.php
public/
index.php
install.php
admin/
files/
api.php
sso_login.php
sso_logout.php
src/
Provision/
Dav/
...
config/– configuration filespublic/– web-exposed entry points (admin UI, files UI, SSO, API)src/– RoundDAV internals (provisioning, DAV backends, etc.)
Installation
- Copy
rounddav/to your server (outside your main vhost if you like). - Point a vhost or alias at
rounddav/public/.
Example Nginx snippet:
location /rounddav/ { alias /var/www/rounddav/public/; index index.php; }
- Run the installer in your browser:
https://your.server/rounddav/public/install.php
-
Fill in:
- Database DSN / user / password
- Files root path (for WebDAV file storage)
- Admin username + password
-
Submit. The installer writes
config/config.phpand initializes the database. -
Run composer update in root to install dependencies.
Configuration Overview
config/config.php is generated from config.dist.php and contains (at least):
return [ 'database' => [ 'dsn' => 'mysql:host=localhost;dbname=rounddav', 'user' => 'rounddav', 'password' => 'secret', 'options' => [], ], 'files' => [ 'root' => '/srv/rounddav/files', 'public_url' => 'https://your.server/rounddav/public/files/', ], 'admin' => [ 'username' => 'admin', 'email' => 'admin@example.com', 'password_hash' => '$2y$10$...', ], 'provision' => [ 'shared_secret' => 'change_me_provision', 'principal_prefix' => 'principals', ], 'sso' => [ 'enabled' => true, 'secret' => 'change_me_sso', 'ttl' => 600, ], ];
Key points:
files.root– base directory for per-user WebDAV storage.files.public_url– where the browser reaches the Files UI.admin.email(oradmin.username) – use this to identify the admin user in templates.provision.shared_secret– for API calls if you ever protect them further.sso.secret– must matchrounddav_sso_secretin the Roundcuberounddav_provisionplugin.security.headers– optional response headers. Leavex_frame_optionsempty (default) if Files UI must be embedded by Roundcube on a different origin.
Provisioning API
RoundDAV exposes a simple HTTP API for provisioning users:
POST /rounddav/public/api.php?r=provision/user
Content-Type: application/json
Example payload (this is what the Roundcube plugin sends):
{
"username": "user@example.com",
"password": "plaintext-or-derived",
"extra_calendars": [
{
"uri": "todo",
"displayname": "Tasks",
"mode": "tasks",
"shared": false
}
],
"extra_addressbooks": [
{
"uri": "work",
"displayname": "Work Contacts",
"shared": false
}
]
}
The server will:
- Ensure the principal and credentials exist
- Ensure the default calendar + addressbook exist
- Ensure any additional calendars/addressbooks defined in the request exist
The endpoint replies with JSON, e.g.:
{
"status": "ok",
"message": "Provisioning OK for user@example.com",
"principal_uri": "principals/user@example.com",
"principal_id": 6
}
SSO Endpoints
These are used by Roundcube (rounddav_provision + rounddav_files) to log users into the web UI without a second login.
Login
GET /rounddav/public/sso_login.php?user=<user>&ts=<unix>&sig=<hmac>
user– principal identifier (usually the email / Roundcube username)ts– Unix timestamp when the token was issuedsig–hash_hmac('sha256', "$user|$ts", $config['sso']['secret'])
If valid:
- Sets
$_SESSION['rounddav_files_user'] = $user - Redirects the browser to
files.public_urlor./files/
Logout
GET /rounddav/public/sso_logout.php?user=<user>&ts=<unix>&sig=<hmac>
- Signature is based on
"$user|$ts|logout"
If valid:
- Destroys the PHP session
- Leaves no visible output
Roundcube’s logout hook fires a tiny new Image().src = ".../sso_logout.php?..."; call to trigger this.
Admin UI
Accessible under:
https://your.server/rounddav/public/admin/
You can:
- View and manage principals
- Create/delete calendars and addressbooks for each principal
- Edit calendar/addressbook properties (URI, displayname, flags)
- Toggle options like “tasks only” vs “events only” vs both
The Admin UI is intentionally minimal, built for admins who already know what DAV is.
Config Backup, Restore, and Embed-Safe Security Settings
The Admin UI (/public/admin/?action=config) now includes config snapshot and backup tools for config/config.php.
Backup and restore workflow
In Config Snapshot:
- Create backup now writes a timestamped
config-YYYYmmdd-HHMMSS-xxxxxxxx.phpfile undervar/config_backups/ - Download exports a single backup file
- Restore replaces the live
config.phpwith the selected backup - Download all as ZIP streams all backups as one archive for off-host storage
RoundDAV also creates a backup automatically before config writes/imports/restores.
Retention policy
Retention is configurable in the same card:
- Keep newest backups (
backups.config.keep_last) - Delete backups older than days (
backups.config.max_age_days,0disables age pruning) - Auto-prune after config writes/restores/backups (
backups.config.auto_prune)
Defaults in config/config.dist.php:
'backups' => [ 'config' => [ 'keep_last' => 30, 'max_age_days' => 0, 'auto_prune' => true, ], ],
Recommended settings for Roundcube iframe embedding
If rounddav_files is shown inside Roundcube via iframe (especially cross-origin):
- Keep
security.headers.enabled = true - Keep
security.headers.x_content_type_options = true - Keep
security.headers.referrer_policy = strict-origin-when-cross-origin - Keep
security.headers.x_frame_options = ''(empty; do not setSAMEORIGIN/DENYfor cross-origin embeds) - If CSP is enabled, ensure
frame-ancestorsincludes your Roundcube origin (for examplehttps://mail.example.com)
Use Verify Current Headers in the Security diagnostics panel to confirm live responses from Admin/Files endpoints match expected values.
Files UI
Accessible under:
https://your.server/rounddav/public/files/
This is the generic Files interface that rounddav_files embeds in an iframe inside Roundcube. It:
- Shows per-user directories under your configured
files.root - Allows uploads, downloads, deletions (depending on your implementation)
- Is styled to roughly match Roundcube skins when embedded
Philosophy
RoundDAV is meant to be:
- Small enough to understand
- Strong enough to be useful
- Quiet enough to disappear behind Roundcube
If you know Roundcube and a bit of PHP, you should be able to debug or extend this without fighting it.
Enjoy!
💰 Donations 💰
If you use this plugin and would like to show your appreciation by buying me a cup of coffee, I surely would appreciate it. A regular cup of Joe is sufficient, but a Starbucks Coffee would be better ...
Zelle (Zelle is integrated within many major banks Mobile Apps by default) - Just send to texxasrulez at yahoo dot com
No Zelle in your banks mobile app, no problem, just click Paypal and I can make a Starbucks run ...
I appreciate the interest in this plugin and hope all the best ...