← Back to projects
Not public

Cosmetra

SaaS platform for beauty and service studios, with a self-built CalDAV server for real calendar synchronization.

Cosmetra

Cosmetra bundles appointment management, customers, staff, booking, and finances for beauty and service studios in a single platform. The core promise: studios see their appointments right where they already work — in Apple Calendar, in Thunderbird, in DAVx⁵ — without a separate sync tool.

To make that possible, Cosmetra runs its own CalDAV server instead of a ready-made library like sabre/dav: lean, scoped to the feature set it actually needs, and integrated directly into its own workspace/token model.

Gallery

Case Study

Situation

Beauty and service studios often manage appointments across several disconnected systems. Cosmetra set out as a two-person venture (Felix Schattenberg as technical lead, a co-founder for product and go-to-market) to solve that with real calendar synchronization, instead of pushing studios onto yet another island tool.

Challenge

The CalDAV server is no trivial appendage at roughly 450 lines of custom controller code: Basic Auth over custom tokens, PROPFIND/REPORT/GET/PUT/DELETE handling, and manual iCal parsing and serialization (line unfolding, property/parameter parsing, TZID handling, escaping). The PUT method was originally a stub that ignored every incoming iCal payload — external calendar clients couldn’t create or change appointments through it. The fix needed a real VEVENT parser that extracts DTSTART/DTEND/SUMMARY/DESCRIPTION/STATUS and maps them onto appointments via updateOrCreate — and made customer_id nullable on the appointments table, because CalDAV clients can create appointments without a known customer.

Approach

Laravel 13 with PHP 8.5, Vue 3.5 and TypeScript via Inertia.js, shadcn/ui (reka-ui) on Tailwind CSS 4, Pest for tests. Multi-tenancy as shared-schema: every core entity carries a workspace_id instead of running a separate database per customer. Shipped via GitHub Actions and Deployer with an atomic symlink swap, queue workers under systemd.

Decisions

Custom CalDAV server instead of sabre/dav

A lean feature set scoped to workspaces and staff, rather than a full DAV/ACL library, integrated directly into the existing token model.

Custom token model instead of session/OAuth for CalDAV

A CaldavToken model with workspace or staff scope, individually restrictable per staff member.

Inertia instead of a separate SPA

Deliberately kept auth and deployment complexity low for a two-founder MVP, rather than running backend and frontend as separate deployments.

Shared-schema multi-tenancy from day one

Avoids later table rewrites; every core entity carries a workspace_id instead of schema-per-tenant.

Monetary amounts as integers in minor units

Rules out rounding errors on amounts; time is kept consistently in UTC internally and only converted to the workspace time zone for display.

My contribution

Full technical implementation: data model, CalDAV server, Vue/TypeScript frontend, booking and finance logic, deployment. A second co-founder is registered as super admin and responsible for the product side — this was primarily, though not exclusively, solo work.

Results

Core modules
9 complete per current project status: Dashboard, Customers, Services, Appointments, Calendar, Staff, Finances, Booking, CalDAV
Tests
220 Pest tests, including dedicated auth/isolation tests for the CalDAV server
Status
Live in production use, public domain still pending

Search & navigation

Navigate Open Esc Close