# Baikal — Client Setup Companion to [`baikal.md`](baikal.md) (auto-generated from compose). This file is hand-maintained and covers how to connect CalDAV/CardDAV clients to the Baikal server on Atlantis. ## Server Reference | Field | Value | |-------|-------| | Host | Atlantis (Synology NAS) | | Container | `baikal` (image `ckulka/baikal`) | | Port | `12852` → container `80` | | Auth | HTTP **Digest** (not Basic) | | DAV root | `/dav.php/` | | Admin UI | `http://:12852/admin/` | ### Endpoints (all equivalent for authed CalDAV) | URL | When to use | |-----|-------------| | `http://192.168.0.200:12852/` | LAN-only clients (dashboard, LAN appliances) | | `http://atlantis:12852/` | Nodes with Tailscale MagicDNS | | `http://atlantis.tail.vish.gg:12852/` | **Preferred for portable clients** (laptops that move on/off LAN) — stable DNS, works everywhere on the tailnet | | `http://100.83.230.112:12852/` | Raw Tailscale IP — fallback if DNS misbehaves | ### URL patterns - Principal: `/dav.php/principals//` — point auto-discovering clients (iOS, macOS, DAVx⁵) here. - Calendar: `/dav.php/calendars///` — explicit calendar URL for clients that need it (Thunderbird with "Locate calendar" off). - Address book: `/dav.php/addressbooks///`. ## Credentials - Baikal users live in the `users` table of `/volume1/docker/baikal/Specific/db/db.sqlite`. Passwords are stored as MD5 digesta1 (`MD5(user:BaikalDAV:password)`) — not reversible. - The `vish` user password is referenced by the dashboard's upcoming-events widget (see `dashboard/api/routers/overview.py`, `BAIKAL_PASS` constant). Pull it from there — do **not** paste it into docs or client configs that get committed. - Reset or create a user from the admin UI at `/admin/` (login with the admin password from compose env). ## Inspecting the database No `sqlite3` binary in the container — use PHP PDO: ```bash ssh atlantis "sudo /usr/local/bin/docker exec baikal php -r ' \$db=new PDO(\"sqlite:/var/www/baikal/Specific/db/db.sqlite\"); foreach(\$db->query(\"SELECT id,username FROM users\") as \$r){ echo \$r[\"id\"].\" \".\$r[\"username\"].PHP_EOL; }'" ``` Useful tables: `users`, `principals`, `calendars`, `calendarinstances` (displayname, ACL, share state), `calendarobjects`, `addressbooks`, `cards`. ## Thunderbird (GUI path) 1. **File → New → Calendar → On the Network → Next**. 2. **Username**: Baikal user (e.g. `vish`). 3. **Location**: either - `http://atlantis.tail.vish.gg:12852/dav.php/principals/vish/` — lets TB auto-discover all calendars for that user, or - a specific calendar URL, e.g. `http://atlantis.tail.vish.gg:12852/dav.php/calendars/vish/default/`. 4. **Find Calendars** → select the one(s) to add → **Subscribe**. 5. Password prompt appears on first sync; tick **Use Password Manager** to save. ## Thunderbird (headless / prefs.js path) Use when the client machine is remote and you want to drop a calendar in without touching the GUI. Procedure: 1. **Close Thunderbird.** `prefs.js` is rewritten on exit — any edits made while it is running get clobbered. 2. Clear stale lock files if present: `rm -f ~/.thunderbird//lock ~/.thunderbird//.parentlock`. 3. Back up: `cp prefs.js prefs.js.bak-$(date +%Y%m%d-%H%M%S)`. 4. Generate a UUID: `cat /proc/sys/kernel/random/uuid`. 5. Append the registry block below (replace `` and tweak `name`, `color`, `uri`, `username`). 6. Update the `calendar.list.sortOrder` line — append the new UUID (space-separated) so the calendar actually shows up. ```ini user_pref("calendar.registry..cache.enabled", true); user_pref("calendar.registry..calendar-main-in-composite", true); user_pref("calendar.registry..color", "#3aa57c"); user_pref("calendar.registry..disabled", false); user_pref("calendar.registry..forceEmailScheduling", false); user_pref("calendar.registry..imip.identity.key", "id1"); user_pref("calendar.registry..name", "vish (Baikal)"); user_pref("calendar.registry..notifications.times", ""); user_pref("calendar.registry..readOnly", false); user_pref("calendar.registry..refreshInterval", "30"); user_pref("calendar.registry..suppressAlarms", false); user_pref("calendar.registry..type", "caldav"); user_pref("calendar.registry..uri", "http://atlantis.tail.vish.gg:12852/dav.php/calendars/vish/default/"); user_pref("calendar.registry..username", "vish"); ``` **Password handling**: do *not* attempt to pre-populate the login store — it is NSS-encrypted and requires live TB APIs to write correctly. Leave the password out of `prefs.js` entirely; on first sync Thunderbird will prompt, and the user ticks **Use Password Manager** once. **Verification (from client machine, before TB relaunch)**: confirm the client can reach the URL with the credentials: ```bash curl -s -o /dev/null -w 'HTTP %{http_code}\n' \ --digest -u ':' \ http://atlantis.tail.vish.gg:12852/dav.php/calendars//default/ ``` Expect `HTTP 200`. If `401`, the password is wrong or the realm name changed. If no response, check Tailscale reachability (`tailscale status | grep atlantis`) before blaming Baikal. ## Deployed clients (as of 2026-04-19) | Client | Calendar | URL | Auth flavor | |--------|----------|-----|-------------| | Dashboard widget (homelab-vm) | `vish/default` | `http://192.168.0.200:12852/dav.php/calendars/vish/default/` | Digest via `httpx.DigestAuth`, creds in `dashboard/api/routers/overview.py` | | Thunderbird on moon laptop | `vish/default` | `http://atlantis.tail.vish.gg:12852/dav.php/calendars/vish/default/` | Digest, password in TB password manager (user-entered on first sync) | ## Troubleshooting - **`401 Unauthorized`**: Baikal speaks Digest, not Basic. Most `curl` tests need `--digest`. Thunderbird handles this automatically. - **Calendar appears but never refreshes**: check `calendar.registry..cache.enabled` is `true`; the local-cache is what TB displays between network syncs. - **Events not showing on the dashboard**: the widget only queries `vish/default`. Other calendars need a separate `calendar-query` REPORT against their URL. - **Admin UI locked out**: `INSTALL_DISABLED` is touched in `/var/www/baikal/Specific/` after first setup. Delete that file in the container to re-enable the install wizard if you need to reset the admin password. ## Related - [`baikal.md`](baikal.md) — auto-generated compose reference. - `hosts/synology/atlantis/baikal/baikal.yaml` — stack definition. - `dashboard/api/routers/overview.py` — dashboard CalDAV consumer (reference implementation for Digest queries).