Hermes Research · executes Themis-PM #8 · Track C / C7
A 5-lane study of how to power a Supabase-backed canonical calendar and a state-of-the-art planning app — tying together the live cal.boas.dev booking pipeline and meet.boas.dev.
C7 resolves to Option C — by the operator's own constraint
“A Supabase calendar DB where we log all events” means Supabase is the SSOT. That disqualifies Option A (Cal.com-centric) and Option B (CalDAV-server-centric) as canonical — each relocates the SSOT into an opaque store. They aren't beaten on merit; they're ruled out by the requirement, and their tooling is re-used as edges around the Supabase core.
The decision was effectively pre-made. The research confirms it's also the correct call: only a Supabase-canonical model gives an agent fleet one SQL-queryable calendar (joins to crm_deals, meetings, meeting_tasks, agent identity) with RLS-scoped visibility and Realtime.
Booking), no arbitrary events, recurrence as a JSON blob (not RFC-5545 RRULE), cannot serve CalDAV. It's a booking front-end — exactly as wired today.tstzrange, EXCLUDE USING gist double-booking, store-the-rule recurrence.tsdav + ical.js + googleapis + graph-client), hybrid push+poll. iCloud is poll-only. Nylas/Cronofy are SaaS-lock fallback only.| Option | What it means | Verdict |
|---|---|---|
| A — Cal.com-centric | Cal.com's Postgres is the calendar | ✗ Disqualified. Booking-only schema; no arbitrary events; not a CalDAV server; commercial/closed drift (v6.4 split, Apr 2026). |
| B — CalDAV-server-centric | Nextcloud / Baïkal / Radicale / DAViCal is canonical | ✗ Disqualified as SSOT. Server-owned, opaque storage; external writes corrupt it. ✓ Re-used as a native-client read-edge. |
| C — Supabase-canonical + edges | Supabase events table is the SSOT; all else syncs to it |
✓ Selected — the only model consistent with “Supabase DB where we log all events.” |
Booking edge · LOCKED
Cal.com → n8n
cal.boas.dev webhook (HMAC) → n8n upsert. Track A, green. Booking front-end only.
Meet edge · Track E
meet.boas.dev/<slug>
Per-booking link written to events.meeting_url.
Native-client edge · C4/C5
Radicale / Baïkal on VPS
One-way from Supabase + ICS/webcal feed. Phones/desktop subscribe. NOT canonical.
Single source of truth
SUPABASE
events · event_attendees · sync_state
RFC-5545 model · RLS · Realtimetstzrange EXCLUDE
External sync edge · C8
TS sync worker
Google / MS Graph / iCloud. Push+poll, echo-loop guard, UID mapping. iCloud poll-only.
Interaction layer
Planning app
Next.js/React · react-big-calendar · ritual UX. AI auto-scheduling deferred.
Fleet joins
crm_deals · meetings · tasks
One SQL-queryable calendar for the agent fleet — the reason C wins.
Everything outside the green box is adopted. The box — schema + planning UX — is the build.
Build · canonical store
events tableGeneralize the live meetings table. Wall-clock + IANA tzid + generated dtstart_utc; EXCLUDE USING gist double-booking; store-the-rule recurrence via pg_rrule/rrule.js; event_kind→TRANSP taxonomy; RLS by agent. Full DDL in lane T2 §7.
Adopt · booking
Booking-only — confirmed it cannot be canonical. Keep the live webhook→n8n→Supabase spine; write the canonical events row from it.
Adopt + thin glue · C4/C5
Lightweight CalDAV on the Hostinger VPS, fed one-way from Supabase + ICS feed. Baïkal for web admin/sharing; Radicale for minimum ops. Avoid SOGo/Nextcloud (6–8 / 2–4 GB).
Adopt libs + build worker · C8
tsdav + ical.js + googleapis + graph-client on the VPS. Push+poll, last-write-wins, sync-id + iCal-UID dedup. Nango optional for OAuth.
Build · interaction layer
Next.js/React on Supabase. react-big-calendar (MIT) → FullCalendar v7 when resource lanes needed. MVP = ritual UX + drag time-blocking + scheduling links. Morgen = reference UX.
Adopt · recurrence
Expand on read; never materialize (520+ rows/decade per weekly event). Note: pg_rrule needs COUNT/UNTIL — set a far-future UNTIL for open-ended series.
| # | Work | Track C | Adopt vs Build |
|---|---|---|---|
| 1 | events + event_attendees + sync_state schema (generalize meetings) | C3 | Build (schema), patterns adopted |
| 2 | Event-kind taxonomy + TRANSP mapping | C6 | Build (CHECK constraints) |
| 3 | Cal.com webhook → write canonical events row | A↔C1 | Adopt (live pipeline) |
| 4 | Track E meet-link bridge → events.meeting_url | E1 | Build (thin glue) |
| 5 | Supabase→CalDAV one-way adapter + run Radicale/Baïkal | C4 | Adopt server + build adapter |
| 6 | ICS/webcal read-only feed publisher | C5 | Build (thin) |
| 7 | Planning app MVP (react-big-calendar + ritual UX) | app layer | Adopt lib + build UX |
| 8 | External sync worker (Google/MS first, iCloud poll) | C8 | Adopt libs + build worker |
Steps 1–4 are the spine and unblock the planning app; 5–6 give native-client reach; 8 (two-way external sync) is heaviest, last.
| Server | Postgres-backable | Ops burden | Last release | Edge role |
|---|---|---|---|---|
| Radicale | No (filesystem) | Very low | v3.7.4 · 2026-05 | Minimum-ops read-edge |
| Baïkal | Uncertain (PDO) | Low | v0.11.1 · 2025-11 | Read-edge + web admin |
| DAViCal | Yes (PG-only, owned) | Moderate | v1.1.9.3 · 2026 | Closest, but slowing |
| Nextcloud | Yes, opaque | Mod–High | Hub 9 · 2026 | Only if already running |
| SOGo | Yes, opaque | Very high (6–8 GB) | v5.12.9 · 2026-05 | Overkill |
| Xandikos | No (Git) | Low | v0.4.2 · 2026-05 | RFC-6578 complete, niche |
| EteSync | No (E2EE) | Mod–High | v0.35.1 · 2025-08 | Incompatible w/ C |
| App | Core bet | AI auto-schedule | Ritual | CalDAV-friendly |
|---|---|---|---|---|
| Motion | Full AI auto-scheduling + reflow | ✓ | partial | ✗ |
| Reclaim | Schedule defense / habits | ✓ lighter | ✗ | ✗ |
| Akiflow | Command-bar speed + daily ritual | ✗ | ✓ best | ✗ |
| Sunsama | Mindful guided planning | 2026 partial | ✓ guided | ✗ |
| Morgen | Unified + CalDAV-native + Linux | AI assist | ✗ | ✓ reference |
Key insight: the most-loved apps win on ritual design + UX speed, not AI. A fast command-bar + daily ritual + drag time-blocking delivers ~80% of value at ~20% of the AI-tier cost.
| Library | Frameworks | Recurrence | License | Pick |
|---|---|---|---|---|
| react-big-calendar | React | manual | MIT | Start here (MVP) |
| FullCalendar v7 | React/Vue/Angular | plugin | MIT core / $480/dev premium | Upgrade for Timeline/Resource |
| Schedule-X | all major | plugin | free core / paid premium | If framework-agnostic matters |
| Toast UI | React/Vue (stale) | unknown | MIT | Avoid (stale) |
| Mobiscroll | all major | yes | $995+/yr commercial | Avoid (no OSS leverage) |
High confidence (multi-lane agreement): Cal.com = booking-edge not store (T2+T3+T5); no CalDAV server as shareable SSOT (T1); store-don't-materialize recurrence (T2+T5); iCloud poll-only (T5); ritual > AI for planning value (T4).
Surfaced fork (resolved, not silently picked): Lane T3 labels Nextcloud/Baïkal “viable canonical store”; lane T1 shows no CalDAV server is Postgres-transparent. Reconciled: those verdicts mean canonical if you let the CalDAV server own the SSOT — which violates the Supabase-SSOT requirement. Hence CalDAV servers are edges, not the store. The lanes don't actually conflict once the constraint is applied.
Watch items: a possible 2026-06-09 Cal.com license change (verify before deeper coupling); tsdav maintenance status; pg_rrule cannot expand infinite RRULEs (needs UNTIL).