Multi-tenant session scheduling platform with a React/Vite frontend, Express API, PostgreSQL, and Microsoft Calendar integration.
- Tenant-aware setup flow for first admin bootstrap
- Public booking with idempotency protection and email-domain allowlists
- Recurring time blocks for project scheduling
- Background queue for retryable booking email and calendar sync jobs
- Microsoft Calendar OAuth integration for engineer scheduling
- Data retention sweeps for redaction and cleanup policies
- Built-in load testing scripts for capacity planning
| Login | Dashboard | Project detail |
|---|---|---|
![]() |
![]() |
![]() |
| Add time blocks | Find a time for everyone |
|---|---|
![]() |
![]() |
| Select a time | Booking confirmed | Reschedule |
|---|---|---|
![]() |
![]() |
![]() |
| Admin overview | Users |
|---|---|
![]() |
![]() |
| Dashboard | Booking |
|---|---|
![]() |
![]() |
- Frontend: React 18 + Vite + TypeScript
- Backend: Express + TypeScript
- Database: PostgreSQL 16
- Shared contracts:
@opencalendar/shared - Infra: Docker Compose (postgres + optional full stack)
packages/sharedshared types and validation schemaspackages/serverAPI server, DB migration/seed scriptspackages/clientReact SPAdocker/init.sqldatabase schema and indexes
- Node.js 20+
- npm 10+
- Docker Desktop (or compatible Docker Engine)
Create .env from the template:
cp .env.example .envImportant defaults:
DATABASE_URLis for local Node scripts (localhost)DOCKER_DATABASE_URLis for the server container (postgresservice hostname)
Full variable reference (defaults, required/conditional usage, behavior notes):
docs/ENVIRONMENT.md- Change log and release notes:
CHANGELOG.md
npm install
npm run dev:setup
npm run devWhat this does:
dev:setupstarts Postgres in Docker and runs migration + seeddevstarts shared watcher, API server, and Vite client
Default local URLs:
- Client:
http://localhost:5173 - API:
http://localhost:4000 - API health:
http://localhost:4000/api/health
- Open
http://localhost:5173/setupon a fresh database to create the first tenant admin account. - The setup flow calls:
GET /api/setup/statusPOST /api/setup/initialize
- After setup completes, normal login/SSO uses
http://localhost:5173/login.
docker compose up --build -dDefault URLs:
- App (nginx + client):
http://localhost:3000 - API (direct):
http://localhost:4000
Stop services:
npm run docker:downnpm run lintlint all workspacesnpm run buildbuild all workspacesnpm run releasepreview the release plan (no changes written)npm run release:applycut a release: bump, roll the CHANGELOG, commit, tag, and pushnpm run release:client/npm run release:serverforce-release a single componentnpm run release -- --bump patch|minor|majoroverride the inferred bump levelnpm run db:migrateapply database schemanpm run db:seedinsert demo seed recordsnpm run db:backupcreate a compressed PostgreSQL backup from Docker Composenpm run db:restore -- <file.sql.gz>restore a PostgreSQL backup into Docker Compose
Auto bump rules:
patch: Client-only changes (packages/client/**)minor: Server-only changes (packages/server/**)major: Both Client and Server changed
- The server now runs an in-memory background job queue for retryable tasks.
- Booking lifecycle events enqueue
booking-emailjobs (booked, rescheduled, cancelled). - Email delivery supports:
EMAIL_PROVIDER=console(default, logs structured payloads)EMAIL_PROVIDER=resend(sends emails through Resend API)
- Retry behavior is configurable with:
JOB_QUEUE_POLL_INTERVAL_MSJOB_QUEUE_MAX_ATTEMPTSJOB_QUEUE_BACKOFF_BASE_MS
- Resend configuration:
RESEND_API_KEYEMAIL_FROMBOOKING_PORTAL_BASE_URL(used for absolute reschedule links)
- Backups can be created and restored with:
npm run db:backupnpm run db:restore -- backups/<file>.sql.gz
POST /api/schedule/book/:shareTokenaccepts an optionalIdempotency-Keyrequest header.- Repeating the same request with the same key returns the original
201booking payload instead of creating duplicates. - Reusing a key with a different payload returns
409.
- Projects can now define an optional
booking_email_domain_allowlist(for exampleclient.com). - Public booking rejects emails outside that domain (subdomains are allowed).
- Calendar sync is available for authenticated
engineerusers only. - Engineers can connect/disconnect Microsoft Calendar from the dashboard.
- Booking lifecycle events now enqueue background sync jobs:
bookedcreates/updates engineer calendar eventscancelledremoves engineer calendar eventsrescheduledis handled as cancel old booking + create new booking events
- Required configuration:
MICROSOFT_CLIENT_IDMICROSOFT_CLIENT_SECRETMICROSOFT_REDIRECT_URI
- Optional configuration:
MICROSOFT_TENANT_ID(defaultcommon)MICROSOFT_OAUTH_SCOPESMICROSOFT_OAUTH_STATE_TTL_SECONDSMICROSOFT_OAUTH_STATE_SECRET(falls back toJWT_SECRET)MICROSOFT_OAUTH_SUCCESS_REDIRECT_URLMICROSOFT_OAUTH_ERROR_REDIRECT_URL
- PMs can now create recurring weekly schedules from the "Add Time Blocks" modal.
- New API endpoint:
POST /api/time-blocks/recurring(also available under/api/v1). - Recurring payload supports:
slots_per_occurrencefor consecutive slots in each recurrencerecurrence.interval_weeksrecurrence.occurrences
- API routes are now exposed under both:
- legacy:
/api/* - versioned:
/api/v1/*
- legacy:
- New clients should target
/api/v1.
- The server now runs a periodic retention sweep that:
- deletes expired booking idempotency records
- redacts booking PII after a configurable retention window
- hard-deletes cancelled bookings after a longer retention window
- New environment variables:
DATA_RETENTION_ENABLEDDATA_RETENTION_SWEEP_INTERVAL_MSDATA_RETENTION_PII_DAYSDATA_RETENTION_CANCELLED_BOOKING_DELETE_DAYSDATA_RETENTION_IDEMPOTENCY_KEY_DELETE_DAYS
- Redacted booking fields are replaced with
[deleted]markers and a synthetic email (deleted+<id>@redacted.local).
- A built-in load test runner is available at
scripts/load-test.mjs. - Supported scenarios:
read-project(public availability read path)book-contention(booking transaction contention path)
- Run:
LOADTEST_SHARE_TOKEN=<share-token> npm run loadtest:readLOADTEST_SHARE_TOKEN=<share-token> LOADTEST_PASSWORD=<project-password> npm run loadtest:book
- Full guide and planning targets:
docs/LOAD_TESTING.md
- PM:
pm@example.com/password123 - Engineer:
engineer@example.com/password123
OpenCalendar is under active development. The repository currently includes a full local development workflow, Docker-backed infrastructure, and production-oriented scheduling capabilities listed above.











